diff --git a/docs/guides/return-the-iri-of-your-resources-relations.php b/docs/guides/return-the-iri-of-your-resources-relations.php index 92602910eb3..ba504b495d3 100644 --- a/docs/guides/return-the-iri-of-your-resources-relations.php +++ b/docs/guides/return-the-iri-of-your-resources-relations.php @@ -15,21 +15,18 @@ use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Link; use ApiPlatform\Metadata\Operation; - use ApiPlatform\State\ProviderInterface; - use Symfony\Component\Serializer\Annotation\Groups; #[ApiResource( operations: [ - new Get(normalizationContext: ['groups' => ['read']], provider: Brand::class), + new Get(provider: Brand::class.'::provide'), ], )] - class Brand implements ProviderInterface + class Brand { public function __construct( #[ApiProperty(identifier: true)] public readonly int $id = 1, - #[Groups('read')] public readonly string $name = 'Anon', // Setting uriTemplate on a relation with a resource collection will try to find the related operation. @@ -38,13 +35,11 @@ public function __construct( * @var array $cars */ #[ApiProperty(uriTemplate: '/brands/{brandId}/cars')] - #[Groups('read')] private array $cars = [], // Setting uriTemplate on a relation with a resource item will try to find the related operation. // It is based on the uriTemplate set on the operation defined on the Address resource (see below). #[ApiProperty(uriTemplate: '/brands/{brandId}/addresses/{id}')] - #[Groups('read')] private ?Address $headQuarters = null ) { @@ -79,9 +74,9 @@ public function setHeadQuarters(?Address $headQuarters): self return $this; } - public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + public static function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null { - return (new self(1, 'Ford')) + return (new Brand(1, 'Ford')) ->setHeadQuarters(new Address(1, 'One American Road near Michigan Avenue, Dearborn, Michigan')) ->addCar(new Car(1, 'Torpedo Roadster')); } @@ -142,7 +137,6 @@ class Address public function __construct( #[ApiProperty(identifier: true)] public readonly int $id = 1, - #[Groups('read')] public readonly string $name = 'Anon', private ?Brand $brand = null ) @@ -176,30 +170,31 @@ public function setBrand(Brand $brand): void function request(): Request { - return Request::create('/brands/1.jsonld', 'GET'); + return Request::create(uri: '/brands/1', method: 'GET', server: ['HTTP_ACCEPT' => 'application/ld+json']); } } namespace App\Tests { - use ApiPlatform\Playground\Test\TestGuideTrait; use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use App\ApiResource\Brand; final class BrandTest extends ApiTestCase { - use TestGuideTrait; public function testResourceExposeIRI(): void { - static::createClient()->request('GET', '/brands/1.jsonld'); + static::createClient()->request('GET', '/brands/1', ['headers' => [ + 'Accept: application/ld+json' + ]]); $this->assertResponseIsSuccessful(); - $this->assertMatchesResourceCollectionJsonSchema(Brand::class, '_api_/brand{._format}_get_item'); + $this->assertMatchesResourceCollectionJsonSchema(Brand::class, '_api_/brands/{id}{._format}_get'); $this->assertJsonContains([ "@context" => "/contexts/Brand", "@id" => "/brands/1", "@type" => "Brand", + "name"=> "Ford", "cars" => "/brands/1/cars", "headQuarters" => "/brands/1/addresses/1" ]); diff --git a/docs/src/Kernel.php b/docs/src/Kernel.php index 30f4e109d57..b0bcd723bec 100644 --- a/docs/src/Kernel.php +++ b/docs/src/Kernel.php @@ -13,7 +13,6 @@ namespace ApiPlatform\Playground; -use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Playground\DependencyInjection\Compiler\AttributeFilterPass; use ApiPlatform\Playground\DependencyInjection\Compiler\FilterPass; use ApiPlatform\Playground\Doctrine\StaticMappingDriver; @@ -28,6 +27,7 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Config\Resource\ReflectionClassResource; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -72,11 +72,21 @@ private function configureContainer(ContainerConfigurator $container, LoaderInte continue; } - if (!str_starts_with($ns, 'App\\Entity')) { + $builder->addResource(new ReflectionClassResource($refl)); + + $hasMetadataAttribute = false; + foreach ($refl->getAttributes() as $a) { + if (str_starts_with($a->getName(), 'ApiPlatform\\Metadata')) { + $hasMetadataAttribute = true; + break; + } + } + + if (str_starts_with($ns, 'App\\Entity')) { $entities[] = $class; } - if ($refl->getAttributes(ApiResource::class, \ReflectionAttribute::IS_INSTANCEOF)) { + if ($hasMetadataAttribute) { $resources[] = $class; continue; } @@ -94,7 +104,7 @@ private function configureContainer(ContainerConfigurator $container, LoaderInte sprintf('sqlite:///%s/%s', $this->getCacheDir(), 'data.db') ); - $services->set('doctrine.orm.default_metadata_driver', StaticMappingDriver::class)->args(['$classes' => $resources]); + $services->set('doctrine.orm.default_metadata_driver', StaticMappingDriver::class)->args(['$classes' => $entities]); if (\function_exists('App\DependencyInjection\configure')) { configure($container);