sociolink / api-resource-bundle
Génère les ressources API Platform (DTOs, State Processors/Providers, filtres QueryParameter) à partir des entités Doctrine — outil de développement, jamais requis en production.
Package info
github.com/SocioLink/ApiResourceBundle
Type:symfony-bundle
pkg:composer/sociolink/api-resource-bundle
Requires
- php: >=8.5
- ext-tokenizer: *
- api-platform/doctrine-orm: ^5.0
- api-platform/metadata: ^5.0
- api-platform/state: ^5.0
- api-platform/validator: ^5.0
- doctrine/dbal: ^4.0
- doctrine/inflector: ^2.0
- doctrine/orm: ^3.5
- nikic/php-parser: ^5.6
- symfony/config: ^7.4 || ^8.0
- symfony/console: ^7.4 || ^8.0
- symfony/dependency-injection: ^7.4 || ^8.0
- symfony/filesystem: ^7.4 || ^8.0
- symfony/http-kernel: ^7.4 || ^8.0
- twig/twig: ^3.0
Requires (Dev)
- ext-pdo_sqlite: *
- api-platform/test: ^5.0
- odolbeau/phone-number-bundle: ^4.2
- phpstan/phpstan: ^2.1.30
- phpstan/phpstan-symfony: ^2.0
- phpunit/phpunit: ^11.0
- symfony/framework-bundle: ^7.4 || ^8.0
- symfony/yaml: ^7.4 || ^8.0
Suggests
- api-platform/test: Requis par les tests fonctionnels générés avec --with-tests (ApiPlatform\Test\ApiTestCase)
- odolbeau/phone-number-bundle: Type Doctrine phone_number, reconnu sans configuration : DTOs générés typés libphonenumber\PhoneNumber
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-27 22:38:54 UTC
README
Génère les ressources API Platform (DTOs, State Processors/Providers, paramètres de filtrage
#[QueryParameter]) à partir des entités Doctrine, via une commande CLI interactive.
Outil de développement. Le code qu'il génère ne référence jamais le bundle — il ne dépend que d'API Platform, de Symfony et de Doctrine — et fonctionne donc en production sans lui.
Licence. Ce dépôt est public afin d'être distribué via Packagist, mais il n'est pas open source : aucun droit d'utilisation, de modification ou de redistribution n'est accordé en dehors des projets explicitement autorisés par l'auteur. Voir LICENSE.
Prérequis
| Minimum | |
|---|---|
| PHP | 8.5 |
| Symfony | 7.4 (compatible 7.4, 8.0, 8.1 et suivants) |
| API Platform | 5.0, composants metadata, state, doctrine-orm et validator |
| Doctrine | ORM 3.5, DBAL 4.0 |
Le bundle dépend des composants d'API Platform, jamais du paquet monolithique api-platform/core.
Il s'installe donc sans rien retirer, que le projet utilise les composants (api-platform/symfony,
installation actuelle) ou api-platform/core (qui remplace ces composants). Les tests générés par
--with-tests utilisent en plus api-platform/test (ApiPlatform\Test\ApiTestCase) :
composer require --dev api-platform/test
Le bundle reconnaît sans configuration le type Doctrine phone_number
d'odolbeau/phone-number-bundle (^4.2, voir
Types Doctrine personnalisés). Ce bundle n'est pas une dépendance
obligatoire : seuls les projets qui stockent des numéros de téléphone l'installent, en require, car
leurs entités en dépendent aussi en production.
Installation
composer require --dev sociolink/api-resource-bundle
Avec Symfony Flex
Dans un projet utilisant Symfony Flex (tout projet créé avec symfony new ou symfony/skeleton),
le bundle est enregistré automatiquement. Le paquet étant installé avec --dev, Flex le réserve aux
environnements dev et test.
Par défaut, Flex applique une recette auto-générée, qui enregistre le bundle sans créer de fichier de configuration (toutes les clés sont optionnelles, voir Configuration) :
Configuring sociolink/api-resource-bundle (>=1.0): From auto-generated recipe
Le dépôt publie aussi sa propre recette (dossier flex/, compilé depuis recipe/), qui crée en
plus config/packages/sociolink_api_resource.yaml, toutes les clés en commentaire avec leur valeur par
défaut. Pour l'utiliser, déclarez ce dépôt de recettes dans le composer.json du projet, avant
d'installer le bundle :
"extra": { "symfony": { "endpoint": [ "https://api.github.com/repos/SocioLink/ApiResourceBundle/contents/flex/index.json", "flex://defaults" ] } }
flex://defaults conserve les recettes officielles de Symfony pour les autres paquets. Composer
affiche alors From github.com/SocioLink/ApiResourceBundle:main.
Sans Flex
Ajoutez le bundle à la main dans config/bundles.php :
return [ // ... SocioLink\ApiResourceBundle\ApiResourceBundle::class => ['dev' => true, 'test' => true], ];
Utilisation
# Menu interactif (terminal requis) : entités regroupées par namespace, « All » en dernier php bin/console generate:resource # Une entité précise — nom court ou FQCN php bin/console generate:resource Article php bin/console generate:resource 'App\Entity\Blog\Article' # Toutes les entités, sans menu (CI, --no-interaction) php bin/console generate:resource '*' --force # Écraser les fichiers existants php bin/console generate:resource Article --force # Aperçu sans écriture php bin/console generate:resource Article --preview
Sans argument :
- dans un terminal, le menu numéroté s'affiche (voir ci-dessous) ;
- sans terminal (CI,
--no-interaction), la commande échoue avec un message explicite — jamais de traitement implicite de toutes les entités.
Menu interactif
Sélection de l'entité
----------------------
App\Entity
[1] User
App\Entity\Blog
[2] Article
[3] Comment
App\Entity\Shop
[4] Order
[5] Product
[6] All — toutes les entités (5)
Numéro de votre choix (1-6):
Les entités sont regroupées par namespace (ordre alphabétique), numérotées en continu, choix unique ; « All » (dernier numéro) traite toutes les entités existantes.
Options
Destructrices
| Option | Effet |
|---|---|
--force / -f |
Écrase les fichiers existants et réinjecte #[ApiResource]. |
--reinit / -r |
Avec --force : supprime les dossiers DTO/<Entité> et State/<Entité> des seules entités traitées (confirmation demandée dans un terminal). |
Mutuellement exclusives (une seule à la fois)
| Option | Effet |
|---|---|
--only-resource / -o |
Injecte #[ApiResource] + filtres uniquement, aucun artefact. |
--with-provider / -w |
Mode par défaut + Provider pour GET {id}, câblé sur Get. |
--toggle-boolean / -t |
Un ToggleDto/ToggleProcessor unique pour tous les booléens. |
--detach-boolean / -d |
Un DTO + Processor PATCH individuel par booléen. |
--all / -a |
#[ApiResource] libre + tous les artefacts générés mais non liés. |
Indépendantes
| Option | Effet |
|---|---|
--dry-run |
Simule sans écrire aucun fichier — entités comprises. |
--preview |
Affiche le code généré dans la console (implique --dry-run). |
--with-tests |
Génère des tests fonctionnels ApiTestCase (paquet api-platform/test). |
--sub-resources |
Sous-ressources pour les relations OneToMany. |
--graphql-filters |
Reporte les paramètres de filtrage sur QueryCollection (GraphQL). |
--with-mercure |
Injecte la directive mercure (privée par défaut : mercure: ['private' => true]). |
--public |
Avec --with-mercure : mercure: true (mises à jour publiées publiquement) au lieu de privées. Refusé sans --with-mercure. |
Sans --with-mercure, aucune directive mercure n'est injectée.
La commande se termine en échec (code 1) si une entité n'a pas pu être traitée ; les autres entités sont tout de même générées.
Artefacts générés
| Artefact | Opération |
|---|---|
<Entité>CreateDto + Processor |
POST — contraintes #[Assert\*] de l'entité recopiées |
<Entité>UpdateDto + Processor |
PATCH partiel — null = champ non envoyé (NotBlank accepte null) |
<Entité>ToggleDto + Processor |
PATCH /{id}/toggle (--toggle-boolean) |
<Entité><Champ>Dto + Processor |
PATCH /{id}/toggle-<champ> (--detach-boolean) |
<Entité>Upload<Champ>Dto + Processor |
PATCH /{id}/<champ> multipart, pour chaque #[Vich\UploadableField] |
<Entité>Provider |
GET /{id} avec chargement des relations ToOne (--with-provider) |
Filtres générés
Les filtres sont des paramètres #[QueryParameter] attachés à GetCollection (#[ApiFilter],
déprécié depuis API Platform 4.4, n'est jamais généré) :
| Champ Doctrine | Filtre généré | Requête |
|---|---|---|
string, ascii_string |
PartialSearchFilter (sous-chaîne, sans casse) |
?name=chai → %chai% |
| énumération, type personnalisé | ExactFilter |
?status=draft |
uuid / guid (hors id) |
UuidFilter |
?reference=<uuid> |
boolean |
ExactFilter (schéma booléen) |
?active=true |
integer, float, decimal |
ChainFilter (ExactFilter + ComparisonFilter) |
?price=25 · ?price[gte]=10 |
| date / heure | DateFilter |
?createdAt[after]=2026-01-01 |
| relation ToOne | IriFilter |
?author=/api/authors/{uuid} |
| relation ToMany, champ nullable | ExistsFilter |
?exists[deletedAt]=true |
| tri | SortFilter |
?order[name]=asc |
Exclus de tout filtre : id, text, json, array, simple_array, blob, binary, dateinterval,
ainsi que les champs listés dans filters.excluded_fields.
Une chaîne est recherchée par sous-chaîne (LOWER(champ) LIKE LOWER('%valeur%'), avec % et _
échappés). Une énumération (colonne string avec enumType) reste en égalité stricte, car ses valeurs
sont fermées. Un type personnalisé (phone_number…) aussi, car sa valeur en base est une conversion de
l'objet PHP. Sur une grande table, une recherche LIKE '%…%' ne peut pas utiliser d'index B-tree :
excluez au besoin le champ (filters.excluded_fields).
Configuration
Toutes les clés sont optionnelles ; les valeurs par défaut conviennent à une application Symfony
standard (App\ dans src/). Fichier config/packages/dev/sociolink_api_resource.yaml :
sociolink_api_resource: root_namespace : App # racine PSR-4 du projet source_dir : src # dossier des sources, relatif au projet entity_namespace : Entity # relatif à root_namespace → App\Entity dto_namespace : DTO # relatif à root_namespace → App\DTO state_namespace : State # relatif à root_namespace → App\State tests : namespace: App\Tests\Functional directory: tests/Functional admin_role : ROLE_ADMIN # rôle requis pour lever un soft-erase (itErased) system_fields : # champs exclus du CreateDto - details - status - createdAt # ... update_system_fields : # champs exclus de l'UpdateDto - details - createdAt # ... boolean_special_fields: # booléens à logique soft-delete / soft-erase - itDeleted - itErased custom_types : # types Doctrine personnalisés → classe PHP hydratée (phone_number est prédéfini) money: App\ValueObject\Money filters : excluded_fields : [ ] # champs exclus des filtres et du tri (ex. createdBy) sort_on_to_one_relations: false # autorise order[author]=asc templates_directory : null # gabarits Twig prioritaires (surcharge), relatif au projet ou absolu
Pour surcharger un gabarit, copiez-le depuis templates/ du bundle dans templates_directory : les
gabarits absents du dossier de surcharge restent ceux du bundle.
Types Doctrine personnalisés
Un type Doctrine standard est converti vers son type PHP (string, int, DateTimeImmutable, Uuid…).
Un type inconnu est traité comme une chaîne, et la commande le signale par un avertissement. Or si Doctrine
hydrate ce type en objet, un DTO typé string fait échouer le setter de l'entité (TypeError) :
déclarez alors la classe hydratée dans custom_types, et le DTO sera typé et importé en conséquence.
phone_number (odolbeau/phone-number-bundle) est
prédéfini : les DTOs typent le champ en libphonenumber\PhoneNumber. Le normaliseur de ce bundle
(activé dès que le Serializer de Symfony est installé) convertit la chaîne JSON reçue
("+33612345678") en objet, puis le numéro est ressérialisé au format E.164. Les tests générés utilisent
+33612345678 comme valeur d'exemple. Une entrée de custom_types peut redéfinir un type prédéfini.
Pour un autre type objet, le projet doit fournir un dénormaliseur (chaîne JSON → objet), faute de quoi API Platform rejette la requête.
Structure des fichiers générés
src/
├── DTO/
│ └── Article/
│ ├── ArticleCreateDto.php
│ └── ArticleUpdateDto.php
└── State/
└── Article/
├── ArticleCreateProcessor.php
└── ArticleUpdateProcessor.php
tests/
└── Functional/
└── Article/
└── ArticleApiTest.php (si --with-tests)
Qualité
composer install
composer check # PHPStan (niveau 6) puis PHPUnit
La suite comprend un test de bout en bout (tests/Functional) : le bundle est enregistré dans un
noyau Symfony avec un EntityManager Doctrine réel, la commande est exécutée dans chaque mode, puis
chaque classe générée est chargée et chaque #[ApiResource] injecté est instancié avec API
Platform. SOCIOLINK_E2E_KEEP=1 composer test conserve le projet généré pour inspection.
Les tests de tests/Recipe vérifient aussi que le fichier de configuration de la recette Flex reprend
exactement les valeurs par défaut, et que flex/ correspond à la recompilation de recipe/
(php recipe/build.php).
CI GitHub Actions (.github/workflows/ci.yml) : PHP 8.5 × Symfony 7.4/8.0/8.1 avec les dépendances
les plus récentes, plus une tâche avec les versions minimales de composer.json (--prefer-lowest).
Portage depuis la commande de projet
Ce bundle remplace la commande generate:resource qui vivait auparavant dans un projet applicatif (App\Command\GenerateResource). Changements de comportement à connaître lors du portage :
- toutes les conventions codées en dur (
App\,src/,ROLE_ADMIN, champs système) sont désormais dansGeneratorConfig, configurables viasociolink_api_resource.*; - la directive
mercuren'est plus injectée par défaut — utilisez--with-mercurepour retrouver l'ancien comportement (mercure: ['private' => true]) ; - les tests générés (
--with-tests) vivent danstests/Functional/et non plussrc/Tests/; - sans argument, un terminal affiche désormais un menu à choix unique au lieu de traiter toutes les entités ;
--reinitne supprime plus les dossiers racinesDTO/etState/, seulement ceux des entités traitées.
Depuis une version antérieure du bundle (BlackSheep\Symfony\ApiResourceBundle)
- espace de noms :
SocioLink\ApiResourceBundle(à mettre à jour dansconfig/bundles.php) ; - racine de configuration :
sociolink_api_resource(au lieu deblack_sheep_api_resource) ; - alias de commande :
sociolink:api-resource:generate(au lieu deblack-sheep:api-resource:generate).
Licence
Ce projet est distribué sous licence propriétaire (voir LICENSE). Le dépôt est public afin de permettre sa distribution via Packagist, mais aucun droit d'utilisation, de modification ou de redistribution n'est accordé en dehors des projets explicitement autorisés par l'auteur.
Auteur
Xavier Kongolo