Aller au contenu

Structure d'un projet Laravel

Bloc Laravel · séance 1 · ~40 min

Slides du chapitre (PDF)PDF

Le projet premier-test-laravel, créé au chapitre Installation et complété au chapitre MVC, compte vingt-huit éléments à sa racine, dossiers cachés compris. Vous modifierez surtout le code de app/, les routes de routes/ et les vues de resources/views/.

D'autres dossiers contiennent la configuration ou des fichiers créés pendant l'installation et le fonctionnement de l'application. Le modèle, le contrôleur et la vue créés à l'exercice 3 du chapitre MVC doivent être présents dans votre projet.

À la fin de ce chapitre, vous serez capables de :

  1. dire à quoi sert chaque dossier de la racine d'un projet Laravel 13,
  2. retrouver où ranger un contrôleur, un modèle, une vue ou une modification de la base, et avec quelle commande les créer,
  3. expliquer le trajet d'une requête fichier par fichier, de public/index.php jusqu'à la vue,
  4. distinguer les fichiers que vous modifiez de ceux que vous ne touchez jamais,
  5. reconnaître $fillable, la liste des colonnes que Laravel peut remplir, sous ses deux écritures.

1. La racine

La racine d'un projet est le dossier qui contient tous ses fichiers et ses sous-dossiers. La commande laravel new a créé celle de premier-test-laravel en septembre 2026. Elle a utilisé Pest, le lanceur de tests, et npm, l'outil qui installe les paquets JavaScript.

premier-test-laravel/
├── .editorconfig      réglages de l'éditeur
├── .git/              historique git
├── .env               vos réglages, hors git
├── .env.example       modèle de .env, versionné
├── .gitattributes     réglages git
├── .gitignore         ce que git ignore
├── .npmrc             réglages npm
├── AGENTS.md          consignes pour agents IA
├── CLAUDE.md          même texte
├── README.md          présentation
├── app/               votre code PHP
├── artisan            console de Laravel
├── bootstrap/         démarrage
├── composer.json      dépendances PHP
├── composer.lock      versions exactes
├── config/            configuration
├── database/          la base de données
├── node_modules/      paquets npm
├── package-lock.json  versions exactes npm
├── package.json       dépendances JavaScript
├── phpunit.xml        configuration des tests
├── public/            ce que voit le navigateur
├── resources/         vues, CSS, JavaScript
├── routes/            les routes
├── storage/           fichiers générés
├── tests/             les tests Pest
├── vendor/            paquets Composer
└── vite.config.js     configuration de Vite

La racine d'un projet Laravel 13 en vue éclatée, huit dossiers numérotés

2. Les fichiers de la racine

.env et .env.example

Une variable d'environnement contient un réglage propre à la machine sur laquelle l'application fonctionne. Votre ordinateur peut utiliser SQLite tandis que le serveur utilise MySQL. Au démarrage, Laravel lit ces valeurs dans .env. Vous pouvez ainsi changer la base, l'adresse du site ou un mot de passe sans modifier les classes de l'application.

Le fichier .env.example contient les noms des variables nécessaires au projet et des valeurs utilisables sans secret.

APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8000
…
DB_CONNECTION=sqlite
…
SESSION_DRIVER=database
QUEUE_CONNECTION=database
CACHE_STORE=database
…
MAIL_MAILER=log

Dans cet exemple, APP_KEY est donc vide. Pendant l'installation, la commande a copié .env.example vers .env, puis elle a créé une clé propre au projet. Sur un serveur de production, .env contient les mots de passe de la base et des services. Le fichier .gitignore empêche Git d'enregistrer trois fichiers qui pourraient contenir ces secrets.

.env
.env.backup
.env.production

N'enregistrez jamais .env dans Git. Quand votre application a besoin d'une nouvelle variable, ajoutez son nom dans .env.example sans y placer de valeur secrète.

artisan

artisan est le fichier qui lance Artisan, l'outil en ligne de commande vu au chapitre Installation. Ses commandes peuvent créer un fichier, lancer un test ou afficher des informations sur le projet. La commande php artisan about affiche notamment les versions installées et les principaux réglages actifs.

  Environment ................................................................
  Application Name ................................................... Laravel
  Laravel Version .................................................... 13.33.0
  PHP Version ......................................................... 8.4.21
  Composer Version ..................................................... 2.8.6
  Environment .......................................................... local
  Debug Mode ......................................................... ENABLED
  URL ......................................................... localhost:8000
  …

Chez vous, la ligne Application Name affiche Premier test, le nom choisi à l'exercice 2 du chapitre Installation. Sous Herd ou Laragon, la ligne URL affiche premier-test-laravel.test.

composer.json

Le fichier composer.json indique les paquets PHP nécessaires au projet et la manière de charger vos classes. Dans premier-test-laravel, ses parties require et autoload contiennent ces lignes.

"require": {
    "php": "^8.3",
    "laravel/framework": "^13.17",
    "laravel/tinker": "^3.0"
},
…
"autoload": {
    "psr-4": {
        "App\\": "app/",
        …
    }
},

Une dépendance est un paquet dont le projet a besoin pour fonctionner. Laravel apparaît donc dans la partie require de composer.json.

Un espace de noms est le début du nom complet d'une classe, comme App\Models dans App\Models\User. La règle PSR-4 relie cet espace de noms au dossier qui contient le fichier de la classe. La ligne "App\\": "app/" permet ainsi de retrouver App\Models\User dans app/Models/User.php.

La partie scripts de composer.json associe des noms courts à des commandes. Vous lancez directement composer run setup pour préparer un projet copié depuis un dépôt Git. composer run dev démarre le serveur et composer run test lance les tests. D'autres noms commencent par pre- ou post-. Composer exécute ces scripts avant ou après une opération précise. Par exemple, post-install-cmd est exécuté après composer install.

package.json et vite.config.js

Le fichier package.json liste les paquets JavaScript du projet, dont Vite, l'outil qui prépare les fichiers chargés par le navigateur. La version installée est Vite 8. Le fichier vite.config.js lui indique de partir de resources/css/app.css et de resources/js/app.js. Vite lit aussi les fichiers auxquels ces deux sources font référence. La commande npm run build écrit le CSS et le JavaScript obtenus dans public/build/.

Le même fichier liste Tailwind CSS 4 et ses extensions.

Les autres fichiers

Le fichier phpunit.xml contient les réglages utilisés pendant les tests. AGENTS.md et CLAUDE.md contiennent le même texte pour les agents IA de programmation. Ce texte leur demande d'installer Laravel Boost, un paquet qui fournit à ces agents des informations sur le projet et les conventions de Laravel.

3. Les dossiers

app/ : votre code

Dans un projet neuf, app/ contient trois fichiers : Http/Controllers/Controller.php, Models/User.php et Providers/AppServiceProvider.php. Votre projet premier-test-laravel contient aussi PostController.php et Post.php, créés au chapitre MVC.

Controller.php contient la classe abstraite vide vue au chapitre Framework et librairie. Votre PostController, créé au chapitre MVC, en hérite. Le dossier Models/ contient une classe par table, comme Post pour la table posts.

Le dossier Providers/ contient les fournisseurs de services. Laravel exécute ces classes pendant son démarrage pour enregistrer ou configurer une classe utilisée ailleurs. Un fournisseur peut, par exemple, indiquer quelle classe doit envoyer les courriels de l'application.

Un projet neuf ne contient pas tous les sous-dossiers possibles de app/. Lorsqu'une commande Artisan crée un premier fichier d'un certain type, elle crée aussi le dossier prévu pour ce type s'il n'existe pas.

php artisan make:event DemoEvent
   INFO  Event [app/Events/DemoEvent.php] created successfully.

Avant cette commande, app/ contient Http, Models et Providers. make:event crée le dossier app/Events/ et le fichier DemoEvent.php. Supprimez ensuite app/Events/, puisque ce fichier sert uniquement à cet essai. Les commandes make:job, make:mail et make:policy créent de la même manière les dossiers prévus pour leurs fichiers.

Vous pouvez aussi créer app/Services/ vous-même. Le chargement automatique permet à Composer de trouver une classe à partir de son nom complet. Composer trouvera App\Services\Mailer si la classe se trouve dans app/Services/Mailer.php et déclare l'espace de noms App\Services.

routes/

Dans un projet neuf, le dossier routes/ contient seulement web.php et console.php. Dans web.php, chaque route associe une adresse et une méthode HTTP au code qui doit traiter la requête.

Route::get('/', function () {
    return view('welcome');
});

Le fichier console.php permet de définir de courtes commandes Artisan, comme inspire. Le fichier api.php n'existe pas dans un projet neuf. La commande php artisan install:api le crée si l'application en a besoin.

La commande php artisan route:list affiche l'adresse, la méthode HTTP et le code associé à chaque route.

  GET|HEAD  / ............................................... routes/web.php:6
  GET|HEAD  posts ....................................... PostController@index
  GET|HEAD  storage/{path} storage.local › vendor/laravel/framework/src/Illum…
  PUT       storage/{path} storage.local.upload › vendor/laravel/framework/sr…
  GET|HEAD  up vendor/laravel/framework/src/Illuminate/Foundation/Configurati…

                                                            Showing [5] routes

La route posts a été ajoutée au chapitre MVC. Laravel enregistre lui-même les deux routes storage/{path} : elles servent les fichiers du disque local, le dossier storage/app/private, à condition que l'adresse porte une signature que Laravel a fabriquée. Vous n'avez pas besoin de les modifier, et vous ne les utiliserez pas dans ce cours.

resources/

Le dossier views/ contient les vues Blade dont le nom se termine par .blade.php. Les dossiers css/ et js/ contiennent les fichiers que Vite lit avant de produire ceux de public/build/.

Le nom passé à view() part de resources/views/ et omet l'extension .blade.php. Dans view('posts.index'), posts désigne le sous-dossier et index désigne le fichier. Laravel ouvre donc resources/views/posts/index.blade.php, la vue créée au chapitre MVC.

Les fichiers de traduction ne se trouvent pas dans resources/. La commande php artisan lang:publish crée le dossier lang/ à la racine pour les recevoir.

database/

Une migration est un fichier PHP qui décrit une modification de la structure de la base. Le dossier migrations/ contient les trois fichiers exécutés par l'installeur pour créer users, cache et jobs. Il contient aussi la migration de posts, ajoutée au chapitre MVC.

Une fabrique crée des objets remplis de données fictives pour les tests. Celle des utilisateurs se trouve dans factories/UserFactory.php. Un seeder ajoute des données de départ dans la base. Le fichier principal est seeders/DatabaseSeeder.php.

Le fichier database.sqlite contient la base SQLite elle-même. La règle inscrite dans database/.gitignore empêche Git de l'enregistrer.

config/

Dix fichiers, un par sujet, de app.php à session.php. Extrait de config/app.php :

'name' => env('APP_NAME', 'Laravel'),
…
'env' => env('APP_ENV', 'production'),

Les fichiers de config/ lisent .env avec env(). Dans env('APP_NAME', 'Laravel'), la seconde valeur est utilisée lorsque APP_NAME n'en contient aucune. Les contrôleurs et les autres classes lisent ensuite ce réglage avec config('app.name').

Un cache de configuration est un fichier qui contient des réglages déjà rassemblés. Sur un serveur de production, la commande php artisan config:cache écrit ces valeurs dans bootstrap/cache/config.php pour accélérer le démarrage. Au démarrage suivant, Laravel charge ce fichier au lieu de relire .env et les fichiers de config/. Un appel direct à env('APP_NAME') dans un contrôleur renvoie alors null, tandis que config('app.name') lit la valeur préparée. Utilisez env() dans config/ et config() dans les autres classes.

public/ : la racine web

La racine web est le dossier dans lequel le serveur cherche les fichiers demandés par le navigateur. Pour ce projet, Herd et Laragon utilisent premier-test-laravel/public à l'adresse premier-test-laravel.test. Le navigateur peut donc recevoir favicon.ico, robots.txt et les fichiers de build/. Il ne peut pas demander directement .env ni les classes de app/, qui se trouvent en dehors de public/.

Le fichier public/index.php reçoit les requêtes qui ne visent pas un fichier présent dans public/. Le fichier .htaccess donne cette règle au serveur Apache.

bootstrap/

Le fichier app.php prépare Laravel et lui indique notamment où trouver les routes. Le fichier providers.php donne la liste des fournisseurs de services à exécuter pendant ce démarrage.

return [
    AppServiceProvider::class,
];

La commande php artisan config:cache, par exemple, écrit dans bootstrap/cache/ le fichier que Laravel relira au prochain démarrage.

storage/, tests/, vendor/

Le dossier storage/ reçoit les fichiers que Laravel produit pendant le fonctionnement. Les fichiers conservés par l'application vont dans storage/app/.

Laravel place ses fichiers de travail dans storage/framework/. Vous y trouvez les caches, les données conservées entre deux requêtes d'un même navigateur et les vues déjà transformées en PHP.

Un journal est un fichier dans lequel Laravel écrit ses erreurs et ses messages. Le journal principal se trouve dans storage/logs/laravel.log. La commande composer run dev lance Pail, l'outil qui lit ce journal et affiche les nouvelles lignes dans le terminal. Pail crée aussi storage/pail/.

Le dossier tests/ contient Feature/, Unit/, TestCase.php et Pest.php. Un test unitaire vérifie une classe seule et se place dans Unit/. Un test de fonctionnalité vérifie plusieurs parties de l'application ensemble, par exemple en envoyant une requête HTTP. La commande make:test crée ce second type de test dans Feature/ par défaut.

Le dossier vendor/ contient les paquets PHP installés par Composer. La commande composer install peut le recréer à partir de composer.lock.

Où ranger quoi

Je veux ajouter… php artisan … Fichier créé
un contrôleur make:controller CommentController app/Http/Controllers/CommentController.php
un modèle make:model Comment app/Models/Comment.php
une migration make:migration create_comments_table database/migrations/2026_09_26_140158_create_comments_table.php
une vue make:view comments.index resources/views/comments/index.blade.php
un test make:test CommentTest tests/Feature/CommentTest.php
une commande Artisan make:command PublishPosts app/Console/Commands/PublishPosts.php

Artisan place au début du nom de la migration la date et l'heure auxquelles vous lancez la commande.

4. Le trajet d'une requête, fichier par fichier

Quand le navigateur demande http://premier-test-laravel.test/, le serveur cherche d'abord un fichier correspondant dans la racine web. Comme cette adresse ne désigne aucun fichier public, le serveur exécute public/index.php.

…
// Register the Composer autoloader...
require __DIR__.'/../vendor/autoload.php';

// Bootstrap Laravel and handle the request...
/** @var Application $app */
$app = require_once __DIR__.'/../bootstrap/app.php';

$app->handleRequest(Request::capture());

La première instruction charge vendor/autoload.php. Quand PHP rencontre un nom de classe, le chargeur de Composer utilise la règle PSR-4 pour trouver et ouvrir son fichier. La deuxième instruction ouvre bootstrap/app.php, qui crée l'objet représentant l'application.

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    …

L'appel à withRouting donne à Laravel le chemin de routes/web.php et celui de routes/console.php. La ligne health: '/up' enregistre aussi la route /up affichée par php artisan route:list.

Dans public/index.php, Request::capture() rassemble l'adresse, la méthode HTTP et les données reçues. La méthode handleRequest() reçoit cet objet et compare ensuite l'adresse / aux routes de routes/web.php. La route correspondante exécute view('welcome'). Laravel ouvre alors resources/views/welcome.blade.php et utilise son contenu pour produire la réponse HTML.

Pour afficher /posts, le serveur commence aussi par public/index.php. Ce fichier active le chargement automatique, puis ouvre bootstrap/app.php. Laravel lit alors routes/web.php et trouve PostController::index().

La méthode index() demande les articles à la classe Post, puis choisit la vue avec view('posts.index'). Laravel ouvre donc resources/views/posts/index.blade.php. Blade parcourt les articles reçus dans $posts et produit le HTML envoyé au navigateur.

Le fichier artisan ouvre lui aussi bootstrap/app.php. Il transmet toutefois une commande à handleCommand au lieu de transmettre une requête HTTP à handleRequest.

Une requête traverse public/index.php, bootstrap/app.php, routes/web.php, le contrôleur et la vue

5. Ce qu'on modifie, ce qu'on ne touche jamais

Vous écrivez dans Vous ne touchez jamais à
app/, routes/, resources/, database/, tests/ vendor/, recréé par composer install
config/ node_modules/, recréé par npm install
.env et .env.example bootstrap/cache/, généré par Laravel
composer.json via composer require storage/framework/, généré par Laravel
public/build/, produit par npm run build

composer install recrée vendor/, tandis que npm install recrée node_modules/. Laravel recrée bootstrap/cache/ et storage/framework/ quand il en a besoin. La commande npm run build remplace le contenu de public/build/. Une modification manuelle dans l'un de ces dossiers disparaît donc à la génération suivante.

6. Deux écritures de $fillable

Le modèle Post lu au chapitre 0 de la POO déclarait protected $fillable = ['title', 'body'];. Celui que vous avez créé au chapitre MVC ne déclare encore rien. Dans Laravel 13, le fichier User.php utilise une autre écriture.

#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'remember_token'])]
class User extends Authenticatable

#[Fillable(...)] est un attribut PHP, une information attachée à une classe que Laravel peut lire. Ici, cet attribut remplace la propriété protected $fillable = [...]. Les deux écritures indiquent les colonnes que Laravel peut remplir à partir d'un ensemble de données, comme celles d'un formulaire validé.

Le modèle Post créé au chapitre MVC ne contient encore ni cette propriété ni cet attribut. L'exercice utilise donc forceCreate(). Cette méthode remplit le nouvel article et l'enregistre sans consulter la liste des colonnes autorisées.

À retenir

  • Vous placez le code PHP dans app/, les routes dans routes/ et les vues dans resources/views/.
  • Le fichier .env contient les réglages de votre machine et ne doit jamais être enregistré dans Git.
  • Le dossier public/ est la racine web, et public/index.php reçoit les requêtes qui ne visent aucun fichier public.
  • Une commande make: crée le fichier demandé et son dossier si celui-ci n'existe pas encore.
  • Les outils recréent vendor/, node_modules/, bootstrap/cache/, storage/framework/ et public/build/. Vous ne modifiez donc pas leur contenu.

Exercices

1. Chasse au trésor

Dans premier-test-laravel, trouvez chaque fichier. Affichez ensuite la réponse de la carte pour vérifier son chemin.

Où la route / est-elle définie ?

routes/web.php

Quelle vue la page d'accueil affiche-t-elle ?

resources/views/welcome.blade.php

Quel fichier reçoit une requête qui ne vise pas un fichier public ?

public/index.php

Où changer le nom de l'application sur votre machine ?

.env, variable APP_NAME

Où la table users est-elle créée ?

database/migrations/0001_01_01_000000_create_users_table.php

Où est la classe mère des contrôleurs ?

app/Http/Controllers/Controller.php

Où sont listés les fournisseurs de services ?

bootstrap/providers.php

Où Laravel écrit-il ses erreurs ?

storage/logs/laravel.log

2. Les générateurs

Dans premier-test-laravel, lancez php artisan list make, puis comptez les commandes dont le nom commence par make:. Lancez ensuite php artisan make:job SendNewsletter et php artisan make:enum Status. Regardez dans app/ quel fichier chaque commande a créé et si un nouveau dossier est apparu. Supprimez enfin ces deux fichiers et le dossier devenu vide.

3. Sur papier : où ranger ces fichiers ?

Écrivez le chemin complet de chaque fichier dans premier-test-laravel. Les étiquettes, ou tags, servent à classer les articles.

  1. TagController.php
  2. le modèle Tag.php
  3. 2026_10_05_120000_create_tags_table.php
  4. la vue appelée par view('tags.index')
  5. TagTest.php, un test de fonctionnalité
  6. TagSeeder.php