Laravel: les ressources

Posté le 12 mai 2020 · 8 minutes de lecture

C’est très orienté débutant, on va voir à l’avenir si la difficulté de ces petits articles augmentent ou s’ils rencontrent un petit succès, j’en ferai probablement davantage.


Depuis que j’ai repris PHP, plus spécifiquement le framework Laravel, il y a un petit truc qui me fait très, très plaisir: les resources.

Mise en contexte

Déjà, pourquoi ? Auparavant, je travaillais beaucoup avec Node.js, Express.js et Sequelize. Toujours du côté back, et… c’était relativement fastidieux de lui dire à chaque fois « De tous les Threads, tu me prends aussi les créateur·rice·s avec certains attributs » comme ceci:

1
2
3
4
5
6
7
8
9
10
11
12
app.get("/api/threads/", async (req, res) => {
  const threads = await Thread.findAll({
    include: [
      {
        model: User, // author,
        attributes: ["id", "username", "avatar"], // ...
      },
    ],
  });

  return res.json(threads);
});

Bien sûr, on peut déclarer attributes ailleurs, comme par exemples modelAttributes.js qui exporterait un ou plusieurs tableaux selon ce qu’on veut récupérer.

Et Laravel là dedans ? C’est comme Node aussi ? 😱 Que nenni ! Avec Laravel, on a des classes qui héritent de JsonResource: les resources (en anglais). Tu ne feras point tout à la main et tu respecteras SOLID autant que possible: le rôle d’un contrôleur c’est de retourner un résultat, pas traiter le résultat.

Le modèle

Puisque j’aime montrer les choses petit à petit, on va commencer par le modèle. Nous n’allons en faire qu’un sans montrer les relations de celui-ci.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Thread extends Model
{
    /**
     * @var array
     */
    protected $fillable = ['name', 'slug', 'user_id'];

    /**
     * @return BelongsTo
     */
    public function author(): BelongsTo
    {
        return $this->belongtTo(User::class, 'user_id', 'id');
    }

    /**
     * @var HasMany
     */
    public function messages(): HasMany
    {
        return $this->hasMany(Message::class);
    }
}

On a deux types de relations ici: one to many pour les messages et many to one pour l’auteur·e.

Quand on fait une requête pour obtenir tout les threads… ou utilisez la pagination.

1
$threads = Thread::all(); // Ou Thread::paginate();

Et vu que les relations sont tout autant logiques (dans Laravel) que physiques (en base de données), on peut faire ceci:

1
2
3
foreach ($threads as $thread) {
    dump($thread->messages, $thread->author);
}

On a ce qu’il faut pour renvoyer une information et vous montrer comment que c’est beau.

Maintenant, on doit juste renvoyer le résultat. On a deux manières pour procéder, on peut simplement faire un return response()->json(Thread::all());, ce qui n’est pas fameux ou utiliser une ressource. Voyons voir comment on fait ça.

Commençons par un:

1
php artisan make:resource ThreadResource

Ensuite on peut aller dans app/Http/resources/ThreadResource.php.

Par défaut, il renvoie simplement le parent. Ce qui veut dire qu’il va tout envoyer tout ce qui est lisible, donc ce qui n’est pas déclaré dans l’attribut $hidden des modèles. Nous allons le modifier un peu pour préciser les choses nous-même et avoir plus de contrôle là-dessus.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ThreadResource extends JsonResource
{
    /**
     * Transform the resource into an array
     *
     * @param Request $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'slug' => $this->slug,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
            'author' => $this->author,
            'messages' => MessageResource::collection($this->messages),
        ];
    }
}

L’avantage de cette méthode, c’est qu’on choisit quoi envoyer: y compris les relations. Rien n’empêche d’avoir un ThreadResource qui renvoie tous les champs avec ses relations et ThreadLiteResource qui ne renvoie que les attributs du modèle; ce qui permet d’éviter la boucle infinie et donc le timeout de PHP.

Et dans la méthode du contrôleur:

1
2
3
4
5
6
7
8
/**
* @return AnonymousResourceCollection
*/
public function index(): AnonymousResourceCollection
{
    $threads = Thread::all();
    return ThreadResource::collection($threads);
}

Quand on veut retourner une seule instance de Thread, il ne faut pas utiliser la méthode statique collection mais créer une instance de ThreadResource.

1
2
3
4
5
6
7
8
/**
 * @param Thread $thread
 * @return ThreadResource
 */
public function show(Thread $thread): ThreadResource
{
    return (new ThreadResource($thread));
}

Bien sûr, ce n’est pas complet. Mon souhait ici est juste de montrer comment utiliser les Resources de Laravel et faire du code maintenable. Si tu veux lire davantage de choses sur les ressources, n’hésite pas à consulter la documentation officielle (en anglais).

Et voilà. Ni plus, ni moins. C’est rapide, simple et efficace. Ça allège beaucoup les contrôleurs: formater le résultat n’est pas leur travail. L’objectif est d’utiliser le principe de Single Responsability de SOLID et de déléguer le code au maximum afin de le rendre lisible et maintenable. 😊

Si tu as une question sur un principe de Laravel ou quelque chose que tu ne comprends pas, hésite pas à me le dire dans les commentaires, je pourrais éclaircir certaines choses si j’en ai les connaissances. 😉


Source de l’en-tête: Unsplash