Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 0 additions & 164 deletions core/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,167 +479,3 @@ resources:
API Platform will find the operation matching this `itemUriTemplate` and use it to generate the IRI.

If this option is not set, the first `Get` operation is used to generate the IRI.

## Expose a Model Without Any Routes

Sometimes, you may want to expose a model, but want it to be used through subrequests only, and never through item or collection operations.
Because the OpenAPI standard requires at least one route to be exposed to make your models consumable, let's see how you can manage this kind
of issue.

Let's say you have the following entities in your project:

```php
<?php
// api/src/Entity/Place.php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class Place
{
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
private ?int $id = null;

#[ORM\Column]
private string $name = '';

#[ORM\Column(type: 'float')]
private float $latitude = 0;

#[ORM\Column(type: 'float')]
private float $longitude = 0;

// ...
}
```

```php
<?php
// api/src/Entity/Weather.php
namespace App\Entity;

class Weather
{
private float $temperature;

private float $pressure;

// ...
}
```

We don't save the `Weather` entity in the database, since we want to return the weather in real time when it is queried.
Because we want to get the weather for a known place, it is more reasonable to query it through a subresource of the `Place` entity, so let's do this:

```php
<?php
// api/src/Entity/Place.php
namespace App\Entity;

use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Put;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\ApiResource;
use App\Controller\GetWeather;
use Doctrine\ORM\Mapping as ORM;

#[ApiResource(
operations: [
new Get(),
new Put(),
new Delete(),
new Get(name: 'weather', uriTemplate: '/places/{id}/weather', controller: GetWeather::class),
new GetCollection(),
new Post(),
]
)]
#[ORM\Entity]
class Place
{
// ...
```

The `GetWeather` controller fetches the weather for the given city and returns an instance of the `Weather` entity.
This implies that API Platform has to know about this entity, so we will need to make it an API resource too:

```php
<?php
// api/src/Entity/Weather.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;

#[ApiResource]
class Weather
{
// ...
```

This will expose the `Weather` model, but also all the default CRUD routes: `GET`, `PATCH`, `DELETE` and `POST`, which is nonsense in our context.
Since we are required to expose at least one route, let's expose just one:

```php
<?php
// api/src/Entity/Weather.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;

#[ApiResource(operations: [
new Get(controller: SomeRandomController::class)
])]
class Weather
{
// ...
}
```

This way, we expose a route that will do… nothing. Note that the controller does not even need to exist.

It's almost done, we have just one final issue: our fake item operation is visible in the API docs.
To remove it, we will need to [decorate the Swagger documentation](openapi.md#overriding-the-openapi-specification).
Then, remove the route from the decorator:

```php
<?php
// src/OpenApi/OpenApiFactory.php
namespace App\OpenApi;

use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\OpenApi\OpenApi;
use ApiPlatform\OpenApi\Model;

final class OpenApiFactory implements OpenApiFactoryInterface
{
private $decorated;

public function __construct(OpenApiFactoryInterface $decorated)
{
$this->decorated = $decorated;
}

public function __invoke(array $context = []): OpenApi
{
$openApi = $this->decorated->__invoke($context);

$paths = $openApi->getPaths()->getPaths();

$filteredPaths = new Model\Paths();
foreach ($paths as $path => $pathItem) {
// If a prefix is configured on API Platform's routes, it must appear here.
if ($path === '/weathers/{id}') {
continue;
}
$filteredPaths->addPath($path, $pathItem);
}

return $openApi->withPaths($filteredPaths);
}
}
```

That's it: your route is gone!