Skip to content

Commit 4441501

Browse files
authored
Add resource fallback for ApiProperty (#1963)
* Add resource fallback for ApiProperty * Fix typo * Use trait aliases * Deprecate the fetchEager attribute * Add real constructors
1 parent 14432bd commit 4441501

File tree

7 files changed

+177
-32
lines changed

7 files changed

+177
-32
lines changed

src/Annotation/ApiProperty.php

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,17 @@
2020
*
2121
* @Annotation
2222
* @Target({"METHOD", "PROPERTY"})
23+
* @Attributes(
24+
* @Attribute("fetchable", type="bool"),
25+
* @Attribute("fetchEager", type="bool"),
26+
* @Attribute("jsonldContext", type="array"),
27+
* @Attribute("swaggerContext", type="array")
28+
* )
2329
*/
2430
final class ApiProperty
2531
{
32+
use AttributesHydratorTrait;
33+
2634
/**
2735
* @var string
2836
*/
@@ -64,7 +72,38 @@ final class ApiProperty
6472
public $identifier;
6573

6674
/**
75+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
76+
*
77+
* @var bool
78+
*/
79+
private $fetchable;
80+
81+
/**
82+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
83+
*
84+
* @var bool
85+
*/
86+
private $fetchEager;
87+
88+
/**
89+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
90+
*
91+
* @var array
92+
*/
93+
private $jsonldContext;
94+
95+
/**
96+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
97+
*
6798
* @var array
6899
*/
69-
public $attributes = [];
100+
private $swaggerContext;
101+
102+
/**
103+
* @throws InvalidArgumentException
104+
*/
105+
public function __construct(array $values = [])
106+
{
107+
$this->hydrateAttributes($values);
108+
}
70109
}

src/Annotation/ApiResource.php

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313

1414
namespace ApiPlatform\Core\Annotation;
1515

16-
use ApiPlatform\Core\Exception\InvalidArgumentException;
17-
use Doctrine\Common\Util\Inflector;
16+
use Doctrine\Common\Annotations\Annotation\Attribute;
1817

1918
/**
2019
* ApiResource annotation.
@@ -34,6 +33,7 @@
3433
* @Attribute("forceEager", type="bool"),
3534
* @Attribute("filters", type="string[]"),
3635
* @Attribute("graphql", type="array"),
36+
* @Attribute("hydraContext", type="array"),
3737
* @Attribute("iri", type="string"),
3838
* @Attribute("itemOperations", type="array"),
3939
* @Attribute("maximumItemsPerPage", type="int"),
@@ -49,11 +49,14 @@
4949
* @Attribute("routePrefix", type="string"),
5050
* @Attribute("shortName", type="string"),
5151
* @Attribute("subresourceOperations", type="array"),
52+
* @Attribute("swaggerContext", type="array"),
5253
* @Attribute("validationGroups", type="mixed")
5354
* )
5455
*/
5556
final class ApiResource
5657
{
58+
use AttributesHydratorTrait;
59+
5760
/**
5861
* @var string
5962
*/
@@ -89,11 +92,6 @@ final class ApiResource
8992
*/
9093
public $graphql;
9194

92-
/**
93-
* @var array
94-
*/
95-
public $attributes = [];
96-
9795
/**
9896
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
9997
*
@@ -136,6 +134,13 @@ final class ApiResource
136134
*/
137135
private $filters;
138136

137+
/**
138+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
139+
*
140+
* @var string[]
141+
*/
142+
private $hydraContext;
143+
139144
/**
140145
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
141146
*
@@ -213,6 +218,13 @@ final class ApiResource
213218
*/
214219
private $routePrefix;
215220

221+
/**
222+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
223+
*
224+
* @var array
225+
*/
226+
private $swaggerContext;
227+
216228
/**
217229
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
218230
*
@@ -225,23 +237,6 @@ final class ApiResource
225237
*/
226238
public function __construct(array $values = [])
227239
{
228-
if (isset($values['attributes'])) {
229-
$this->attributes = $values['attributes'];
230-
unset($values['attributes']);
231-
}
232-
233-
foreach ($values as $key => $value) {
234-
if (!property_exists($this, $key)) {
235-
throw new InvalidArgumentException(sprintf('Unknown property "%s" on annotation "%s".', $key, self::class));
236-
}
237-
238-
$property = new \ReflectionProperty($this, $key);
239-
240-
if ($property->isPublic()) {
241-
$this->$key = $value;
242-
} else {
243-
$this->attributes += [Inflector::tableize($key) => $value];
244-
}
245-
}
240+
$this->hydrateAttributes($values);
246241
}
247242
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
/*
14+
* This file is part of the API Platform project.
15+
*
16+
* (c) Kévin Dunglas <[email protected]>
17+
*
18+
* For the full copyright and license information, please view the LICENSE
19+
* file that was distributed with this source code.
20+
*/
21+
22+
namespace ApiPlatform\Core\Annotation;
23+
24+
use ApiPlatform\Core\Exception\InvalidArgumentException;
25+
use Doctrine\Common\Util\Inflector;
26+
27+
/**
28+
* Hydrates attributes from annotation's parameters.
29+
*
30+
* @internal
31+
*
32+
* @author Baptiste Meyer <[email protected]>
33+
* @author Kévin Dunglas <[email protected]>
34+
*/
35+
trait AttributesHydratorTrait
36+
{
37+
/**
38+
* @var array
39+
*/
40+
public $attributes = [];
41+
42+
/**
43+
* @throws InvalidArgumentException
44+
*/
45+
private function hydrateAttributes(array $values)
46+
{
47+
if (isset($values['attributes'])) {
48+
$this->attributes = $values['attributes'];
49+
unset($values['attributes']);
50+
}
51+
52+
foreach ($values as $key => $value) {
53+
if (!property_exists($this, $key)) {
54+
throw new InvalidArgumentException(sprintf('Unknown property "%s" on annotation "%s".', $key, self::class));
55+
}
56+
57+
(new \ReflectionProperty($this, $key))->isPublic() ? $this->$key = $value : $this->attributes += [Inflector::tableize($key) => $value];
58+
}
59+
}
60+
}

src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,14 @@ private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInt
170170
$inAttributes = null;
171171
}
172172

173-
if (false === $fetchEager = $propertyMetadata->getAttribute('fetchEager')) {
173+
if (
174+
(null === $fetchEager = $propertyMetadata->getAttribute('fetch_eager')) &&
175+
(null !== $fetchEager = $propertyMetadata->getAttribute('fetchEager'))
176+
) {
177+
@trigger_error('The "fetchEager" attribute is deprecated since 2.3. Please use "fetch_eager" instead.', E_USER_DEPRECATED);
178+
}
179+
180+
if (false === $fetchEager) {
174181
continue;
175182
}
176183

tests/Annotation/PropertyTest.php renamed to tests/Annotation/ApiPropertyTest.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
/**
2020
* @author Kévin Dunglas <[email protected]>
2121
*/
22-
class PropertyTest extends TestCase
22+
class ApiPropertyTest extends TestCase
2323
{
2424
public function testAssignation()
2525
{
@@ -44,4 +44,22 @@ public function testAssignation()
4444
$this->assertTrue($property->identifier);
4545
$this->assertEquals(['foo' => 'bar'], $property->attributes);
4646
}
47+
48+
public function testConstruct()
49+
{
50+
$property = new ApiProperty([
51+
'fetchable' => true,
52+
'fetchEager' => false,
53+
'jsonldContext' => ['foo' => 'bar'],
54+
'swaggerContext' => ['foo' => 'baz'],
55+
'attributes' => ['unknown' => 'unknown', 'fetchable' => false],
56+
]);
57+
$this->assertEquals([
58+
'fetchable' => false,
59+
'fetch_eager' => false,
60+
'jsonld_context' => ['foo' => 'bar'],
61+
'swagger_context' => ['foo' => 'baz'],
62+
'unknown' => 'unknown',
63+
], $property->attributes);
64+
}
4765
}

tests/Bridge/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,19 @@ public function testApplyToCollectionNoPartial()
809809
}
810810

811811
public function testApplyToCollectionWithANonRedableButFetchEagerProperty()
812+
{
813+
$this->doTestApplyToCollectionWithANonRedableButFetchEagerProperty(false);
814+
}
815+
816+
/**
817+
* @group legacy
818+
*/
819+
public function testLegacyApplyToCollectionWithANonRedableButFetchEagerProperty()
820+
{
821+
$this->doTestApplyToCollectionWithANonRedableButFetchEagerProperty(true);
822+
}
823+
824+
private function doTestApplyToCollectionWithANonRedableButFetchEagerProperty(bool $legacy)
812825
{
813826
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
814827
$resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata());
@@ -817,7 +830,7 @@ public function testApplyToCollectionWithANonRedableButFetchEagerProperty()
817830

818831
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
819832
$relationPropertyMetadata = new PropertyMetadata();
820-
$relationPropertyMetadata = $relationPropertyMetadata->withAttributes(['fetchEager' => true]);
833+
$relationPropertyMetadata = $relationPropertyMetadata->withAttributes([$legacy ? 'fetchEager' : 'fetch_eager' => true]);
821834
$relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
822835
$relationPropertyMetadata = $relationPropertyMetadata->withReadable(false);
823836

@@ -852,6 +865,19 @@ public function testApplyToCollectionWithANonRedableButFetchEagerProperty()
852865
}
853866

854867
public function testApplyToCollectionWithARedableButNotFetchEagerProperty()
868+
{
869+
$this->doTestApplyToCollectionWithARedableButNotFetchEagerProperty(false);
870+
}
871+
872+
/**
873+
* @group legacy
874+
*/
875+
public function testLeacyApplyToCollectionWithARedableButNotFetchEagerProperty()
876+
{
877+
$this->doTestApplyToCollectionWithARedableButNotFetchEagerProperty(true);
878+
}
879+
880+
private function doTestApplyToCollectionWithARedableButNotFetchEagerProperty(bool $legacy)
855881
{
856882
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
857883
$resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata());
@@ -860,7 +886,7 @@ public function testApplyToCollectionWithARedableButNotFetchEagerProperty()
860886

861887
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
862888
$relationPropertyMetadata = new PropertyMetadata();
863-
$relationPropertyMetadata = $relationPropertyMetadata->withAttributes(['fetchEager' => false]);
889+
$relationPropertyMetadata = $relationPropertyMetadata->withAttributes([$legacy ? 'fetchEager' : 'fetch_eager' => false]);
864890
$relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
865891
$relationPropertyMetadata = $relationPropertyMetadata->withReadable(true);
866892

tests/Bridge/Doctrine/Orm/Extension/FilterEagerLoadingExtensionTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ public function testFetchEagerWithNoForceEager()
335335
$filterEagerLoadingExtension = new FilterEagerLoadingExtension($resourceMetadataFactoryProphecy->reveal(), false);
336336
$filterEagerLoadingExtension->applyToCollection($qb, $queryNameGenerator->reveal(), CompositeRelation::class, 'get');
337337

338-
$expected = <<<SQL
338+
$expected = <<<DQL
339339
SELECT o
340340
FROM ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\CompositeRelation o
341341
INNER JOIN o.compositeItem item
@@ -354,7 +354,7 @@ public function testFetchEagerWithNoForceEager()
354354
LEFT JOIN o_2.foo foo_2 WITH o_2.bar = item_2.foo
355355
WHERE item_2.field1 = :foo
356356
)
357-
SQL;
357+
DQL;
358358

359359
$this->assertEquals($this->toDQLString($expected), $qb->getDQL());
360360
}

0 commit comments

Comments
 (0)