Aller au contenu principal

Module 3 – Routes, contrôleurs et pages

Niveau 5.2 – Laravel Inertia (React & Vue)


Objectif

À la fin de ce module vous saurez :

  • Utiliser Inertia::render() dans les contrôleurs pour envoyer une « page » et des props au client.
  • Recevoir et utiliser ces props dans les composants React et Vue (typage, structure).
  • Organiser les fichiers de pages (dossiers, nommage) pour correspondre aux noms passés à Inertia::render().
  • Comprendre le cycle : URL → route → contrôleur → réponse Inertia → composant rendu.

Nous donnons des exemples complets Laravel + React et Laravel + Vue pour une même fonctionnalité (liste d’utilisateurs + détail).


Rappel : qui décide quelle page afficher ?

En Inertia, Laravel décide quelle page (composant) afficher. Chaque URL est gérée par une route Laravel. Le contrôleur exécute la logique (requêtes BDD, validation, etc.) puis appelle Inertia::render('NomDuComposant', [ ... props ... ]). Le nom du composant est le chemin du fichier sous resources/js/Pages/, sans extension.

Exemples de correspondance :

Appel LaravelFichier côté client (React)Fichier côté client (Vue)
Inertia::render('Dashboard')Pages/Dashboard.jsxPages/Dashboard.vue
Inertia::render('Users/Index')Pages/Users/Index.jsxPages/Users/Index.vue
Inertia::render('Articles/Show')Pages/Articles/Show.jsxPages/Articles/Show.vue

Les props que vous passez en deuxième argument deviennent les props du composant React ou Vue. Tout ce qui est sérialisable en JSON (tableaux, objets, chaînes, nombres, booléens, null) peut être passé. Les modèles Eloquent sont automatiquement convertis en tableaux (ou en objets JSON) par Laravel.


Inertia::render() – signature et bonnes pratiques

Signature typique :

Inertia::render(string $component, array $props = [])
  • $component : nom de la page (chemin relatif à Pages/, sans extension). Utilisez des sous-dossiers pour grouper : Users/Index, Users/Create, Users/Edit.
  • $props : tableau associatif. Chaque clé devient le nom de la prop côté client.

Bonnes pratiques :

  • Passer uniquement les données nécessaires à la page (éviter de tout envoyer « au cas où »).
  • Pour les listes, passer la collection paginée (Laravel renvoie un objet avec data, links, current_page, etc.) ; côté client vous afficherez users.data et les liens de pagination.
  • Pour les formulaires d’édition, passer la ressource (ex. user) et éventuellement des listes pour les selects (rôles, etc.).
  • Ne pas passer d’objets non sérialisables (ressources, closures). Les modèles Eloquent sont automatiquement transformés (attributs visibles, ou via $hidden, $appends, etc.).

Exemple complet : liste d’utilisateurs (Laravel)

Supposons un modèle User avec au moins id, name, email. Nous voulons une page liste et une page détail.

Routes

// routes/web.php
use App\Http\Controllers\UserController;

Route::get('/users', [UserController::class, 'index'])->name('users.index');
Route::get('/users/{user}', [UserController::class, 'show'])->name('users.show');

Contrôleur

// app/Http/Controllers/UserController.php
namespace App\Http\Controllers;

use App\Models\User;
use Inertia\Inertia;

class UserController extends Controller
{
public function index()
{
$users = User::query()
->orderBy('name')
->paginate(10);

return Inertia::render('Users/Index', [
'users' => $users,
]);
}

public function show(User $user)
{
return Inertia::render('Users/Show', [
'user' => $user,
]);
}
}
  • index : on passe la pagination Laravel ($users). Côté client vous recevrez un objet avec data, links, current_page, last_page, etc.
  • show : on passe le modèle $user. Laravel le sérialise en tableau (attributs du modèle).

Si vous voulez limiter les champs du modèle exposés, vous pouvez passer un tableau au lieu du modèle :

return Inertia::render('Users/Show', [
'user' => $user->only(['id', 'name', 'email', 'created_at']),
]);

Ou utiliser les API Resources Laravel pour un format cohérent (hors scope de ce module, mais très utile en projet réel).


Recevoir les props – React (Users/Index et Users/Show)

Page liste – resources/js/Pages/Users/Index.jsx

import React from 'react';
import { Link } from '@inertiajs/react';

export default function Index({ users }) {
return (
<div>
<h1>Liste des utilisateurs</h1>
<ul>
{users.data.map((user) => (
<li key={user.id}>
<Link href={`/users/${user.id}`}>
{user.name}{user.email}
</Link>
</li>
))}
</ul>

{/* Liens de pagination simples */}
{users.prev_page_url && (
<Link href={users.prev_page_url}>Page précédente</Link>
)}
{users.next_page_url && (
<Link href={users.next_page_url}>Page suivante</Link>
)}
</div>
);
}
  • users : c’est l’objet paginé Laravel. users.data est le tableau des éléments de la page courante. users.prev_page_url / users.next_page_url sont des URLs prêtes à l’emploi pour la pagination.
  • Link : composant Inertia pour la navigation sans rechargement. On l’utilise pour aller au détail d’un utilisateur et pour les liens de pagination.

Page détail – resources/js/Pages/Users/Show.jsx

import React from 'react';
import { Link } from '@inertiajs/react';

export default function Show({ user }) {
return (
<div>
<h1>Profil de {user.name}</h1>
<p>Email : {user.email}</p>
<p>Inscrit le : {new Date(user.created_at).toLocaleDateString()}</p>
<Link href="/users">Retour à la liste</Link>
</div>
);
}
  • user : objet (ou tableau) avec les champs du modèle (id, name, email, created_at, etc.). On l’utilise directement dans le JSX.

Typage TypeScript (React) – optionnel mais recommandé

En TypeScript, vous pouvez typer les props pour éviter les erreurs et avoir l’autocomplétion :

// Pages/Users/Index.tsx
import React from 'react';
import { Link } from '@inertiajs/react';

interface User {
id: number;
name: string;
email: string;
}

interface PaginatedUsers {
data: User[];
current_page: number;
last_page: number;
prev_page_url: string | null;
next_page_url: string | null;
}

interface Props {
users: PaginatedUsers;
}

export default function Index({ users }: Props) {
// ...
}

Recevoir les props – Vue (Users/Index et Users/Show)

Page liste – resources/js/Pages/Users/Index.vue

<template>
<div>
<h1>Liste des utilisateurs</h1>
<ul>
<li v-for="user in users.data" :key="user.id">
<Link :href="`/users/${user.id}`">
{{ user.name }} – {{ user.email }}
</Link>
</li>
</ul>

<Link v-if="users.prev_page_url" :href="users.prev_page_url">
Page précédente
</Link>
<Link v-if="users.next_page_url" :href="users.next_page_url">
Page suivante
</Link>
</div>
</template>

<script setup>
import { Link } from '@inertiajs/vue3';

defineProps({
users: {
type: Object,
required: true,
},
});
</script>
  • defineProps : définit les props du composant. Ici on attend un objet users (l’objet paginé Laravel).
  • Link : composant Inertia pour la navigation, importé depuis @inertiajs/vue3.

Page détail – resources/js/Pages/Users/Show.vue

<template>
<div>
<h1>Profil de {{ user.name }}</h1>
<p>Email : {{ user.email }}</p>
<p>Inscrit le : {{ formatDate(user.created_at) }}</p>
<Link href="/users">Retour à la liste</Link>
</div>
</template>

<script setup>
import { Link } from '@inertiajs/vue3';

defineProps({
user: {
type: Object,
required: true,
},
});

function formatDate(value) {
return value ? new Date(value).toLocaleDateString() : '';
}
</script>

Typage avec TypeScript (Vue) – optionnel

En Vue 3 avec <script setup lang="ts">, vous pouvez définir une interface pour les props :

<script setup lang="ts">
import { Link } from '@inertiajs/vue3';

interface User {
id: number;
name: string;
email: string;
created_at: string;
}

interface Props {
user: User;
}

defineProps<Props>();

function formatDate(value: string) {
return value ? new Date(value).toLocaleDateString() : '';
}
</script>

Structure des dossiers Pages/

Une organisation claire facilite la maintenance. Exemple :

resources/js/Pages/
├── Welcome.jsx → Inertia::render('Welcome')
├── Dashboard.jsx → Inertia::render('Dashboard')
├── Users/
│ ├── Index.jsx → Inertia::render('Users/Index')
│ ├── Show.jsx → Inertia::render('Users/Show')
│ ├── Create.jsx → Inertia::render('Users/Create')
│ └── Edit.jsx → Inertia::render('Users/Edit')
├── Articles/
│ ├── Index.jsx
│ ├── Show.jsx
│ └── ...
└── Settings/
└── Profile.jsx → Inertia::render('Settings/Profile')

Même logique en Vue avec des fichiers .vue. Gardez une convention de nommage (Index pour la liste, Show pour le détail, Create/Edit pour les formulaires) pour que tout le monde s’y retrouve.


Examen et erreurs courantes

  • Page blanche ou « Component not found » : vérifier que le nom dans Inertia::render('Users/Index') correspond bien au fichier Pages/Users/Index.jsx (ou .vue), avec la bonne casse et les bons slashes.
  • Props undefined : vérifier que vous passez bien les clés dans le tableau de Inertia::render() et que vous les lisez sous le même nom dans le composant (ex. users côté Laravel → users en prop).
  • Pagination : Laravel renvoie un objet avec data pour les éléments. Toujours utiliser users.data pour la liste et users.links (ou prev_page_url / next_page_url) pour la pagination.

À retenir

  • Inertia::render('Nom/De/Page', [ 'prop1' => $value1, ... ]) : nom = chemin sous Pages/ sans extension ; les clés du tableau deviennent les props du composant.
  • React : les props sont le premier argument de la fonction composant (ou définies via TypeScript).
  • Vue : les props se déclarent avec defineProps et sont utilisées dans le template.
  • Pagination : passer le résultat de ->paginate() ; côté client utiliser .data pour les éléments et .links / prev_page_url / next_page_url pour la navigation.
  • Link (Inertia) pour tous les liens internes afin de garder la navigation en XHR.

Dans le prochain module, nous verrons comment créer des layouts (navbar, sidebar) et les réutiliser dans les pages React et Vue.