IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Tutoriel pour apprendre à utiliser le framework Laravel 5.3

Les données


précédentsommairesuivant

I. Migrations et modèles

À partir de ce chapitre, il serait souhaitable que vous installiez une barre de débogage. La plus utile est celle proposée par barryvdh. Suivez les indications fournies pour l'installation, ça vous fera un bon exercice.

I-A. Les migrations

Une migration permet de créer et de mettre à jour un schéma de base de données. Autrement dit, vous pouvez créer des tables, des colonnes dans ces tables, en supprimer, créer des index… Tout ce qui concerne la maintenance de vos tables peut être pris en charge par cet outil. Vous avez ainsi un suivi de vos modifications.

I-A-1. La configuration de la base

Vous devez dans un premier temps avoir une base de données. Laravel permet de gérer les bases de type MySQL, Postgres, SQLite et SQL Server. Je ferai tous les exemples avec MySQL, mais le code sera aussi valable pour les autres types de bases.

Il faut indiquer où se trouve votre base, son nom, le nom de l'utilisateur, le mot de passe dans le fichier de configuration .env :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

Ici nous avons les valeurs par défaut à l'installation de Laravel.

Voici par exemple mes réglages pour ma base de test MySQL nommée « tuto » avec MySQL en local non sécurisé :

 
Sélectionnez
DB_DATABASE=tuto
DB_USERNAME=root
DB_PASSWORD=

I-A-2. Artisan

Nous avons déjà utilisé Artisan qui permet de faire beaucoup de choses, vous avez un aperçu des commandes en entrant :

 
Sélectionnez
php artisan

Vous avez une longue liste. Pour ce chapitre nous allons nous intéresser uniquement à celles qui concernent les migrations :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
  Migrate              Run the database migrations
...
  migrate
  migrate:install      Create the migration repository
  migrate:refresh      Reset and re-run all migrations
  migrate:reset        Rollback all database migrations
  migrate:rollback     Rollback the last database migration
  migrate:status       Show the status of each migration

I-A-3. Créer la migration

On va créer la migration pour notre table :

 
Sélectionnez
php artisan make:migration create_emails_table

Si vous regardez maintenant dans le dossier database/migrations, vous trouvez un fichier du genre 2016_08_31_212841_create_emails_table.php (la partie numérique qui inclut la date sera évidemment différente pour vous) :

Image non disponible

Mais il y a déjà des migrations présentes, à quoi servent-elles ?

Il y a déjà effectivement deux migrations présentes :

  • table users : c'est une migration de base pour créer une table des utilisateurs ;
  • table password_resets : c'est une migration liée à la précédente qui permet de gérer le renouvellement des mots de passe en toute sécurité.

Nous nous intéresserons à ces migrations dans un chapitre ultérieur. Comme nous n'allons pas avoir besoin immédiatement de ces migrations, le mieux est de les supprimer pour le moment pour éviter de créer des tables inutiles :

Image non disponible

Voici le contenu de la migration que nous venons de créer :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
<?php
 
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
 
class CreateEmailsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        //
    }
 
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        //
    }
}

On dispose dans cette classe de deux fonctions :

  • up : ici on mettra le code de création ;
  • down : ici on mettra le code de suppression.

On veut créer une table « emails » avec :

  • un id autoincrémenté ;
  • un champ « email » de type texte ;
  • des champs pour mémoriser les dates de création et de modification.

Voilà le code correspondant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
public function up()
{
    Schema::create('emails', function(Blueprint $table) {
        $table->increments('id');
        $table->string('email');
        $table->timestamps();
    });
}

On demande au constructeur de schéma (Schema) de créer (create) la table « emails ». Dans la fonction anonyme, on définit ce qu'on veut pour la table :

  • une colonne « id » auto-incrémentée qui sera ainsi la clé primaire de la table ;
  • une colonne « email » de type string ;
  • deux colonnes pour les dates (générées par la méthode timestamps).

Pour la méthode down on va juste supprimer la table avec un drop :

 
Sélectionnez
1.
2.
3.
4.
public function down()
{
    Schema::drop('emails');
}

Notre migration est maintenant créée.

I-A-4. Utiliser la migration

On va maintenant utiliser la migration (méthode up de la migration) :

Image non disponible

Si on regarde maintenant dans la base, on trouve la table « emails » avec ces quatre colonnes :

Image non disponible

Vous avez aussi la création d'une table migrations :

Image non disponible

Cette table sert à l'intendance des migrations et vous ne devez pas y toucher.

Si vous avez fait une erreur, vous pouvez revenir en arrière avec un rollback qui annule la dernière migration effectuée (utilisation de la méthode down de la migration) :

Image non disponible

La table a maintenant été supprimée de la base. Comme on va avoir besoin de cette table, on relance la migration.

On peut aussi effectuer un rafraîchissement de toutes les migrations avec la commande refresh (rollback de toutes les migrations et nouveau lancement de toutes les migrations).

Vous disposez également de la commande status pour savoir où vous en êtes :

Image non disponible

Mais pour le moment avec une seule migration, il n'y a vraiment pas de quoi se perdre.

I-B. Eloquent et le modèle

Laravel propose un ORM (acronyme de object-relational mapping ou en bon français un mappage objet-relationnel) très performant.

De quoi s'agit-il ?

Tout simplement que tous les éléments de la base de données ont une représentation sous forme d'objets manipulables.

Quel intérêt ?

Tout simplement de simplifier grandement les opérations sur la base comme nous allons le voir dans toute cette partie du cours.

Avec Eloquent une table est représentée par une classe qui étend la classe Model. Pour notre table emails, on va à nouveau utiliser Artisan pour la création du modèle :

 
Sélectionnez
php artisan make:model Email

On trouve le fichier ici :

Image non disponible

Avec cette trame de base :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
<?php
 
namespace App;
 
use Illuminate\Database\Eloquent\Model;
 
class Email extends Model
{
    //
}

On va se contenter de ce code pour notre exemple.

On peut créer le modèle en même temps que la migration pour la table avec cette syntaxe :

 
Sélectionnez
php artisan make:model Email -m

Nous allons voir maintenant comment utiliser cette classe en construisant notre petite application.

I-C. La validation

Pour la validation on va encore créer une requête de formulaire :

 
Sélectionnez
php artisan make:request EmailRequest

On trouve la requête dans son dossier :

Image non disponible

La voici avec le code complété :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
<?php
 
namespace App\Http\Requests;
 
use Illuminate\Foundation\Http\FormRequest;
 
class EmailRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }
 
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return ['email' => 'bail|required|email|unique:emails'];
    }
}

On a quatre règles :

  • bail : on s'arrête à la première erreur ;
  • required : le champ est requis ;
  • email : on doit avoir une adresse email valide ;
  • unique : l'email ne doit pas déjà exister (unique) dans la table emails (on sous-entend qu'il s'agit de la colonne email).

Remarquez la puissance de la dernière règle : Eloquent va vérifier que notre email n'existe pas déjà dans la table !

I-D. Les routes et le contrôleur

I-D-1. Routes

On va avoir deux routes :

 
Sélectionnez
Route::get('email', 'EmailController@create'); 
Route::post('email', 'EmailController@store')->name('store.email');

Remarquez que la seconde route est nommée (store.email).

L'URL de base sera : http://monsite.fr/email

I-D-2. Contrôleur

On crée le contrôleur avec Artisan :

 
Sélectionnez
php artisan make:controller EmailController

On le trouve dans son dossier :

Image non disponible

Le code du contrôleur va reprendre l'essentiel de ce que nous avons vu dans les chapitres précédents en utilisant à nouveau la validation injectée. Modifiez ainsi le code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
<?php
 
namespace App\Http\Controllers;
 
use App\Email;
use App\Http\Requests\EmailRequest;
 
class EmailController extends Controller
{
    public function create()
    {
        return view('email');
    }
 
    public function store(EmailRequest $request)
    {
        $email = new Email;
        $email->email = $request->email;
        $email->save();
         
        return view('email_ok');
    }
}

La nouveauté réside uniquement dans l'utilisation du modèle :

 
Sélectionnez
$email = new Email;
$email->email = $request->email;
$email->save();

Ici on crée une nouvelle instance de Email. On affecte l'attribut email avec la valeur de l'entrée. Enfin on demande au modèle d'enregistrer cette ligne effectivement dans la table (save).

On ne se soucie pas des colonnes des dates (created_at et updated_at) qui sont automatiquement renseignées par Eloquent.

I-E. Les vues

On va utiliser le même tempate que dans les précédents chapitres (resources/views/template.blade.php) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
<!DOCTYPE html>
<html lang="fr">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Les emails</title>
    {!! Html::style('<a href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css</a>') !!}
    {!! Html::style('<a href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css">https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css</a>') !!}
    <!--[if lt IE 9]>
      {{ Html::style('<a href="https://oss.maxcdn.com/libs/html5shiv/3.7.2/html5shiv.js">https://oss.maxcdn.com/libs/html5shiv/3.7.2/html5shiv.js</a>') }}
      {{ Html::style('<a href="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js">https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js</a>') }}
    <![endif]-->
  </head>
  <body>
    @yield('contenu')
  </body>
</html>

Voici la vue pour le formulaire(resources/views/email.blade.php) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
@extends('template')
 
@section('contenu')
    <br>
    <div class="col-sm-offset-4 col-sm-4">
        <div class="panel panel-info">
            <div class="panel-heading">Inscription à la lettre d'information</div>
            <div class="panel-body"> 
                {!! Form::open(['route' => 'store.email']) !!}
                    <div class="form-group {!! $errors->has('email') ? 'has-error' : '' !!}">
                        {!! Form::email('email', null, array('class' => 'form-control', 'placeholder' => 'Entrez votre email')) !!}
                        {!! $errors->first('email', '<small class="help-block">:message</small>') !!}
                    </div>
                    {!! Form::submit('Envoyer !', ['class' => 'btn btn-info pull-right']) !!}
                {!! Form::close() !!}
            </div>
        </div>
    </div>
@endsection

Cette vue ne présente aucune nouveauté pour vous si ce n'est l'utilisation du nom de la route, elle répond à l'URL (avec le verbe get) : http://monsite.fr/email.

L'aspect est le suivant :

Image non disponible

Voici maintenant la vue de confirmation (resources/views/email_ok.blade.php) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
@extends('template')
 
@section('contenu')
    <br>
    <div class="col-sm-offset-3 col-sm-6">
        <div class="panel panel-info">
            <div class="panel-heading">Inscription à la lettre d'information</div>
            <div class="panel-body"> 
                Merci. Votre adresse a bien été prise en compte.
            </div>
        </div>
    </div>
@endsection

Avec cet aspect :

Image non disponible

I-F. Le fonctionnement

Voyons maintenant si tout se passe bien. Je soumets une adresse :

Image non disponible

Je reçois la confirmation :

Image non disponible

Je regarde dans la base :

Image non disponible

Je soumets la même adresse :

Image non disponible

Voyons un peu les requêtes générées par Eloquent avec par exemple la soumission de l'adresse toto@gui.com (vous les trouvez à la rubrique Queries de la barre de débogage) :

 
Sélectionnez
select count(*) as aggregate from `emails` where `email` = 'toto@gui.com'
insert into `emails` (`email`) values ('toto@gui.com')

La première requête est destinée à tester la présence éventuelle de l'adresse dans la table pour répondre à la règle « unique ». La seconde insère l'enregistrement dans la table. Vous voyez qu'Eloquent vous simplifie la tâche, vous n'avez pas besoin d'écrire les requêtes SQL, il le fait pour vous. Vous vous contentez de manipuler un objet.

N'hésitez pas à regarder les informations de la barre de débogage, vous y trouverez de précieux renseignements sur les requêtes (HTTP et SQL), les vues utilisées, les routes, les délais, les exceptions générées… Vous avez aussi un historique en cliquant sur la petite image de dossier :

Image non disponible

I-G. Organisation du code

Maintenant posons-nous à nouveau la question de l'organisation du code. Dans le contrôleur nous avons mis la gestion du modèle :

 
Sélectionnez
$email = new Email;
$email->email = $request->email;
$email->save();

Autrement dit nous avons lié de façon étroite le contrôleur et le modèle. Supposons que nous fassions des modifications dans notre base de données et que nous placions l'email dans une autre table. Nous devrions évidemment intervenir dans le code du contrôleur pour tenir compte de cette modification.

Vous pouvez évidemment considérer que c'est peu probable, que la modification du code n'est pas très importante… Mais ici on a une application très simple, dans une situation réelle les utilisations de modèles sont nombreuses et alors la question devient bien plus pertinente.

I-G-1. Première version

Dans ce cours je m'efforce de vous entraîner à prendre de bonnes habitudes. Plutôt que d'instancier directement une classe dans une autre, il vaut mieux une injection et laisser faire le conteneur. Regardez cette nouvelle version du contrôleur :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
<?php
 
namespace App\Http\Controllers;
 
use App\Email;
use App\Http\Requests\EmailRequest;
 
class EmailController extends Controller
{
    public function create()
    {
        return view('email');
    }
 
    public function store(EmailRequest $request, Email $email)
    {
        $email->email = $request->email;
        $email->save();
         
        return view('email_ok');
    }
}

Maintenant le modèle est injecté dans la méthode, c'est plus élégant et efficace. Si jamais vous changez de modèle, vous n'avez plus qu'un changement de code limité sur le contrôleur. Mais ce n'est pas encore parfait.

I-G-2. Seconde version

Dans l'idéal on veut que notre contrôleur ne soit pas du tout concerné par un changement dans la gestion des modèles. Voici une façon de procéder :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
<?php
 
namespace App\Http\Controllers;
 
use App\Http\Requests\EmailRequest;
use App\Repositories\EmailRepository;
 
class EmailController extends Controller
{
    public function create()
    {
        return view('email');
    }
 
    public function store(EmailRequest $request, EmailRepository $emailRepository)
    {
        $emailRepository->save($request->email);
         
        return view('email_ok');
    }
}

Maintenant j'injecte une classe de gestion qui possède la méthode save. Voici le contrat avec une interface (app/Repositories/EmailRepositoryInterface) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
<?php
 
namespace App\Repositories;
 
interface EmailRepositoryInterface
{
    public function save($mail);
}

Et voici la classe qui implémente cette interface (app/Repositories/EmailRepository) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
<?php
 
namespace App\Repositories;
 
use App\Email;
 
class EmailRepository implements EmailRepositoryInterface
{
    protected $email;
 
    public function __construct(Email $email)
    {
        $this->email = $email;
    }
 
    public function save($mail)
    {
        $this->email->email = $mail;
        $this->email->save();
    }
}

Le modèle est injecté dans cette classe. Je l'ai injecté dans le constructeur pour généraliser la démarche en imaginant qu'on créera d'autres méthodes que l'on peut regrouper ici pour gérer les enregistrements. Le code est maintenant parfaitement organisé, facile à modifier et à tester.

Image non disponible

Le Design Pattern Repository est un des plus répandus. Il permet de gérer la persistance des informations.

I-G-3. Troisième version

On peut enfin, comme on l'a déjà vu, référencer l'interface plutôt que la classe, mais dans ce cas il faut informer le conteneur de la dépendance. Modifiez ainsi le fichier app/Http/Providers/AppServiceProvider.php :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
public function register()
{
    $this->app->bind(
        'App\Repositories\EmailRepositoryInterface', 
        'App\Repositories\EmailRepository'
    );
}

Et le contrôleur :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
<?php
 
namespace App\Http\Controllers;
 
use App\Http\Requests\EmailRequest;
use App\Repositories\EmailRepositoryInterface;
 
class EmailController extends Controller
{
    public function create()
    {
        return view('email');
    }
 
    public function store(EmailRequest $request, EmailRepositoryInterface $emailRepository)
    {
        $emailRepository->save($request->email);
         
        return view('email_ok');
    }
}

Maintenant, étant donné que le conteneur sait quelle classe instancier à partir de l'interface passée en paramètre, vous avez un code propre et facile à maintenir et à tester. Si vous changez d'avis sur la manière de stocker les emails, il vous suffit de décider d'instancier une autre classe à partir de l'interface, tant que le contrat passé avec le contrôleur ne change pas !

I-H. En résumé

  • La base de données doit être configurée pour fonctionner avec Laravel.
  • Les migrations permettent d'intervenir sur le schéma des tables de la base.
  • Eloquent permet une représentation des tables sous forme d'objets pour simplifier les manipulations des enregistrements.
  • Il est judicieux de prévoir la gestion du modèle dans une classe injectée dans le contrôleur.
  • La barre de débogage donne de précieux renseignements sur les requêtes.

précédentsommairesuivant

Copyright © 2017 Laravel. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.