feat: ajout de rules pour cursor

This commit is contained in:
ext.jeremy.guillot@maxicoffee.domains
2025-03-23 17:35:27 +01:00
parent fe92e53be7
commit 19a697c712
4 changed files with 679 additions and 0 deletions

View File

@@ -0,0 +1,213 @@
---
description:
globs:
alwaysApply: true
---
# API Platform dans Mangarr
## Structure de l'API
L'API est organisée dans la couche Infrastructure de chaque domaine :
```
Domain/Manga/Infrastructure/ApiPlatform/
├── Resource/ # Configuration des ressources API
│ └── MangaResource.php
├── State/ # Providers et Processors
├── Provider/ # State Providers
└── Processor/ # State Processors
```
## Règles d'Organisation
### 1. Resources
- Localisation : `Infrastructure/ApiPlatform/Resource/`
- Principes :
- Une classe = une ressource API
- Documentation exhaustive avec les attributs PHP 8
- Validation contraintes avec les attributs Symfony
- Nommage : `{Nom}Resource`
### 2. State Providers/Processors
- Localisation : `Infrastructure/ApiPlatform/State/`
- Principes :
- Utiliser les cas d'utilisation du domaine (Commands/Queries)
- Ne pas contenir de logique métier
- Conversion Resource ↔ Command/Query
- Nommage : `{Action}{Resource}StateProvider/Processor`
## Exemples de Code
### 1. Resource API
```php
namespace App\Domain\Manga\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Post;
use App\Domain\Manga\Infrastructure\ApiPlatform\State\Provider\GetMangaStateProvider;
use App\Domain\Manga\Infrastructure\ApiPlatform\State\Processor\CreateMangaStateProcessor;
use Symfony\Component\Validator\Constraints as Assert;
#[ApiResource(
shortName: 'Manga',
operations: [
new Get(
uriTemplate: '/mangas/{id}',
provider: GetMangaStateProvider::class,
description: 'Récupère un manga par son identifiant',
openapi: [
'summary' => 'Récupère un manga',
'200' => [
'description' => 'Manga trouvé',
'content' => [
'application/json' => [
'schema' => [
'type' => 'object',
'properties' => [
'id' => ['type' => 'string', 'format' => 'uuid'],
'title' => ['type' => 'string'],
'description' => ['type' => 'string', 'nullable' => true],
'authors' => [
'type' => 'array',
'items' => ['type' => 'string']
],
'coverUrl' => ['type' => 'string', 'format' => 'uri', 'nullable' => true]
],
'required' => ['id', 'title', 'authors']
]
]
]
],
'404' => [
'description' => 'Manga non trouvé'
]
]
),
new Post(
uriTemplate: '/mangas',
processor: CreateMangaStateProcessor::class,
description: 'Crée un nouveau manga',
openapi: [
'requestBody' => [
'content' => [
'application/json' => [
'schema' => [
'type' => 'object',
'properties' => [
'title' => ['type' => 'string'],
'description' => ['type' => 'string', 'nullable' => true],
'authors' => [
'type' => 'array',
'items' => ['type' => 'string']
],
'coverUrl' => ['type' => 'string', 'format' => 'uri', 'nullable' => true]
],
'required' => ['title']
]
]
]
],
'responses' => [
'201' => [
'description' => 'Manga créé'
],
'400' => [
'description' => 'Données invalides'
]
]
]
)
]
)]
class MangaResource
{
}
```
### 2. State Provider
```php
namespace App\Domain\Manga\Infrastructure\ApiPlatform\State\Provider;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Domain\Manga\Application\Query\GetMangaByIdQuery;
use App\Domain\Shared\Contract\Response;
use Symfony\Component\Messenger\MessageBusInterface;
class GetMangaStateProvider implements ProviderInterface
{
public function __construct(
private readonly MessageBusInterface $queryBus
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?Response
{
$query = new GetMangaByIdQuery($uriVariables['id']);
$response = $this->queryBus->dispatch($query);
if (null === $response) {
return null;
}
return $response;
}
}
```
### 3. State Processor
```php
namespace App\Domain\Manga\Infrastructure\ApiPlatform\State\Processor;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Domain\Manga\Application\Command\CreateMangaCommand;
use Symfony\Component\Messenger\MessageBusInterface;
class CreateMangaStateProcessor implements ProcessorInterface
{
public function __construct(
private readonly MessageBusInterface $commandBus
) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): void
{
assert($data instanceof MangaResource);
$command = new CreateMangaCommand(
title: $data->title,
description: $data->description,
authors: $data->authors,
coverUrl: $data->coverUrl
);
$this->commandBus->dispatch($command);
}
}
```
## Bonnes Pratiques
### 1. Documentation
- Documentation exhaustive des endpoints
- Description claire des paramètres
- Exemples de requêtes/réponses
- Documentation des codes d'erreur
### 2. Validation
- Validation stricte des entrées
- Groupes de validation par contexte
- Messages d'erreur explicites
- Validation des types et formats
### 3. Sécurité
- Définition claire des accès
- Validation des permissions
- Sanitization des entrées
- Gestion des erreurs sécurisée
### 4. Performance
- Pagination par défaut
- Sélection des champs (sparse fieldsets)
- Gestion des includes (relationships)
- Cache approprié