doganddev
Accueil Blog Boutique

Laravel Query Gate : APIs REST déclaratives (filtres, tris, actions, versioning)

DOG&DEV · 25/01/2025

Réseaux Optimisation Système
Laravel Query Gate : APIs REST déclaratives (filtres, tris, actions, versioning)

Laravel Query Gate : APIs REST déclaratives (filtres, tris, actions, versioning)

Laravel Query Gate (behindsolution/laravel-query-gate) permet de générer des APIs REST (liste, show, create, update, delete) en les déclarant sur le modèle : filtres, tris, sélection de colonnes, actions personnalisées (publier, archiver, etc.) et versioning. Fini les contrôleurs répétitifs pour chaque ressource ; la doc OpenAPI peut être générée automatiquement.

Prérequis

  • Laravel 10+ (ou 11/12)
  • Modèles Eloquent avec relations si besoin de filtres imbriqués

1. Installation

composer require behindsolution/laravel-query-gate

Publier la config et enregistrer les modèles :

// config/query-gate.php
'models' => [
    App\Models\Post::class,
    App\Models\Comment::class,
],

2. Déclarer une Query Gate sur le modèle

Ajoutez le trait HasQueryGate et la méthode queryGate() :

use BehindSolution\LaravelQueryGate\Traits\HasQueryGate;
use BehindSolution\LaravelQueryGate\Support\QueryGate;

class Post extends Model
{
    use HasQueryGate;

    public static function queryGate(): QueryGate
    {
        return QueryGate::make()
            ->alias('posts')
            ->middleware(['auth:sanctum'])
            ->filters([
                'status' => ['string', 'in:draft,published,archived'],
                'category_id' => 'integer',
                'author.name' => ['string', 'max:100'],
                'created_at' => 'date',
            ])
            ->allowedFilters([
                'status' => ['eq', 'in', 'neq'],
                'category_id' => ['eq', 'in'],
                'author.name' => ['like'],
                'created_at' => ['gte', 'lte', 'between'],
            ])
            ->select(['id', 'title', 'slug', 'status', 'created_at', 'author.name'])
            ->sorts(['created_at', 'title'])
            ->actions(fn ($actions) => $actions
                ->create(fn ($a) => $a->validations([
                    'title' => ['required', 'string', 'max:255'],
                    'content' => ['required', 'string'],
                ]))
                ->update()
                ->delete()
            );
    }
}

Aucun contrôleur ni routes manuels pour le CRUD de base.

3. Routes générées

Pour l’alias posts :

  • GET /query/posts — Liste (filtres, tri, pagination)
  • GET /query/posts/{id} — Détail
  • POST /query/posts — Création
  • PATCH /query/posts/{id} — Mise à jour
  • DELETE /query/posts/{id} — Suppression

Le préfixe (/query) et le middleware sont configurables.

4. Filtres et notation pointée

Exemples de requêtes :

GET /query/posts?filter[status][eq]=published
GET /query/posts?filter[category_id][in]=1,2,3
GET /query/posts?filter[author.name][like]=John
GET /query/posts?filter[created_at][between]=2024-01-01,2024-12-31
GET /query/posts?filter[views_count][gte]=1000&sort=views_count:desc

Opérateurs typiques : eq, neq, in, like, gte, lte, between, etc.

5. Actions personnalisées

En plus de create, update, delete, vous pouvez définir des actions métier. Ex. PublishPost :

// app/Actions/QueryGate/Posts/PublishPost.php
class PublishPost extends AbstractQueryGateAction
{
    public function action(): string { return 'publish'; }

    public function handle($request, $model, array $payload)
    {
        $model->update(['status' => 'published', 'published_at' => now()]);
        return ['message' => 'Post publié !', 'post' => $model];
    }

    public function authorize($request, $model): ?bool
    {
        return $request->user()->can('update', $model);
    }
}

Route générée : POST /query/posts/{id}/publish.
L’exemple du dépôt propose aussi : unpublish, archive, feature, duplicate, approve/reject/spam pour les commentaires.

6. Versioning d’API

Adapter filtres et select selon une version (header X-Query-Version) :

QueryGate::make()
    ->version('2024-01-01', fn (QueryGate $gate) => $gate
        ->filters(['title' => 'string', 'status' => 'string'])
        ->select(['id', 'title', 'status']))
    ->version('2025-01-01', fn (QueryGate $gate) => $gate
        ->filters(['title' => 'string', 'status' => 'string', 'views_count' => 'integer', 'author.name' => 'string'])
        ->select(['id', 'title', 'status', 'views_count', 'author.name']));

Changelog : GET /query/posts/__changelog.

7. Documentation OpenAPI

  • GET /query/docs — Interface interactive (Swagger UI ou équivalent)
  • GET /query/docs.json — Spécification OpenAPI

La doc est dérivée des filtres, validations et routes déclarées.

Dépannage

Symptôme Cause possible Correctif
404 sur /query/posts Modèle non enregistré dans config/query-gate.php Ajouter le modèle dans models et vider le cache de config
Filtre ignoré ou erreur Nom de colonne/relation ou opérateur non autorisé Vérifier filters et allowedFilters ; les noms doivent correspondre (y compris author.name)
Action non trouvée Classe d’action non enregistrée ou action() mal nommé Suivre la convention de nommage et d’enregistrement des actions du package

Bonnes pratiques

  • Restreindre allowedFilters et select pour limiter la surface d’attaque et les fuites de données.
  • Utiliser middleware (auth, throttle) et authorize dans les actions personnalisées.
  • Versioning dès qu’une API est consommée par des clients externes.

Ressources


Cet article s’inscrit dans notre série de guides technique et développement web. Pour un serveur ou une application sur-mesure, contact.

Commentaires (0)

Laisser un commentaire