Qu’est-ce qu’une API Resource
Lorsque vous construisez une API, renvoyer un modèle Eloquent directement en JSON risque d’exposer des colonnes que vous vouliez cacher, ou d’envoyer plus de données que le client n’en a besoin.
Une Eloquent API Resource ajoute une couche de transformation entre le modèle et la réponse JSON. Sa méthode toArray() définit explicitement ce qui est inclus et sous quelle forme.
Principaux avantages :
Contrôle total des champs renvoyés
Renommage/traitement des champs centralisé
Champs conditionnels
Relations imbriquées cohérentes
Créer une Resource
Utilisez make:resource.
php artisan make:resource UserResource
Le fichier est créé dans app/Http/Resources.
<? php
namespace App\Http\Resources ;
use Illuminate\Http\ Request ;
use Illuminate\Http\Resources\Json\ JsonResource ;
class UserResource extends JsonResource
{
public function toArray ( Request $request ) : array
{
return [
'id' => $this -> id ,
'name' => $this -> name ,
'email' => $this -> email ,
'created_at' => $this -> created_at ,
'updated_at' => $this -> updated_at ,
];
}
}
$this accède aux propriétés du modèle : la Resource sert de proxy.
Utilisation dans un contrôleur
Retournez simplement la resource depuis une route ou un contrôleur.
use App\Http\Resources\ UserResource ;
use App\Models\ User ;
Route :: get ( '/users/{user}' , function ( User $user ) {
return new UserResource ( $user );
});
Ou avec la méthode toResource() du modèle :
Route :: get ( '/users/{user}' , function ( User $user ) {
return $user -> toResource ();
});
toResource() recherche automatiquement la classe correspondante (UserResource).
Par défaut, la réponse est enveloppée sous la clé data.
{
"data" : {
"id" : 1 ,
"name" : "Taro Yamada" ,
"email" : "[email protected] " ,
"created_at" : "2024-01-15T10:00:00.000000Z" ,
"updated_at" : "2024-01-15T10:00:00.000000Z"
}
}
Collections de resources
Pour plusieurs modèles, utilisez collection().
use App\Http\Resources\ UserResource ;
use App\Models\ User ;
Route :: get ( '/users' , function () {
return UserResource :: collection ( User :: all ());
});
Ou via toResourceCollection() d’une collection Eloquent :
return User :: all () -> toResourceCollection ();
Collection de resource personnalisée
Pour ajouter des métadonnées à la collection, créez une classe dédiée.
php artisan make:resource UserCollection
<? php
namespace App\Http\Resources ;
use Illuminate\Http\ Request ;
use Illuminate\Http\Resources\Json\ ResourceCollection ;
class UserCollection extends ResourceCollection
{
public function toArray ( Request $request ) : array
{
return [
'data' => $this -> collection ,
'links' => [
'self' => route ( 'users.index' ),
],
];
}
}
Route :: get ( '/users' , function () {
return new UserCollection ( User :: all ());
});
Renommez ou traitez les valeurs dans toArray().
public function toArray ( Request $request ) : array
{
return [
'id' => $this -> id ,
'full_name' => $this -> name , // Renommage
'email_address' => $this -> email , // Renommage
'role' => strtoupper ( $this -> role ), // Traitement
'registered_at' => $this -> created_at -> toDateString (), // Formatage
];
}
Champs conditionnels
when() — Ajout conditionnel
Inclure un champ uniquement si une condition est remplie.
public function toArray ( Request $request ) : array
{
return [
'id' => $this -> id ,
'name' => $this -> name ,
'email' => $this -> email ,
// Uniquement si l'utilisateur authentifié est admin
'secret_token' => $this -> when (
$request -> user () ?-> isAdmin (),
$this -> secret_token
),
];
}
Si la condition est fausse, la clé est retirée de la réponse.
mergeWhen() — Ajout conditionnel groupé
Ajoutez plusieurs champs conditionnellement d’un coup.
public function toArray ( Request $request ) : array
{
return [
'id' => $this -> id ,
'name' => $this -> name ,
$this -> mergeWhen ( $request -> user () ?-> isAdmin (), [
'admin_note' => $this -> admin_note ,
'internal_id' => $this -> internal_id ,
]),
];
}
whenLoaded() — Uniquement si chargé
Incluez la relation seulement si elle est déjà eager-loaded : prévient N+1 tout en gardant des réponses flexibles.
use App\Http\Resources\ PostResource ;
public function toArray ( Request $request ) : array
{
return [
'id' => $this -> id ,
'name' => $this -> name ,
'email' => $this -> email ,
// Uniquement si posts est chargé via with()
'posts' => PostResource :: collection ( $this -> whenLoaded ( 'posts' )),
];
}
Contrôlez le chargement côté contrôleur.
// Avec les posts
return new UserResource ( $user -> load ( 'posts' ));
// Sans les posts
return new UserResource ( $user );
whenCounted() — Compter conditionnellement
Inclut le comptage d’une relation chargé via loadCount().
public function toArray ( Request $request ) : array
{
return [
'id' => $this -> id ,
'name' => $this -> name ,
'posts_count' => $this -> whenCounted ( 'posts' ),
];
}
return new UserResource ( $user -> loadCount ( 'posts' ));
Resources imbriquées
Imbriquez d’autres resources pour une structure cohérente.
// app/Http/Resources/PostResource.php
class PostResource extends JsonResource
{
public function toArray ( Request $request ) : array
{
return [
'id' => $this -> id ,
'title' => $this -> title ,
'body' => $this -> body ,
'author' => new UserResource ( $this -> whenLoaded ( 'user' )),
'comments' => CommentResource :: collection ( $this -> whenLoaded ( 'comments' )),
'published_at' => $this -> published_at ?-> toDateString (),
];
}
}
// Contrôleur
$post = Post :: with ([ 'user' , 'comments' ]) -> findOrFail ( $id );
return new PostResource ( $post );
{
"data" : {
"id" : 1 ,
"title" : "Apprendre les API Resources avec Laravel" ,
"author" : {
"id" : 5 ,
"name" : "Taro Yamada" ,
"email" : "[email protected] "
},
"comments" : [
{ "id" : 10 , "body" : "Très utile !" }
]
}
}
Ajouter des métadonnées
with() — Métadonnées au niveau supérieur
Surchargez with() pour ajouter des données au top-level d’une collection.
class UserCollection extends ResourceCollection
{
public function toArray ( Request $request ) : array
{
return parent :: toArray ( $request );
}
public function with ( Request $request ) : array
{
return [
'meta' => [
'version' => '1.0' ,
'generated_at' => now () -> toIso8601String (),
],
];
}
}
Exemple :
{
"data" : [ ... ],
"meta" : {
"version" : "1.0" ,
"generated_at" : "2024-01-15T10:00:00+00:00"
}
}
additional() — Métadonnées dynamiques
Depuis le contrôleur, ajoutez des métadonnées dynamiques.
return User :: all ()
-> load ( 'roles' )
-> toResourceCollection ()
-> additional ([ 'meta' => [
'total_admins' => User :: where ( 'role' , 'admin' ) -> count (),
]]);
Passer un résultat paginé à une resource ajoute automatiquement meta et links.
Route :: get ( '/users' , function () {
return UserResource :: collection ( User :: paginate ( 15 ));
});
Ou :
return User :: paginate ( 15 ) -> toResourceCollection ();
Exemple :
{
"data" : [
{ "id" : 1 , "name" : "Taro Yamada" },
{ "id" : 2 , "name" : "Hanako Suzuki" }
],
"links" : {
"first" : "https://example.com/users?page=1" ,
"last" : "https://example.com/users?page=5" ,
"prev" : null ,
"next" : "https://example.com/users?page=2"
},
"meta" : {
"current_page" : 1 ,
"from" : 1 ,
"last_page" : 5 ,
"per_page" : 15 ,
"to" : 15 ,
"total" : 72
}
}
Même avec withoutWrapping(), la pagination conserve toujours la clé data pour coexister avec meta / links.
Désactiver le wrapping
Par défaut, la resource externe est enveloppée dans data. Désactivez-le via AppServiceProvider::boot().
use Illuminate\Http\Resources\Json\ JsonResource ;
public function boot () : void
{
JsonResource :: withoutWrapping ();
}
withoutWrapping() ne concerne que l’enveloppement externe. Les clés data définies manuellement ne sont pas retirées.
Exemple : API utilisateur
Application typique : liste et détail utilisateur avec réponses homogènes.
UserResource
<? php
namespace App\Http\Resources ;
use Illuminate\Http\ Request ;
use Illuminate\Http\Resources\Json\ JsonResource ;
class UserResource extends JsonResource
{
public function toArray ( Request $request ) : array
{
return [
'id' => $this -> id ,
'name' => $this -> name ,
'email' => $this -> email ,
'avatar_url' => $this -> avatar_url ,
'role' => $this -> role ,
// Visible pour l'admin uniquement
'created_at' => $this -> when (
$request -> user () ?-> isAdmin (),
$this -> created_at -> toDateString ()
),
// Seulement si chargé
'posts' => PostResource :: collection ( $this -> whenLoaded ( 'posts' )),
'posts_count' => $this -> whenCounted ( 'posts' ),
];
}
}
UserController
<? php
namespace App\Http\Controllers\Api ;
use App\Http\Resources\ UserResource ;
use App\Models\ User ;
use Illuminate\Http\ Request ;
use Illuminate\Http\Resources\Json\ AnonymousResourceCollection ;
class UserController extends Controller
{
public function index () : AnonymousResourceCollection
{
$users = User :: withCount ( 'posts' ) -> paginate ( 20 );
return UserResource :: collection ( $users );
}
public function show ( User $user ) : UserResource
{
$user -> loadCount ( 'posts' ) -> load ( 'posts' );
return new UserResource ( $user );
}
}
Pages associées
Introduction aux relations Eloquent Définitions de relations et eager loading.
Pagination Combiner la pagination et les API Resources.