Découverte de Laravel

CLI Artisan

Le développement d'un projet Laravel est rythmé par l'utilisation d'un outil en ligne de commande (CLI) nommé Artisan.

Cet outil peut être appelé depuis un terminal placé dans le répertoire racine du projet :

$ php artisan <command> [args...]

Pour avoir une liste de toutes les commandes possibles :

$ php artisan list

Le modèle MVC

Laravel comme beaucoup d'autres frameworks suit le schéma "MVC". Le modèle MVC répartit la charge de travail à trois modules différents :

Ce schéma n'est pas applicable comme tel pour un framework web car le schéma initial ne prend pas en compte l'aspect Requête/Réponse du protocole HTTP.

Pour palier à cela, les frameworks web ont intégré un 4ème module au schéma : le routeur.

Le routeur

Le routeur est le chef d'orchestre d'une application web.

C'est un grand annuaire qui associe une URI avec une destination.

Dans un projet Laravel, les routes sont répertoriées dans le dossier routes/, et le fichier principal pour les routes de notre applications se trouvent dans le fichier routes/web.php.

Voici un exemple de routeur :

Route::get('/', function () {
    return "Bonjour";
});

Aller sur la page d'accueil du site affichera en brut :

Bonjour

Routes selon la méthode HTTP

Route::get('/contact', function () {
    return view('contact'); // Affiche le formulaire de contact
});

Route::post('/contact', function () {
    // Traitement du formulaire de contact
    return redirect()->to('/contact');
});

Nommage des routes

Route::get('/contact', function () {
    return view('contact'); 
})->name('contact');

// Génération d'une URL
route('contact'); // -> https://monsite.com/contact

// Redirection vers une route nommée
redirect()->route('contact');

Pourquoi nommer ses routes ?

Route::get('/page-contact', function () {
    return view('contact');
})->name('contact');

// Génération de l'URL
route('contact'); // -> https://monsite.com/page-contact
Route::get('/contact', function () {
    return view('contact');
})->name('contact');

// Même code pour générer l'URL
route('contact'); // -> https://monsite.com/contact

Paramètres d'URI

Pour rendre nos URL dynamiques, nous pouvons composer nos URI avec des paramètres :

Route::get('/post/{post_id}', function ($post_id) {
    // ...
});

// /post/13 ($post_id = 13), /post/test ($post_id = "test"), ...

Nous pouvons également définir des paramètres facultatifs :

Route::get('/posts/{page?}', function ($page = 1) {
    // ...
});

// /posts/4 ($page = 4), /posts ($page = 1)

Attention à l'ordre des routes quand vous les définissez !

Route::get('/posts/{page?}', function ($page = 1) {
    return "KO";
});

Route::get('/posts/edit', function () {
    return "OK";
});

// /posts/edit
// => "KO" ($page = 'edit')

Groupes de routes

Les groupes de routes nous évite à recopier des composants de routes qui sont communs à plusieurs :

Route::prefix('/posts')->name('posts.')->group(function () {
    Route::get('/', '...')->name('index')
    // GET /posts (posts.index)

    Route::get('/create', '...')->name('create')
    // GET /posts (posts.create)

    Route::post('/create', '...')->name('store')
    // POST /posts (posts.store)

    Route::get('/{post}/edit', '...')->name('edit')
    // GET /posts (posts.edit)

    Route::post('/{post}/edit', '...')->name('update')
    // POST /posts (posts.update)

    Route::delete('/{post}', '...')->name('destroy')
    // DELETE /posts (posts.destroy)

});


// Même code pour générer l'URL
route('contact'); // -> https://monsite.com/contact

Le moteur de template : Laravel Blade

Le moteur de template de Larevel s'inspire beaucoup de celui d'ASP.NET (Razor) :

  • Les variables sont affichées avec {{ $variable }}
  • Les directives sont préfixées de @ : @if ($condition), @endif, etc...
  • Les commentaires sont insérés avec {# Commentaire #}

Exemple de template Blade :

{# resources/views/index.blade.php #}

@if ($user == 'admin')
    Bonjour {{ $user }} !
@else
    Vous n'êtes pas admin
@endif

<ul>
    @forelse ($posts as $post)
        <li>{{ $post->title }}</li>
    @empty
        <li>Aucun article</li>
    @endforelse
</ul>

Les directives des templates sont du PHP !

@if (in_array('PHP', $languagesCools))
    Le PHP c'est bien !
@endif

Il y a {{ count($languagesCools }} languages cools

Echappement des variables

Les sorties des variables sont échappées : L'HTML est affichée en brut pour éviter les failles XSS :

<?php $lien = "<a href='/'>Accueil</a>"; ?>

{{ $lien }}
-> <a href='/'>Accueil</a>

{!! $lien !!}
-> Accueil

Héritage

Il est possible également de faire de l'héritage

{# resources/views/app.blade.php #}

<div class="content">
    @yield('content')
</div>
{# resources/views/index.blade.php #}

@extend('app')

@section('content')
    Contenu de ma page
@endsection

Retourner un template depuis une route

Maintenant que nous savons comment construire nos vues, voici comment les utiliser :

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

Nous pouvons également passer des variables à afficher dans nos vues :

Route::get('/', function () {
    $date = Carbon\Carbon::now();
    return view('home', ['date' => $date]);
});

ou plus simplement :

Route::get('/', function () {
    $date = Carbon\Carbon::now();
    return view('home', compact('date'));
});

Contrôleurs

L'utilisation de contrôleurs permettent d'éviter de placer tout le business logic de notre site dans le fichier de routes.

Les contrôleurs sont des classes qui ont la responsabilité de convertir la requête entrante en réponse.

C'est ici que les traitements (récupération depuis la BDD, préparation des données pour la vue, etc...) se font.

Un contrôleur possède des actions, ce qui est représenté par une classe et ses méthodes.

Pour nous faciliter la vie, nous pouvons générer le contrôleur avec Artisan :

$ php artisan make:controller HomeController

Nous pouvons donc modifier notre fichier de routes pour le rendre beaucoup plus lisible :

Route::get('/', 'HomeController@index')->name('index');

Et écrire notre action dans notre contrôleur :

// app/Http/Controller/HomeController
class HomeController extends Controller {
    public function index()
    {
        return view('index');
    }
}

Traitement d'un formulaire

Soit les routes suivantes :

Route::get('/contact', 'ContactController@index')->name('contact');
Route::post('/contact', 'ContactController@store');

Nous pouvons traiter le formulaire de telle manière :

use Illuminate\Http\Request; 
class ContactController extends Controller {
    public function store(Request $request)
    {
        $request->all();
        $request->get('email');
        $request->has('checkbox');
        return redirect()->route('contact');
    }
}

Validation d'un formulaire

Il est également important de valider les données des formulaires

use Illuminate\Http\Request; 
class ContactController extends Controller {
    public function store(Request $request)
    {
        $this->validate($request, [
            'name' => ['required'],
            'email' => ['required', 'email'],
        ]);

        // ...
    }
}

Vous pouvez retrouver toutes les règles de validation de sur la doc Laravel

Bases de données

Laravel intègre son propre ORM : Laravel Eloquent. Ce dernier permet de gérer la couche modèle du schéma MVC.

Convetion de nommage :

  • Les tables sont en minuscules et au pluriel (users, posts, etc...)
  • Les modèles sont au singulier (User, Post, etc...)

Migrations

Les migrations permettent de définir la structure de la base de données (tables, colonnes, etc...)

Elles décrivent l'évolution de la base de données au fur et à mesure que nous progressons dans l'élaboration de l'application.

Il est important lorsque qu'un changement doit être fait en base par rapport à l'état actuel de créer une nouvelle migration et non modifier une précédente.

Lorsque l'on travaille à plusieurs ou avec des serveurs de test/production, il est possible que des bases soit à des états différents.

C'est pour cela que les migrations doivent être consécutives et immuables.

Pour créer une migration, on utilise toujours Artisan :

$ php artisan make:migration create_posts_table

Ceci va nous créer un fichier YYYY_MM_DD_HHMMSS_create_posts_table.php dans le répertoire database/migrations/.

class CreatePostsTable extends Migration
{
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
            $table->string('title');
            // ...
        });
    }

    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

Une fois la migration écrite, une commande Artisan permet de l'éxécuter :

$ php artisan migrate

Il est également possible de créer une migration pour modifier une table (ajouter, modifier, supprimer une colonne) :

$ php artisan make:migration add_author_to_posts

Modèles

Les modèles sont des classes qui représente une table dans notre base de données. Une instance de ces classes représente une entrée dans la base de données.

Artisan nous permet également de créer ces classes pour nous :

$ php artisan make:model Post

Ceci va créer une classe app/Post.php vide. On peut y préciser beaucoup de propriétés de notre modèle si besoin.

Query Builder

Le Query Builder permet de faire des requêtes sur la base pour interroger une table.

Il nous est possible de faire des requêtes complexes sans une seule ligne d'SQL !

Récupérer toutes les entitées (!!) :

$posts = Post::all();

Récupérer un maximum de 5 entitées selon des critères et un ordre particulier :

$posts = Post::where('published', true)
             ->orderByDesc('published_at')
             ->take(5)
             ->get();

Récupérer une entité selon son ID :

$post = Post::find($id);

Récupérer une entité selon des critères :

$post = Post::where('id', $id)->first();

Créer une entité :

$post = new Post();
$post->title = "Hello World";
$post->save();

Modifier une entité :

$post = Post::find($id);
$post->title = "Hello World";
$post->save();

Supprimer une entité :

$post = Post::find($id);
$post->delete();

Middleware

Il s'agit de classes qui permettent de traiter la requête avant qu'elle arrive au contrôleur et/ou de traiter la réponse avant qu'elle soit renvoyée au client.