Skip to content

Commit 72d9aa9

Browse files
committed
[Refactor] Get default include paths working
1 parent 5541456 commit 72d9aa9

File tree

10 files changed

+484
-54
lines changed

10 files changed

+484
-54
lines changed

src/Api/Api.php

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
use CloudCreativity\LaravelJsonApi\Resolver\NamespaceResolver;
3535
use GuzzleHttp\Client;
3636
use Neomerx\JsonApi\Contracts\Http\Headers\MediaTypeInterface;
37-
use Neomerx\JsonApi\Contracts\Schema\SchemaContainerInterface;
3837

3938
/**
4039
* Class Api
@@ -322,7 +321,7 @@ public function encoder($options = 0, $depth = 512)
322321
}
323322

324323
return $this->factory
325-
->createExtendedEncoder($this->getSchemaContainer())
324+
->createLaravelEncoder($this->getContainer())
326325
->withEncoderOptions($options);
327326
}
328327

@@ -359,7 +358,7 @@ public function client($clientHostOrOptions = [], array $options = [])
359358

360359
$client = ($clientHostOrOptions instanceof Client) ? $clientHostOrOptions : new Client($options);
361360

362-
return $this->factory->createClient($client, $this->getSchemaContainer(), $this->encoder());
361+
return $this->factory->createClient($client, $this->getContainer(), $this->encoder());
363362
}
364363

365364
/**
@@ -399,14 +398,4 @@ public function register(AbstractProvider $provider)
399398
{
400399
$this->resolver->attach($provider->getResolver());
401400
}
402-
403-
/**
404-
* @return SchemaContainerInterface
405-
*/
406-
private function getSchemaContainer(): SchemaContainerInterface
407-
{
408-
return $this->factory->createLaravelSchemaContainer(
409-
$this->getContainer()
410-
);
411-
}
412401
}

src/Codec/Codec.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ public function getEncoder(): Encoder
110110
throw new \RuntimeException('Codec does not support encoding JSON API content.');
111111
}
112112

113-
$encoder = $this->factory->createExtendedEncoder(
114-
$this->factory->createLaravelSchemaContainer($this->container)
113+
$encoder = $this->factory->createLaravelEncoder(
114+
$this->container,
115115
);
116116

117117
$options = $this->encoding->getOptions();

src/Container.php

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919

2020
use CloudCreativity\LaravelJsonApi\Contracts\Adapter\ResourceAdapterInterface;
2121
use CloudCreativity\LaravelJsonApi\Contracts\Auth\AuthorizerInterface;
22+
use CloudCreativity\LaravelJsonApi\Contracts\ContainerAwareInterface;
2223
use CloudCreativity\LaravelJsonApi\Contracts\ContainerInterface;
2324
use CloudCreativity\LaravelJsonApi\Contracts\Http\ContentNegotiatorInterface;
2425
use CloudCreativity\LaravelJsonApi\Contracts\Resolver\ResolverInterface;
2526
use CloudCreativity\LaravelJsonApi\Contracts\Schema\SchemaProviderInterface;
2627
use CloudCreativity\LaravelJsonApi\Contracts\Validation\ValidatorFactoryInterface;
2728
use CloudCreativity\LaravelJsonApi\Exceptions\RuntimeException;
29+
use Illuminate\Contracts\Container\BindingResolutionException;
2830
use Illuminate\Contracts\Container\Container as IlluminateContainer;
2931

3032
/**
@@ -501,18 +503,37 @@ protected function createContentNegotiatorFromClassName($className)
501503
}
502504

503505
/**
504-
* @inheritDoc
506+
* @param string|null $className
507+
* @return mixed|nulL
505508
*/
506-
protected function create($className)
509+
protected function create(?string $className)
507510
{
508-
return $this->exists($className) ? $this->container->make($className) : null;
511+
if (false === $this->exists($className)) {
512+
return null;
513+
}
514+
515+
try {
516+
$value = $this->container->make($className);
517+
} catch (BindingResolutionException $ex) {
518+
throw new RuntimeException(
519+
sprintf('JSON:API container was unable to build %s via the service container.', $className),
520+
0,
521+
$ex,
522+
);
523+
}
524+
525+
if ($value instanceof ContainerAwareInterface) {
526+
$value->withContainer($this);
527+
}
528+
529+
return $value;
509530
}
510531

511532
/**
512-
* @param $className
533+
* @param string|null $className
513534
* @return bool
514535
*/
515-
protected function exists($className): bool
536+
protected function exists(?string $className): bool
516537
{
517538
if (null === $className) {
518539
return false;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
/*
3+
* Copyright 2022 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace CloudCreativity\LaravelJsonApi\Contracts;
21+
22+
interface ContainerAwareInterface
23+
{
24+
/**
25+
* Inject the container instance.
26+
*
27+
* @param ContainerInterface $container
28+
* @return void
29+
*/
30+
public function withContainer(ContainerInterface $container): void;
31+
}

src/Eloquent/AbstractAdapter.php

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@
2020
use CloudCreativity\LaravelJsonApi\Adapter\AbstractResourceAdapter;
2121
use CloudCreativity\LaravelJsonApi\Contracts\Adapter\HasManyAdapterInterface;
2222
use CloudCreativity\LaravelJsonApi\Contracts\Adapter\RelationshipAdapterInterface;
23+
use CloudCreativity\LaravelJsonApi\Contracts\ContainerAwareInterface;
24+
use CloudCreativity\LaravelJsonApi\Contracts\ContainerInterface;
2325
use CloudCreativity\LaravelJsonApi\Contracts\Http\Query\QueryParametersInterface;
2426
use CloudCreativity\LaravelJsonApi\Contracts\Pagination\PageInterface;
2527
use CloudCreativity\LaravelJsonApi\Contracts\Pagination\PagingStrategyInterface;
28+
use CloudCreativity\LaravelJsonApi\Contracts\Schema\SchemaProviderInterface;
2629
use CloudCreativity\LaravelJsonApi\Document\ResourceObject;
2730
use CloudCreativity\LaravelJsonApi\Http\Query\QueryParameters;
2831
use CloudCreativity\LaravelJsonApi\Exceptions\RuntimeException;
@@ -37,7 +40,7 @@
3740
*
3841
* @package CloudCreativity\LaravelJsonApi
3942
*/
40-
abstract class AbstractAdapter extends AbstractResourceAdapter
43+
abstract class AbstractAdapter extends AbstractResourceAdapter implements ContainerAwareInterface
4144
{
4245

4346
use Concerns\DeserializesAttributes,
@@ -92,6 +95,11 @@ abstract class AbstractAdapter extends AbstractResourceAdapter
9295
*/
9396
private $scopes;
9497

98+
/**
99+
* @var SchemaProviderInterface|null
100+
*/
101+
private ?SchemaProviderInterface $schema = null;
102+
95103
/**
96104
* Apply the supplied filters to the builder instance.
97105
*
@@ -114,6 +122,14 @@ public function __construct(Model $model, PagingStrategyInterface $paging = null
114122
$this->scopes = [];
115123
}
116124

125+
/**
126+
* @inheritDoc
127+
*/
128+
public function withContainer(ContainerInterface $container): void
129+
{
130+
$this->schema = $container->getSchema($this->model);
131+
}
132+
117133
/**
118134
* @inheritDoc
119135
*/
@@ -682,7 +698,7 @@ protected function queryOne($query, QueryParametersInterface $parameters)
682698
}
683699

684700
/**
685-
* Get JSON API parameters to use when constructing an Eloquent query.
701+
* Get JSON:API parameters to use when constructing an Eloquent query.
686702
*
687703
* This method is used to push in any default parameter values that should
688704
* be used if the client has not provided any.
@@ -693,7 +709,7 @@ protected function queryOne($query, QueryParametersInterface $parameters)
693709
protected function getQueryParameters(QueryParametersInterface $parameters)
694710
{
695711
return new QueryParameters(
696-
$parameters->getIncludePaths(),
712+
$parameters->getIncludePaths() ?? $this->getSchema()->getIncludePaths(),
697713
$parameters->getFieldSets(),
698714
$parameters->getSortParameters() ?: $this->defaultSort(),
699715
$parameters->getPaginationParameters() ?: $this->defaultPagination(),
@@ -702,6 +718,21 @@ protected function getQueryParameters(QueryParametersInterface $parameters)
702718
);
703719
}
704720

721+
/**
722+
* @return SchemaProviderInterface
723+
*/
724+
protected function getSchema(): SchemaProviderInterface
725+
{
726+
if ($this->schema) {
727+
return $this->schema;
728+
}
729+
730+
throw new RuntimeException(sprintf(
731+
'Expecting schema to be set in adapters %s.',
732+
get_class($this),
733+
));
734+
}
735+
705736
/**
706737
* @return string
707738
*/

src/Encoder/DataAnalyser.php

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
/*
3+
* Copyright 2022 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace CloudCreativity\LaravelJsonApi\Encoder;
21+
22+
use ArrayAccess;
23+
use CloudCreativity\LaravelJsonApi\Contracts\ContainerInterface;
24+
use Generator;
25+
use Illuminate\Support\Enumerable;
26+
use Iterator;
27+
use RuntimeException;
28+
29+
class DataAnalyser
30+
{
31+
/**
32+
* @var ContainerInterface
33+
*/
34+
private ContainerInterface $container;
35+
36+
/**
37+
* DataAnalyser constructor.
38+
*
39+
* @param ContainerInterface $container
40+
*/
41+
public function __construct(ContainerInterface $container)
42+
{
43+
$this->container = $container;
44+
}
45+
46+
/**
47+
* @param object|iterable|null $data
48+
* @return object|null
49+
*/
50+
public function getRootObject($data): ?object
51+
{
52+
if ($data instanceof Generator) {
53+
throw new RuntimeException('Generators are not supported as resource collections.');
54+
}
55+
56+
if (null === $data || $this->isResource($data)) {
57+
return $data;
58+
}
59+
60+
$value = $this->getRootObjectFromIterable($data);
61+
62+
if (null === $value || $this->isResource($value)) {
63+
return $value;
64+
}
65+
66+
throw new RuntimeException(
67+
sprintf('Unexpected data type: %s.', get_debug_type($value)),
68+
);
69+
}
70+
71+
/**
72+
* @param object|iterable|null $data
73+
* @return array
74+
*/
75+
public function getIncludePaths($data): array
76+
{
77+
$includePaths = [];
78+
$root = $this->getRootObject($data);
79+
80+
if (null !== $root) {
81+
$includePaths = $this->container->getSchema($root)->getIncludePaths();
82+
}
83+
84+
return $includePaths;
85+
}
86+
87+
/**
88+
* @param mixed $value
89+
* @return bool
90+
*/
91+
private function isResource($value): bool
92+
{
93+
return is_object($value) && $this->container->hasSchema($value);
94+
}
95+
96+
/**
97+
* @param iterable $data
98+
* @return object|null
99+
*/
100+
private function getRootObjectFromIterable(iterable $data): ?object
101+
{
102+
if (is_array($data)) {
103+
return $data[0] ?? null;
104+
}
105+
106+
if ($data instanceof Enumerable) {
107+
return $data->first();
108+
}
109+
110+
if ($data instanceof Iterator) {
111+
$data->rewind();
112+
return $data->valid() ? $data->current() : null;
113+
}
114+
115+
foreach ($data as $value) {
116+
return $value;
117+
}
118+
119+
return null;
120+
}
121+
}

0 commit comments

Comments
 (0)