Laravel

Un framework qui rend heureux

Voir cette catégorie
Vers le bas
Cours Laravel 11 – les données – les ressources d'API
Dimanche 17 mars 2024 18:08

Laravel permet de bâtir facilement des API à partir de ressource Eloquent. Voyons cela dans cet article.

Les API REST

Généralités

REST (Representational State Transfer) est un style architectural pour la conception de API (Application Programming Interfaces) basé sur des concepts et des constraints bien définis. Dans le contexte de Laravel, une API REST se caractérise par les points suivants:
  1. Organisée autour de ressources : les API REST sont structurées autour de ressources, qui peuvent être des entités de données telles que des utilisateurs, des articles, des commandes, etc. Ces ressources sont accessibles via des URL uniques et bien définies.
  2. Sans persistance (sans session) : dans une API REST, chaque requête doit contenir toutes les informations nécessaires pour être traitée. Les serveurs ne doivent pas stocker le contexte entre les requêtes, ce qui implique que les API REST sont généralement sans session (stateless).
  3. Orientée client-serveur : les API REST reposent sur un modèle client-serveur, où les clients envoient des requêtes au serveur et reçoivent des réponses. Le serveur ne peut pas initier une interaction avec le client, mais peut fournir des liens hypertextes aux ressources pour guider le client.
  4. Cacheable : lne API REST doit pouvoir être mise en cache, ce qui signifie qu'une requête identique devrait toujours retourner la même réponse, sauf si la ressource sous-jacente a été modifiée entre-temps. Ceci facilite le cache des données et réduit le nombre de requêtes nécessaires.
  5. Format de données indépendant : les API REST peuvent retourner différents types de données, bien que le JSON (JavaScript Object Notation) soit communément utilisé en raison de sa légèreté, de sa facilité de lecture et de son support par de nombreux langages de programmation.
  6. Utilisation des méthodes HTTP : les API REST utilisent les méthodes HTTP (GET, POST, PUT, DELETE, PATCH, etc.) pour effectuer des opérations sur des ressources. Chaque méthode a une signification spécifique, par exemple, GET pour récupérer une ressource, POST pour créer une ressource, PUT/PATCH pour mettre à jour une ressource, et DELETE pour supprimer une ressource.
En résumé, Laravel permet de créer des API REST qui sont structurées autour de ressources, stateless, cacheables, et orientées client-serveur. Les API REST peuvent retourner différents types de données, mais le JSON est couramment utilisé. Enfin, les API REST utilisent les méthodes HTTP pour manipuler des ressources, en suivant des conventions bien définies.

Les endpoints

Un endpoint (point de terminaison ou point d'accès) est une URL unique qui représente une ressource spécifique ou une collection de ressources avec laquelle un client peut interagir en utilisant des méthodes HTTP.
Un endpoint est l'adresse ou le chemin d'accès auquel un client envoie une requête pour effectuer une opération sur une ressource donnée. Par exemple, dans une API REST pour une application de gestion de blogs, des endpoints pourraient être:
  1. /api/blogs : cet endpoint représente une collection de tous les blogs. Un client pourrait utiliser une requête GET pour récupérer la liste de tous les blogs.
  2. /api/blogs/{id} : cet endpoint représente un blog spécifique identifié par son identifiant unique ({id}). Un client pourrait utiliser une requête GET pour récupérer les informations sur un blog spécifique, un POST pour créer un nouveau blog, un PUT/PATCH pour mettre à jour un blog existant ou un DELETE pour supprimer un blog.
Chaque endpoint est associé à une ou plusieurs méthodes HTTP, telles que GET, POST, PUT, PATCH, DELETE, etc., qui définissent les opérations permises sur la ressource. En général, les endpoints REST suivent des conventions et des normes bien établies pour la structure des URL, l'utilisation des méthodes HTTP et les formats de données, ce qui facilite l'interopérabilité entre différentes applications et systèmes.
Dans Laravel, les endpoints sont définis dans les routes de l'application. Vous pouvez utiliser le fichier web.php ou api.php pour définir des routes qui pointent vers des contrôleurs ou des actions spécifiques, et ces routes sont les endpoints de votre API REST.
Pour continuer avec notre application de films, on peut imaginer ce endpoint pour les informations de tous les films :
GET /api/films
[
   {
      id: 1,
      title: 'Aut numquam.'
   },
   {
      id: 2,
      title: 'Sint incidunt consequatur.'
   }
   ...
]
Ou ce endpoint pour les informations concernant un film en particulier :
GET /api/films/2
{
   id: 2,
   name: 'Sint incidunt consequatur.'
}
On peut imaginer d'autres endpoints pour modifier, ajouter, supprimer... Il ne reste plus qu'à voir comment on réalise ça avec Laravel mais vous en avez désormais une idée déjà assez précise avec ce qu'on a déjà vu dans les précédents articles.

Le contrôleur

On va créer le contrôleur :
php artisan make:controller Api/FilmController --api
On le retrouve rangé dans le dossier app/Http/Controllers/Api créé par la même occasion :

Avec évidemment le même code que celui qu'on a eu quand on avait déjà créé une ressource (et Laravel se débrouille tout seul pour les espaces de nom) mais sans les méthodes create et edit qui n'ont pas lieu d'être pour une API :

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class FilmController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     */
    public function show(string $id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, string $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(string $id)
    {
        //
    }
}
Et on va coder les méthodes :
<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Database\Eloquent\Collection;
use App\Models\Film;

class FilmController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index(): Collection
    {
        return Film::all();
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request): void
    {
        Film::create($request->all());
    }

    /**
     * Display the specified resource.
     * 
     * @param  \App\Film $film
     */
    public function show(Film $film): Film
    {
        return $film;
    }

    /**
     * Update the specified resource in storage.
     * 
     * @param  \App\Film $film
     */
    public function update(Request $request, Film $film): Response
    {
        $film->update($request->all());
    }

    /**
     * Remove the specified resource from storage.
     * 
     * @param  \App\Film $film
     */
    public function destroy(Film $film): void
    {
        $film->delete();
    }
}

Évidemment, ça ressemble énormément à ce qu'on a fait précédemment et c'est normal puisqu'il s'agit du même genre de traitement sur les mêmes données.

Les routes

Laravel 11 ne possède pas par défaut un fichier pour les routes des API, alors on va le créer :
php artisan install:api
Pour les routes, on adopte aussi une ressource dans le fichier routes/api.php :
use App\Http\Controllers\Api\FilmController;

Route::apiResource('films', FilmController::class);
Ce qui donne ces routes :

Si vous ajoutez ces routes à l'application existante que nous avons construite dans les précédents articles vous allez avoir une redondance des noms des routes et certaines fonctionnalités ne vont plus marcher puisqu'on a utilisé justement ces noms de routes pour les boutons d'action. Mais je m'intéresse uniquement à l'API dans cet article.

Le fait de déclarer ces routes dans le fichier routes/api.php a pour effet de leur appliquer le middleware api au lieu de web. Comme on est sans état, plus de cookie, de session, de protection CSRF...

Le fonctionnement

Maintenant avec l'url ...api/films, on obtient une réponse JSON (Laravel convertit automatiquement la réponse en JSON sans qu'on lui demande) :

On obtient toutes les colonnes non cachées, c'est-à-dire celles qui ne sont pas définies dans la propriété $hidden du modèle. Et comme on n'a pas créé cette propriété, on récupère toutes les colonnes. Alors, on va juste renvoyer titre, année et description en ajoutant la propriété $hidden dans le modèle Film :

protected $hidden = ['id', 'created_at', 'updated_at', 'deleted_at'];

On dispose aussi de la propriété $visible qui est exactement l'inverse, on précise alors seulement les colonnes qu'on veut voir.

Avec l'url .../api/films/2 on obtient :

On va récupérer également les catégories et les acteurs mais pas avec toutes leurs colonnes ! On va ajouter cette ligne pour les deux modèles (Actor et Category) :

protected $visible = ['name'];
Et on modifie le contrôleur :
public function index(): Collection
{
    return Film::with('categories', 'actors')->get();
}
Maintenant, on obtient ce résultat : On pourrait aussi imaginer utiliser une pagination pour cette API. Il suffit de changer la méthode index :
use Illuminate\Pagination\LengthAwarePaginator;

...

public function index(): LengthAwarePaginator
{
    return Film::with('categories', 'actors')->paginate(4);
}
On peut alors demander la page qu'on veut, par exemple la seconde avec l'url ...api/films?page=2 : On ne reçoit que les informations de la page 2 et en plus des renseignements concernant la pagination. On peut imaginer aussi filtrer, ordonner...

Je ne traite pas dans ce chapitre de l'authentification spécifique aux API. Je vous invite à regarder par exemple la documentation de Passport sur le sujet.

Les ressources d'API

On a vu ci-dessus comme il est facile de retourner une réponse JSON à partir des données d'un modèle. C'est parfait tant qu'on veut une simple sérialisation des valeurs des colonnes. Mais parfois, on désire effectuer quelques traitements intermédiaires (ça correspond au design pattern transformer). Imaginons que nous souhaitions retourner seulement les 10 premiers mots de la description...

On utilise Artisan pour créer la ressource :
php artisan make:resource Film
On se retrouve avec la ressource ici : Avec ce code :
<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class Film extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return parent::toArray($request);
    }
}

Là, on se contente de tout sérialiser, ça ne va donc pas changer grand-chose à ce qu'on avait précédemment. Dans le contrôleur, on utilise la ressource, par exemple pour la méthode show :

use App\Http\Resources\Film as FilmResource;

...

public function show(Film $film): FilmResource
{
    return new FilmResource($film);
}

Maintenant pour l'url .../api/films/2 on obtient le même résultat qu'auparavant. Maintenant changeons la ressource :

public function toArray($request): array
{
    return [
        'title' => $this->title,
        'year' => $this->year,
        'description' => str()->words($this->description, 10),
    ];
}
Maintenant pour l'url .../api/films/2 on obtient :

On voit qu'on n'a plus que les 10 premiers mots de la description. On a bien effectué un traitement intermédiaire sur les valeurs retournées. Mais on remarque aussi qu'on ne récupère plus les catégories et les acteurs ! On a juste ce qu'on a demandé. Mais personne ne nous empêche d'en demander plus :

public function toArray($request)
{
    return [
        'title' => $this->title,
        'year' => $this->year,
        'description' => str()->words($this->description, 10),
        'categories' => $this->categories,
        'actors' => $this->actors,
    ];
}

Et désormais, on récupère bien les éléments en relation.

Il y aurait encore beaucoup à dire sur les ressources et les API, mais vous avez dorénavant une bonne base de départ. La documentation complète est ici.

En résumé

  • Laravel permet de créer facilement une API et de permettre des transformations avec des ressources.


Par bestmomo

Aucun commentaire