diff --git a/src/Annotation/ApiProperty.php b/src/Annotation/ApiProperty.php index 48de8d3f5b1..1274f1c28aa 100644 --- a/src/Annotation/ApiProperty.php +++ b/src/Annotation/ApiProperty.php @@ -20,9 +20,17 @@ * * @Annotation * @Target({"METHOD", "PROPERTY"}) + * @Attributes( + * @Attribute("fetchable", type="bool"), + * @Attribute("fetchEager", type="bool"), + * @Attribute("jsonldContext", type="array"), + * @Attribute("swaggerContext", type="array") + * ) */ final class ApiProperty { + use AttributesHydratorTrait; + /** * @var string */ @@ -64,7 +72,38 @@ final class ApiProperty public $identifier; /** + * @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112 + * + * @var bool + */ + private $fetchable; + + /** + * @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112 + * + * @var bool + */ + private $fetchEager; + + /** + * @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112 + * + * @var array + */ + private $jsonldContext; + + /** + * @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112 + * * @var array */ - public $attributes = []; + private $swaggerContext; + + /** + * @throws InvalidArgumentException + */ + public function __construct(array $values = []) + { + $this->hydrateAttributes($values); + } } diff --git a/src/Annotation/ApiResource.php b/src/Annotation/ApiResource.php index ba06031e247..a7e19f4500e 100644 --- a/src/Annotation/ApiResource.php +++ b/src/Annotation/ApiResource.php @@ -13,8 +13,7 @@ namespace ApiPlatform\Core\Annotation; -use ApiPlatform\Core\Exception\InvalidArgumentException; -use Doctrine\Common\Util\Inflector; +use Doctrine\Common\Annotations\Annotation\Attribute; /** * ApiResource annotation. @@ -34,6 +33,7 @@ * @Attribute("forceEager", type="bool"), * @Attribute("filters", type="string[]"), * @Attribute("graphql", type="array"), + * @Attribute("hydraContext", type="array"), * @Attribute("iri", type="string"), * @Attribute("itemOperations", type="array"), * @Attribute("maximumItemsPerPage", type="int"), @@ -49,11 +49,14 @@ * @Attribute("routePrefix", type="string"), * @Attribute("shortName", type="string"), * @Attribute("subresourceOperations", type="array"), + * @Attribute("swaggerContext", type="array"), * @Attribute("validationGroups", type="mixed") * ) */ final class ApiResource { + use AttributesHydratorTrait; + /** * @var string */ @@ -89,11 +92,6 @@ final class ApiResource */ public $graphql; - /** - * @var array - */ - public $attributes = []; - /** * @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112 * @@ -136,6 +134,13 @@ final class ApiResource */ private $filters; + /** + * @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112 + * + * @var string[] + */ + private $hydraContext; + /** * @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112 * @@ -213,6 +218,13 @@ final class ApiResource */ private $routePrefix; + /** + * @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112 + * + * @var array + */ + private $swaggerContext; + /** * @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112 * @@ -225,23 +237,6 @@ final class ApiResource */ public function __construct(array $values = []) { - if (isset($values['attributes'])) { - $this->attributes = $values['attributes']; - unset($values['attributes']); - } - - foreach ($values as $key => $value) { - if (!property_exists($this, $key)) { - throw new InvalidArgumentException(sprintf('Unknown property "%s" on annotation "%s".', $key, self::class)); - } - - $property = new \ReflectionProperty($this, $key); - - if ($property->isPublic()) { - $this->$key = $value; - } else { - $this->attributes += [Inflector::tableize($key) => $value]; - } - } + $this->hydrateAttributes($values); } } diff --git a/src/Annotation/AttributesHydratorTrait.php b/src/Annotation/AttributesHydratorTrait.php new file mode 100644 index 00000000000..96f6363a459 --- /dev/null +++ b/src/Annotation/AttributesHydratorTrait.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); +/* + * This file is part of the API Platform project. + * + * (c) Kévin Dunglas + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ApiPlatform\Core\Annotation; + +use ApiPlatform\Core\Exception\InvalidArgumentException; +use Doctrine\Common\Util\Inflector; + +/** + * Hydrates attributes from annotation's parameters. + * + * @internal + * + * @author Baptiste Meyer + * @author Kévin Dunglas + */ +trait AttributesHydratorTrait +{ + /** + * @var array + */ + public $attributes = []; + + /** + * @throws InvalidArgumentException + */ + private function hydrateAttributes(array $values) + { + if (isset($values['attributes'])) { + $this->attributes = $values['attributes']; + unset($values['attributes']); + } + + foreach ($values as $key => $value) { + if (!property_exists($this, $key)) { + throw new InvalidArgumentException(sprintf('Unknown property "%s" on annotation "%s".', $key, self::class)); + } + + (new \ReflectionProperty($this, $key))->isPublic() ? $this->$key = $value : $this->attributes += [Inflector::tableize($key) => $value]; + } + } +} diff --git a/src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php b/src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php index f8ea0926027..6422e5894ab 100644 --- a/src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php +++ b/src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php @@ -170,7 +170,14 @@ private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInt $inAttributes = null; } - if (false === $fetchEager = $propertyMetadata->getAttribute('fetchEager')) { + if ( + (null === $fetchEager = $propertyMetadata->getAttribute('fetch_eager')) && + (null !== $fetchEager = $propertyMetadata->getAttribute('fetchEager')) + ) { + @trigger_error('The "fetchEager" attribute is deprecated since 2.3. Please use "fetch_eager" instead.', E_USER_DEPRECATED); + } + + if (false === $fetchEager) { continue; } diff --git a/tests/Annotation/PropertyTest.php b/tests/Annotation/ApiPropertyTest.php similarity index 68% rename from tests/Annotation/PropertyTest.php rename to tests/Annotation/ApiPropertyTest.php index 1a5c7df53f3..e57e288b17b 100644 --- a/tests/Annotation/PropertyTest.php +++ b/tests/Annotation/ApiPropertyTest.php @@ -19,7 +19,7 @@ /** * @author Kévin Dunglas */ -class PropertyTest extends TestCase +class ApiPropertyTest extends TestCase { public function testAssignation() { @@ -44,4 +44,22 @@ public function testAssignation() $this->assertTrue($property->identifier); $this->assertEquals(['foo' => 'bar'], $property->attributes); } + + public function testConstruct() + { + $property = new ApiProperty([ + 'fetchable' => true, + 'fetchEager' => false, + 'jsonldContext' => ['foo' => 'bar'], + 'swaggerContext' => ['foo' => 'baz'], + 'attributes' => ['unknown' => 'unknown', 'fetchable' => false], + ]); + $this->assertEquals([ + 'fetchable' => false, + 'fetch_eager' => false, + 'jsonld_context' => ['foo' => 'bar'], + 'swagger_context' => ['foo' => 'baz'], + 'unknown' => 'unknown', + ], $property->attributes); + } } diff --git a/tests/Bridge/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php b/tests/Bridge/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php index 9522168c2dc..d07b5f78035 100644 --- a/tests/Bridge/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php +++ b/tests/Bridge/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php @@ -809,6 +809,19 @@ public function testApplyToCollectionNoPartial() } public function testApplyToCollectionWithANonRedableButFetchEagerProperty() + { + $this->doTestApplyToCollectionWithANonRedableButFetchEagerProperty(false); + } + + /** + * @group legacy + */ + public function testLegacyApplyToCollectionWithANonRedableButFetchEagerProperty() + { + $this->doTestApplyToCollectionWithANonRedableButFetchEagerProperty(true); + } + + private function doTestApplyToCollectionWithANonRedableButFetchEagerProperty(bool $legacy) { $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); $resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata()); @@ -817,7 +830,7 @@ public function testApplyToCollectionWithANonRedableButFetchEagerProperty() $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); $relationPropertyMetadata = new PropertyMetadata(); - $relationPropertyMetadata = $relationPropertyMetadata->withAttributes(['fetchEager' => true]); + $relationPropertyMetadata = $relationPropertyMetadata->withAttributes([$legacy ? 'fetchEager' : 'fetch_eager' => true]); $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false); $relationPropertyMetadata = $relationPropertyMetadata->withReadable(false); @@ -852,6 +865,19 @@ public function testApplyToCollectionWithANonRedableButFetchEagerProperty() } public function testApplyToCollectionWithARedableButNotFetchEagerProperty() + { + $this->doTestApplyToCollectionWithARedableButNotFetchEagerProperty(false); + } + + /** + * @group legacy + */ + public function testLeacyApplyToCollectionWithARedableButNotFetchEagerProperty() + { + $this->doTestApplyToCollectionWithARedableButNotFetchEagerProperty(true); + } + + private function doTestApplyToCollectionWithARedableButNotFetchEagerProperty(bool $legacy) { $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); $resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata()); @@ -860,7 +886,7 @@ public function testApplyToCollectionWithARedableButNotFetchEagerProperty() $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); $relationPropertyMetadata = new PropertyMetadata(); - $relationPropertyMetadata = $relationPropertyMetadata->withAttributes(['fetchEager' => false]); + $relationPropertyMetadata = $relationPropertyMetadata->withAttributes([$legacy ? 'fetchEager' : 'fetch_eager' => false]); $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true); $relationPropertyMetadata = $relationPropertyMetadata->withReadable(true); diff --git a/tests/Bridge/Doctrine/Orm/Extension/FilterEagerLoadingExtensionTest.php b/tests/Bridge/Doctrine/Orm/Extension/FilterEagerLoadingExtensionTest.php index 9f2edeb7931..780ac1afc27 100644 --- a/tests/Bridge/Doctrine/Orm/Extension/FilterEagerLoadingExtensionTest.php +++ b/tests/Bridge/Doctrine/Orm/Extension/FilterEagerLoadingExtensionTest.php @@ -335,7 +335,7 @@ public function testFetchEagerWithNoForceEager() $filterEagerLoadingExtension = new FilterEagerLoadingExtension($resourceMetadataFactoryProphecy->reveal(), false); $filterEagerLoadingExtension->applyToCollection($qb, $queryNameGenerator->reveal(), CompositeRelation::class, 'get'); - $expected = <<assertEquals($this->toDQLString($expected), $qb->getDQL()); }