> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Eloquent API Resources

> Comment convertir des modèles Eloquent en réponses JSON cohérentes : création de classes de resource, champs conditionnels et pagination.

## 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.

```mermaid theme={null}
flowchart LR
  A["Modèle Eloquent<br>(User)"] --> B["Classe Resource<br>(UserResource)"]
  B --> C["Réponse JSON<br>{ id, name, email }"]
```

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`.

```shell theme={null}
php artisan make:resource UserResource
```

Le fichier est créé dans `app/Http/Resources`.

```php theme={null}
<?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.

```php theme={null}
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 :

```php theme={null}
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`.

```json theme={null}
{
  "data": {
    "id": 1,
    "name": "Taro Yamada",
    "email": "yamada@example.com",
    "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()`.

```php theme={null}
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 :

```php theme={null}
return User::all()->toResourceCollection();
```

### Collection de resource personnalisée

Pour ajouter des métadonnées à la collection, créez une classe dédiée.

```shell theme={null}
php artisan make:resource UserCollection
```

```php theme={null}
<?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'),
            ],
        ];
    }
}
```

```php theme={null}
Route::get('/users', function () {
    return new UserCollection(User::all());
});
```

## Transformation des champs

Renommez ou traitez les valeurs dans `toArray()`.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
// 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()`.

```php theme={null}
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'posts_count' => $this->whenCounted('posts'),
    ];
}
```

```php theme={null}
return new UserResource($user->loadCount('posts'));
```

## Resources imbriquées

Imbriquez d'autres resources pour une structure cohérente.

```php theme={null}
// 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(),
        ];
    }
}
```

```php theme={null}
// Contrôleur
$post = Post::with(['user', 'comments'])->findOrFail($id);

return new PostResource($post);
```

```json theme={null}
{
  "data": {
    "id": 1,
    "title": "Apprendre les API Resources avec Laravel",
    "author": {
      "id": 5,
      "name": "Taro Yamada",
      "email": "yamada@example.com"
    },
    "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.

```php theme={null}
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 :

```json theme={null}
{
  "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.

```php theme={null}
return User::all()
    ->load('roles')
    ->toResourceCollection()
    ->additional(['meta' => [
        'total_admins' => User::where('role', 'admin')->count(),
    ]]);
```

## Combinaison avec la pagination

Passer un résultat paginé à une resource ajoute automatiquement `meta` et `links`.

```php theme={null}
Route::get('/users', function () {
    return UserResource::collection(User::paginate(15));
});
```

Ou :

```php theme={null}
return User::paginate(15)->toResourceCollection();
```

Exemple :

```json theme={null}
{
  "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
  }
}
```

<Info>
  Même avec `withoutWrapping()`, la pagination conserve toujours la clé `data` pour coexister avec `meta` / `links`.
</Info>

## Désactiver le wrapping

Par défaut, la resource externe est enveloppée dans `data`. Désactivez-le via `AppServiceProvider::boot()`.

```php theme={null}
use Illuminate\Http\Resources\Json\JsonResource;

public function boot(): void
{
    JsonResource::withoutWrapping();
}
```

<Warning>
  `withoutWrapping()` ne concerne que l'enveloppement externe. Les clés `data` définies manuellement ne sont pas retirées.
</Warning>

## Exemple : API utilisateur

Application typique : liste et détail utilisateur avec réponses homogènes.

```mermaid theme={null}
flowchart TD
  A["GET /api/users"] --> B["UserController@index"]
  C["GET /api/users/:id"] --> D["UserController@show"]
  B --> E["UserResource::collection(paginate)"]
  D --> F["new UserResource(user)"]
  E --> G["{ data: [...], meta, links }"]
  F --> H["{ data: { id, name, email, ... } }"]
```

### UserResource

```php theme={null}
<?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 theme={null}
<?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

<Card title="Introduction aux relations Eloquent" icon="link" href="/fr/eloquent-relationships">
  Définitions de relations et eager loading.
</Card>

<Card title="Pagination" icon="list" href="/fr/pagination">
  Combiner la pagination et les API Resources.
</Card>


## Related topics

- [Sérialisation Eloquent](/fr/eloquent-serialization.md)
- [Accesseurs, mutateurs et casts Eloquent](/fr/eloquent-mutators.md)
- [Frontend](/fr/frontend.md)
- [Pagination](/fr/pagination.md)
- [Attributs PHP](/fr/advanced/php-attributes.md)
