# Subresources A Subresource is another way of declaring a resource that usually involves a more complex URI. In API Platform you can declare as many `ApiResource` as you want on a PHP class creating Subresources. Subresources work well by implementing your own state [providers](./state-providers.md) or [processors](./state-processors.md). In API Platform, we provide functional Doctrine and Eloquent layers for subresources, as long as the correct configuration for URI variables is added. ## URI Variables Configuration URI Variables are configured via the `uriVariables` node on an `ApiResource`. It's an array indexed by the variables present in your URI, `/companies/{companyId}/employees/{id}` has two URI variables `companyId` and `id`. For each of these, we need to create a `Link` between the previous and the next node, in this example the link between a Company and an Employee. If you're using the Doctrine or the Eloquent implementation, queries are automatically built using the provided links. ### Answer to a Question > [!NOTE] > In Symfony we use the term “entities”, while the following documentation is mostly for Laravel “models”. For this example we have two classes, a Question and an Answer. We want to find the Answer to the Question about the Universe using the following URI: `/question/42/answer`. Let's start by defining the resources: ```php id; } // ... } // api/src/Entity/Question.php namespace App\Entity; use ApiPlatform\Metadata\ApiResource; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] #[ApiResource] class Question { #[ORM\Id, ORM\Column, ORM\GeneratedValue] private ?int $id = null; #[ORM\Column(type: 'text')] public string $content; #[ORM\OneToOne] #[ORM\JoinColumn(referencedColumnName: 'id', unique: true)] public Answer $answer; public function getId(): ?int { return $this->id; } // ... } ``` ```yaml # The YAML syntax is only supported for Symfony # api/config/api_platform/resources.yaml resources: App\Entity\Answer: ~ App\Entity\Question: ~ ``` ```xml ``` Now to create a new way of retrieving an Answer we will declare another resource on the `Answer` class. To make things work, API Platform needs information about how to retrieve the `Answer` belonging to the `Question`, this is done by configuring the `uriVariables`: ```php new Link( fromClass: Question::class, fromProperty: 'answer' ) ], operations: [new Get()] )] class Answer { // ... } ``` ```yaml # The YAML syntax is only supported for Symfony # api/config/api_platform/resources.yaml resources: App\Entity\Answer: uriTemplate: /questions/{id}/answer uriVariables: id: fromClass: App\Entity\Question fromProperty: answer operations: ApiPlatform\Metadata\Get: ~ App\Entity\Question: ~ ``` ```xml ``` In this example, we instructed API Platform that the `Answer` we retrieve comes **from** the **class** `Question` **from** the **property** `answer` of that class. URI Variables are defined using Links (`ApiPlatform\Metadata\Link`). A `Link` can be binded either from or to a class and a property. If we had a `relatedQuestions` property on the `Answer` we could retrieve the collection of related questions via the following definition: ```php #[ApiResource( uriTemplate: '/answers/{id}/related_questions.{_format}', uriVariables: [ 'id' => new Link(fromClass: Answer::class, fromProperty: 'relatedQuestions') ], operations: [new GetCollection()] )] ``` ```yaml #The YAML syntax is only supported for Symfony # api/config/api_platform/resources.yaml resources: App\Entity\Question: uriTemplate: /answers/{id}/related_questions.{_format} uriVariables: id: fromClass: App\Entity\Answer fromProperty: relatedQuestions operations: ApiPlatform\Metadata\GetCollection: ~ ``` ```xml ``` ### Company Employee's > [!NOTE] > In Symfony we use the term “entities”, while the following documentation is mostly for Laravel “models”. Note that in this example, we declared an association using Doctrine only between Employee and Company using a ManyToOne. There is no inverse association hence the use of `toProperty` in the URI Variables definition. The following declares a few subresources: - `/companies/{companyId}/employees/{id}` - get an employee belonging to a company - `/companies/{companyId}/employees` - get the company employee's ```php new Link(fromClass: Company::class, toProperty: 'company'), 'id' => new Link(fromClass: Employee::class), ], operations: [ new Get() ] )] #[ApiResource( uriTemplate: '/companies/{companyId}/employees', uriVariables: [ 'companyId' => new Link(fromClass: Company::class, toProperty: 'company'), ], operations: [ new GetCollection() ] )] class Employee { #[ORM\Id, ORM\Column, ORM\GeneratedValue] public ?int $id; #[ORM\Column] public string $name; #[ORM\ManyToOne(targetEntity: Company::class)] public ?Company $company; public function getId() { return $this->id; } } ``` Now let's add the Company class: ```php new Link(fromClass: Employee::class, fromProperty: 'company'), ], operations: [ new Get() ] )] class Company { // ... } ``` ## Security > [!WARNING] > This is not yet available with Laravel, you're welcome to contribute [on GitHub](https://github.com/api-platform/core) In order to use Symfony's built-in security system on subresources the security option of the `Link` attribute can be used. To restrict the access to a subresource based on the parent object simply use the Symfony expression language as you would do normally, with the exception that the name defined in `toProperty` or `fromProperty` is used to access the object. Alternatively you can also use the `securityObjectName` to set a custom name. ```php new Link(fromClass: Employee::class, toProperty: 'company', security: "is_granted(some_voter, company)"), ], operations: [ new Get() ] )] class Company { // ... } ``` This is currently an experimental feature disabled by default. To enable it please set `enable_link_security` to true: ```yaml # api/config/packages/api_platform.yaml api_platform: enable_link_security: true ```