// src/App/ApiResource.php
namespace App\ApiResource;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Operation;
#[ApiResource(
operations: [
new Get(provider: Brand::class.'::provide'),
],
)]
class Brand
{
public function __construct(
#[ApiProperty(identifier: true)]
public readonly int $id = 1,
public readonly string $name = 'Anon',
/**
* @var array<int, Car> $cars
*/
#[ApiProperty(uriTemplate: '/brands/{brandId}/cars')]
private array $cars = [],
#[ApiProperty(uriTemplate: '/brands/{brandId}/addresses/{id}')]
private ?Address $headQuarters = null
) {
}
/**
* @return array<int, Car>
*/
public function getCars(): array
{
return $this->cars;
}
public function addCar(Car $car): self
{
$car->setBrand($this);
$this->cars[] = $car;
return $this;
}
public function getHeadQuarters(): ?Address
{
return $this->headQuarters;
}
public function setHeadQuarters(?Address $headQuarters): self
{
$headQuarters?->setBrand($this);
$this->headQuarters = $headQuarters;
return $this;
}
public static function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
return (new self(1, 'Ford'))
->setHeadQuarters(new Address(1, 'One American Road near Michigan Avenue, Dearborn, Michigan'))
->addCar(new Car(1, 'Torpedo Roadster'));
}
}
#[ApiResource(
operations: [
new Get(),
new GetCollection(uriTemplate: '/cars'),
new GetCollection(
uriTemplate: '/brands/{brandId}/cars',
uriVariables: [
'brandId' => new Link(toProperty: 'brand', fromClass: Brand::class),
]
),
],
)]
class Car
{
public function __construct(
#[ApiProperty(identifier: true)]
public readonly int $id = 1,
public readonly string $name = 'Anon',
private ?Brand $brand = null
) {
}
public function getBrand(): Brand
{
return $this->brand;
}
public function setBrand(Brand $brand): void
{
$this->brand = $brand;
}
}
#[ApiResource(
operations: [
new Get(uriTemplate: '/addresses/{id}'),
new Get(
uriTemplate: '/brands/{brandId}/addresses/{id}',
uriVariables: [
'brandId' => new Link(toProperty: 'brand', fromClass: Brand::class),
'id' => new Link(fromClass: Address::class),
]
),
],
)]
class Address
{
public function __construct(
#[ApiProperty(identifier: true)]
public readonly int $id = 1,
public readonly string $name = 'Anon',
private ?Brand $brand = null
) {
}
public function getBrand(): Brand
{
return $this->brand;
}
public function setBrand(Brand $brand): void
{
$this->brand = $brand;
}
}
GetCollection
operation on the target resource, it will result in a NotFoundException
.
The OpenAPI documentation will set the properties as read-only
of type string
in the format iri-reference
for JSON-LD
, JSON:API
and HAL
formats.
The Hydra documentation will set the properties as Link
with the right domain, with readable
to true
but writable
to false
.
When using JSON:API or HAL formats, the IRI will be used and set links, embedded and relationship.
Additional Note: If you are using the default doctrine provider, this will prevent unnecessary sql join and related processing.// src/App/Playground.php
namespace App\Playground;
use Symfony\Component\HttpFoundation\Request;
function request(): Request
{
return Request::create(uri: '/brands/1', method: 'GET', server: ['HTTP_ACCEPT' => 'application/ld+json']);
}
// src/App/Tests.php
namespace App\Tests;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\ApiResource\Brand;
final class BrandTest extends ApiTestCase
{
public function testResourceExposeIRI(): void
{
static::createClient()->request('GET', '/brands/1', ['headers' => [
'Accept: application/ld+json',
]]);
$this->assertResponseIsSuccessful();
$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',
]);
}
}
Made with love by
Les-Tilleuls.coop can help you design and develop your APIs and web projects, and train your teams in API Platform, Symfony, Next.js, Kubernetes and a wide range of other technologies.
Learn more