Tutoriel pour apprendre à créer une application avec Laravel 5

Les données

Ce tutoriel initie une nouvelle série sur la version 5 de Laravel qui va décrire le processus de création d'une application en permettant ainsi de voir les principales étapes et les éléments essentiels de ce framework. Ce n'est pas à proprement parler une initiation progressive mais plutôt une approche transversale permettant d'ordonner les connaissances et de les voir en œuvre dans une application concrète.

1 commentaire Donner une note à l'article (5)

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

L'application que je vous propose de réaliser existe déjà et est disponible sur Github. Il vous est donc possible de la charger et la tester. La procédure d'installation est décrite et ne devrait présenter aucune difficulté. Je vous conseille d'ailleurs de commencer par là et de parcourir l'application pour en découvrir les possibilités. Globalement c'est un système de blog avec authentification, rôles, gestion des utilisateurs, gestion des médias… C'est-à-dire quelque chose d'assez classique mais de suffisamment approfondi pour révéler les principaux aspects du framework.

Mon parti-pris a été de n'utiliser aucun package supplémentaire autre que celui qui permet de faciliter l'écriture des vues pour les formulaires. Ce package était d'ailleurs inclus dans la précédente version de Laravel. Il a été exclu de la nouvelle version parce que celle-ci se présente comme versatile et pas forcément destinée à gérer un « frontend ».

Dans ce tutoriel je vais commencer par présenter la gestion de données mise en place pour l'application.

I-A. Le schéma

L'établissement du schéma des données est certainement la première chose à faire lorsque l'on crée une application.

Voici le schéma global de la base de données :

Image non disponible

Il comporte huit tables :

  • users : table des utilisateurs :

    • username : le pseudo,
    • email : l'email,
    • password : mot de passe,
    • seen : un booléen pour savoir si le nouvel utilisateur a été vu par l'administrateur,
    • valid : un booléen pour savoir si l'utilisateur a été validé pour laisser des commentaires,
    • role_id : c'est la clé étrangère pour connaître le rôle de l'utilisateur,
    • confirmed : un booléen pour la confirmation de l'adresse email,
    • confirmation_code : un code pour authentifier la confirmation de l'adresse email ;
  • roles : table des rôles des utilisateurs :

    • title : titre du rôle tel qu'il s'affiche,
    • slug : titre du rôle tel qu'il est utilisé dans le code ;
  • password_reset : table spécifique de l'authentification pour la réinitialisation des mots de passe ;
  • contacts : table pour mémoriser les messages des visiteurs :

    • name : nom,
    • email : email,
    • text : message ;
  • posts : table des articles :

    • title : titre,
    • slug : titre formaté pour être inclus dans les URL,
    • summary : sommaire,
    • content : contenu,
    • seen : un booléen pour savoir si le nouvel article a été vu par l'administrateur,
    • active : un booléen pour savoir si l'article est publié,
    • user_id : c'est la clé étrangère pour connaître l'auteur de l'article ;
  • comments : table des commentaires des articles :

    • content : le commentaire,
    • seen : un booléen pour savoir si le nouveau commentaire a été vu par l'administrateur,
    • user_id : c'est la clé étrangère pour connaître l'auteur du commentaire,
    • post_id : c'est la clé étrangère pour connaître l'article associé ;
  • tags : table des mots clés pour les articles :

    • tag : mot clé ;
  • post_tags : table pivot pour relier les tags aux articles :

    • post_id : c'est la clé étrangère pour connaître l'article associé,
    • tag_id : c'est la clé étrangère pour connaître le tag associé.

Évidemment les données auraient pu être organisées de bien d'autres manières mais j'ai voulu quelque chose de relativement simple et cohérent.

En particulier j'aurais pu créer une table spécifique pour regrouper les champs seen. Il aurait alors fallu prévoir une relation polymorphique avec les tables concernées. Le tout aurait été sans doute plus « propre », mais aurait un peu compliqué la gestion.

I-B. Migrations et population

I-B-1. Les migrations

Laravel utilise un outil de migration qui simplifie les opérations sur la base de données et permet le versioning. Les migrations se trouvent dans le dossier database/migrations :

Image non disponible

Classiquement on crée un fichier de migration par opération élémentaire : création d'une table, modification d'une table, création d'un index…

Ici j'ai créé autant de migrations que de tables. D'autre part, il y a un fichier spécifique pour la clé étrangère des utilisateurs.

Voyons un de ces fichiers, par exemple celui pour la création de la table des articles :

 
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.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
<?php
 
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
 
class CreatePostsTable extends Migration {
 
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function(Blueprint $table) {
            $table->increments('id');
            $table->timestamps();
            $table->string('title', 255);
            $table->string('slug', 255)->unique();
            $table->text('summary');
            $table->text('content');
            $table->boolean('seen')->default(false);
            $table->boolean('active')->default(false);
            $table->integer('user_id')->unsigned();
        });
 
        Schema::table('posts', function(Blueprint $table) {
            $table->foreign('user_id')->references('id')->on('users')
                        ->onDelete('restrict')
                        ->onUpdate('restrict');
        });
    }
 
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('posts', function(Blueprint $table) {
            $table->dropForeign('posts_user_id_foreign');
        });     
 
        Schema::drop('posts');
    }
 
}

La fonction up est destinée à créer quelque chose dans la base, la fonction down est destinée à faire l'opération inverse.

Lorsque l'on effectue la commande php artisan migrate la fonction up est appelée. La table posts est créée dans la base avec tous les champs décrits :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
Schema::create('posts', function(Blueprint $table) {
    $table->increments('id');
    $table->timestamps();
    $table->string('title', 255);
    $table->string('slug', 255)->unique();
    $table->text('summary');
    $table->text('content');
    $table->boolean('seen')->default(false);
    $table->boolean('active')->default(false);
    $table->integer('user_id')->unsigned();
});

Est aussi déclarée, la clé étrangère :

 
Sélectionnez
1.
2.
3.
4.
5.
Schema::table('posts', function(Blueprint $table) {
    $table->foreign('user_id')->references('id')->on('users')
                ->onDelete('restrict')
                ->onUpdate('restrict');
});

I-B-2. La population

Pour que l'application fonctionne on a besoin d'enregistrements. C'est le travail de la population (seed) d'en créer. Le fichier correspondant est placé dans le dossier database/seeds :

Image non disponible

Je n'insiste pas sur cet aspect un peu accessoire. Vous pouvez aller voir dans le fichier comment c'est réalisé.

On appelle ce fichier avec la commande php artisan db:seed.

Il est possible de faire d'un seul coup la migration et la population avec la commande php artisan migrate -seed.

I-C. Les relations

Il y a un certain nombre de relations entre les tables.

I-C-1. Has Many

On a une relation has many entre les utilisateurs et les articles :

Image non disponible

C'est la clé étrangère user_id dans la table posts qui crée la relation.

Il y a aussi une relation has many entre les utilisateurs et les commentaires :

Image non disponible

C'est la clé étrangère user_id dans la table comments qui crée la relation.

On a une troisième relation has many entre les rôles et les utilisateurs :

Image non disponible

C'est la clé étrangère role_id dans la table users qui crée la relation.

On a une dernière relation has many entre les articles et les commentaires :

Image non disponible

C'est la clé étrangère post_id dans la table comments qui crée la relation.

On trouve dans les modèles, du côté « 1 » de la relation, la fonction hasMany. Par exemple, dans le modèle User :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
/**
 * One to Many relation
 *
 * @return Illuminate\Database\Eloquent\Relations\hasMany
 */
public function posts() 
{
  return $this->hasMany('App\Models\Post');
}
 
/**
 * One to Many relation
 *
 * @return Illuminate\Database\Eloquent\Relations\hasMany
 */
public function comments() 
{
  return $this->hasMany('App\Models\Comment');
}

On trouve dans les modèles, du côté « n » de la relation, la fonction belongsTo. Comme par exemple, dans le modèle Comment :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
/**
 * One to Many relation
 *
 * @return Illuminate\Database\Eloquent\Relations\BelongsTo
 */
public function user() 
{
    return $this->belongsTo('App\Models\User');
}
 
/**
 * One to Many relation
 *
 * @return Illuminate\Database\Eloquent\Relations\BelongsTo
 */
public function post() 
{
    return $this->belongsTo('App\Models\Post');
}

I-C-2. Belongs to Many

On a une relation belongs to many entre les articles et les tags :

Image non disponible

On trouve dans la table pivot post_tag les deux clés étrangères post_id et tag_id.

On a dans les deux modèles impliqués dans la relation la fonction belongsToMany, comme par exemple, dans le modèle Post :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
/**
 * Many to Many relation
 *
 * @return Illuminate\Database\Eloquent\Relations\belongToMany
 */
public function tags()
{
    return $this->belongsToMany('App\Models\Tag');
}

I-D. Les rôles

Il est prévu trois rôles dans la table roles :

Image non disponible

L'administrateur a tous les droits dans l'application, le rédacteur peut rédiger des articles, l'utilisateur ne peut qu'ajouter des commentaires aux articles.

Dans le modèle User figurent deux fonctions qui ont pour objet de déterminer le rôle de l'utilisateur connecté :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
/**
 * Check admin role
 *
 * @return bool
 */
public function isAdmin()
{
    return $this->role->slug == 'admin';
}
 
/**
 * Check not user role
 *
 * @return bool
 */
public function isNotUser()
{
    return $this->role->slug != 'user';
}

La première fonction détermine si c'est un administrateur et la seconde que ce n'est pas un simple utilisateur (dans ce cas c'est donc soit un administrateur, soit un rédacteur). On aura besoin de ces méthodes dans plusieurs endroits de l'application.

I-E. Le presenter

Vous trouverez dans les modèles Comment, Contact et Post la référence du trait DatePresenter. Par exemple, dans le modèle Comment :

 
Sélectionnez
class Comment extends Model  {
 
    use DatePresenter;

Vous trouvez ce trait dans le dossier app/Presenters :

 
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.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
<?php namespace App\Presenters;
 
use Carbon\Carbon;
 
trait DatePresenter {
 
    /**
     * Format created_at attribute
     *
     * @param Carbon  $date
     * @return string
     */
    public function getCreatedAtAttribute($date)
    {
        return $this->getDateFormated($date);
    }
 
    /**
     * Format updated_at attribute
     *
     * @param Carbon  $date
     * @return string
     */
    public function getUpdatedAtAttribute($date)
    {
        return $this->getDateFormated($date);
    }
 
    /**
     * Format date
     *
     * @param Carbon  $date
     * @return string
     */
    private function getDateFormated($date)
    {
        return Carbon::parse($date)->format(config('app.locale') == 'fr' ? 'd/m/Y' : 'm/d/Y');
    }
 
}

Il y a deux « accessor » qui ont pour objet de formater la date récupérée dans les champs created_at et updated_at. Ce format est défini en fonction de la localisation (on verra cet aspect dans un tutoriel ultérieur).

Ainsi on n'aura pas à se préoccuper du format des dates, elles seront automatiquement bien formées.

II. Remerciements

Nous tenons à remercier Maurice Chavelli qui nous a autorisés à publier ce tutoriel.

Nous remercions également Winjerome pour la gabarisation et Jacques_jean pour la relecture orthographique.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2017 Laravel. Aucune reproduction, même partielle, ne peut être faite de ce site et 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.