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

Tutoriel pour apprendre à créer une application Laravel

Des vues propres

Lorsqu'on crée une application, on a finalement pas mal de travail pour créer les vues. Même en utilisant un framewok CSS comme Bootstrap, on se retrouve à écrire de nombreuses lignes de code. Et on a du mal à respecter la doctrine DRY (Dont Repeat Yourself).

La version 4 de Laravel comportait le composant Illuminate\Html. Ce composant existe toujours, mais il n'est plus chargé par défaut, Laravel pouvant servir à autre chose, par exemple créer une API. D'autre part ce composant n'évoluera plus. Son successeur est laravelcollective/html. Vous pouvez trouver sa documentation ici.

Ce tutoriel est destiné à montrer comment ce composant a été utilisé et étendu dans l'application.

9 commentaires Donner une note à l´article (5)

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. FormBuilder

I-A. Installation

L'installation de ce composant se fait de façon classique à partir de composer.json :

 
Sélectionnez
1.
2.
3.
4.
"require": {
    "laravel/framework": "5.1.*",
    "laravelcollective/html": "5.1.*"
},

Il suffit ensuite de faire un composer update pour charger le composant et mettre à jour le chargement automatique des classes. On a alors le composant présent dans le dossier vendor :

Image non disponible

Il faut aussi prévoir de signaler à Laravel que ce composant existe dans config/app.php :

 
Sélectionnez
1.
2.
3.
4.
5.
'providers' => [
  // ...
  Collective\Html\HtmlServiceProvider::class,
  // ...
],

Mais vous ne trouverez pas cette ligne de code dans l'application pour une raison que nous allons voir au prochain chapitre.

Il faut aussi enregistrer les façades :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
'aliases' => [
  // ...
    'Form' => Collective\Html\FormFacade::class,
    'Html' => Collective\Html\HtmlFacade::class,
  // ...
],

Avec tout ça, le composant est directement utilisable dans les vues.

I-B. Les macros

La façon la plus classique d'étendre les possibilités des classes FormBuilder et HtmlBuilder est d'utiliser des macros.

En effet si on regarde la classe FormBuilder par exemple :

 
Sélectionnez
1.
2.
3.
class FormBuilder {
 
    use Macroable;

On trouve la déclaration du trait Macroable. Ce trait fait partie de Laravel :

Image non disponible

Il donne la possibilité de créer des macros pour la classe concernée. Par exemple :

 
Sélectionnez
1.
2.
3.
4.
Form::macro('specialField', function()
{
  return '<input type="special">';
});

On peut alors l'utiliser dans une vue :

 
Sélectionnez
{!! Form::specialField() !!}

J'ai choisi de ne pas utiliser cette possibilité, mais de plutôt étendre la classe FormBuilder.

I-C. Étendre FormBuilder

Vous trouverez dans les services le dossier Html :

Image non disponible

La classe FormBuilder étend celle du composant :

 
Sélectionnez
1.
2.
3.
<?php namespace App\Services\Html;
 
class FormBuilder extends \Collective\Html\FormBuilder {

L'initialisation se fait dans le provider HtmlServiceProvider qui étend lui aussi le provider du composant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
<?php namespace App\Services\Html;
 
class HtmlServiceProvider extends \Collective\Html\HtmlServiceProvider {
 
    /**
     * Register the form builder instance.
     *
     * @return void
     */
    protected function registerFormBuilder()
    {
        $this->app->bindShared('form', function($app)
        {
            $form = new FormBuilder($app['html'], $app['url'], $app['session.store']->getToken());
 
            return $form->setSessionStore($app['session.store']);
        });
    }
 
}

En effet, on veut seulement surcharger la méthode registerFormBuilder pour que le conteneur de Laravel instancie la classe du service au lieu de celle du composant.

Il faut aussi prévoir de signaler à Laravel que ce composant existe dans config/app.php :

 
Sélectionnez
1.
2.
3.
4.
5.
'providers' => [
  // ...
  App\Services\Html\HtmlServiceProvider::class,
  // ...
],

L'intendance est ainsi en place et il ne reste plus qu'à coder la classe et l'utiliser.

I-D. Les méthodes ajoutées

I-D-1. Bouton de soumission

Comme il y a de nombreux formulaires, on retrouve autant de fois le bouton de soumission. Son code de base est celui-ci :

 
Sélectionnez
1.
2.
3.
<div class="form-group *">
    <input class="btn btn-default" type="submit" value="Envoyer">
</div>

L'astérisque représente une classe de la grille de Bootstrap. Voici en conséquence le code de la méthode pour générer le bouton :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
public function submit($value = null, $options = [])
{
    return sprintf('
        <div class="form-group %s">
            %s
        </div>',
        empty($options) ? '' : $options[0],
        parent::submit($value, ['class' => 'btn btn-default'])
    );
}

Le deuxième paramètre est un tableau pour correspondre à la signature de la méthode de la classe parente.

Voici un cas d'utilisation avec le paramètre renseigné :

 
Sélectionnez
{!! Form::submit(trans('front/form.send'), ['col-lg-12']) !!}

I-D-2. Bouton de suppression

On a aussi plusieurs fois la présence d'un bouton de suppression avec ce code :

 
Sélectionnez
<input class="btn btn-danger btn-block " type="submit" value="*" onclick="return confirm(*)">

Les astérisques représentent les valeurs variables. Voici le code de la méthode :

 
Sélectionnez
1.
2.
3.
4.
public function destroy($text, $message, $class = null)
{
    return parent::submit($text, ['class' => 'btn btn-danger btn-block ' . ($class? $class:''), 'onclick' => 'return confirm(\'' . $message . '\')']);
}

Avec un cas d'utilisation :

 
Sélectionnez
{!! Form::destroy(trans('back/blog.destroy'), trans('back/blog.destroy-warning')) !!}

I-D-3. Les contrôles

Il y a de nombreux contrôles utilisés dans les formulaires de l'application. Ils ont tous la même structure :

 
Sélectionnez
1.
2.
3.
4.
<div class="form-group *">
    <label class="control-label" for="*">*</label>
    <input id="*" class="form-control" type="*" name="*" placeholder="*">
</div>

Cette fois on a pas mal d'astérisques parce que les possibilités sont très variées. Voici la méthode correspondante :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
public function control($type, $colonnes, $nom, $errors, $label = null, $valeur = null, $pop = null, $placeholder = '')
{
    $attributes = ['class' => 'form-control', 'placeholder' => $placeholder];
    return sprintf('
        <div class="form-group %s %s">
            %s
            %s
            %s
            %s
        </div>',
        ($colonnes == 0)? '': 'col-lg-' . $colonnes,
        $errors->has($nom) ? 'has-error' : '',
        $label ? $this->label($nom, $label, ['class' => 'control-label']) : '',
        $pop? '<a href="#" tabindex="0" class="badge pull-right" data-toggle="popover" data-trigger="focus" title="' . $pop[0] .'" data-content="' . $pop[1] . '"><span>?</span></a>' : '',
        call_user_func_array(['Form', $type], ($type == 'password')? [$nom, $attributes] : [$nom, $valeur, $attributes]),
        $errors->first($nom, '<small class="help-block">:message</small>')
    );
}

Ça permet des vues très épurées, par exemple :

 
Sélectionnez
1.
2.
3.
{!! Form::control('text', 6, 'name', $errors, trans('front/contact.name')) !!}
{!! Form::control('email', 6, 'email', $errors, trans('front/contact.email')) !!}
{!! Form::control('textarea', 12, 'message', $errors, trans('front/contact.message')) !!}

Ce qui génère ceci :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
<div class="form-group col-lg-6 ">
    <label class="control-label" for="name">Votre nom</label>
    <input id="name" class="form-control" type="text" name="name" placeholder="">
</div>
<div class="form-group col-lg-6 ">
    <label class="control-label" for="email">Votre Email</label>
    <input id="email" class="form-control" type="email" name="email" placeholder="">
</div>
<div class="form-group col-lg-12 ">
    <label class="control-label" for="message">Votre message</label>
    <textarea id="message" class="form-control" rows="10" cols="50" name="message" placeholder=""></textarea>
</div>

I-D-4. Case à cocher

On a aussi quelques fois une case à cocher avec ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
<div class="checkbox col-lg-12">
    <label>
        <input type="checkbox" value="" name="*"> *
    </label>
</div>

Voici la méthode :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
public function check($name, $label)
{
    return sprintf('
        <div class="checkbox col-lg-12">
            <label>
                %s%s
            </label>
        </div>',
        parent::checkbox($name),
        $label
    );        
}

Et un cas d'utilisation :

 
Sélectionnez
{!! Form::check('memory', trans('front/login.remind')) !!}

I-D-5. Liste

On a aussi des listes avec du code dans ce genre :

 
Sélectionnez
1.
2.
3.
4.
<select id="*" class="form-control" name="*">
    <option value="*">*</option>
    ...
</select>

Voici la méthode :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
public function selection($nom, $list = [], $selected = null, $label = null)
{
    return sprintf('
        <div class="form-group" style="width:200px;">
            %s
            %s
        </div>',
        $label ? $this->label($nom, $label, ['class' => 'control-label']) : '',
        parent::select($nom, $list, $selected, ['class' => 'form-control'])
    );
}

Et un cas d'utilisation :

 
Sélectionnez
{!! Form::selection('role', $select, $user->role_id, trans('back/users.role')) !!}

Avec ce code généré :

 
Sélectionnez
1.
2.
3.
4.
5.
<select id="role" class="form-control" name="role">
    <option value="1">Administrator</option>
    <option value="2">Redactor</option>
    <option selected="selected" value="3">User</option>
</select>

Toutes les méthodes créées pourraient évidemment l'être différemment. Le seul but est de fournir un exemple de réalisation.

II. Les vues partielles

Il est souvent judicieux pour éviter les répétitions de code d'utiliser des vues partielles. Regardez le template du front-end :

Image non disponible

On y trouve le code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
<main role="main" class="container">
    @if(session()->has('ok'))
        @include('partials/error', ['type' => 'success', 'message' => session('ok')])
    @endif  
    @if(isset($info))
        @include('partials/error', ['type' => 'info', 'message' => $info])
    @endif
    @yield('main')
</main>

On voit que si on veut afficher une barre d'erreur, on appelle la vue partielle partials/error en lui transmettant des variables. Voici cette vue partielle :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
<div class="alert alert-{{ $type }} alert-dismissible" role="alert">
    <button type="button" class="close" data-dismiss="alert">
        <span aria-hidden="true">×</span>
        <span class="sr-only">Close</span>
    </button>
    {!! $message !!}
</div>

On évite ainsi de répéter le code de mise en forme de la barre de Bootstrap.

On a aussi deux vues partielles dans le back-end :

Image non disponible

Le principe est le même. Par exemple la vue partielle pannel.blade.php est destinée à mettre en forme un petit panneau de l'administration :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
<div class="col-lg-4 col-md-6">
    <div class="panel panel-{{ $color }}">
        <div class="panel-heading">
            <div class="row">
                <div class="col-xs-3">
                    <span class="fa fa-{{ $icone }} fa-5x"></span>
                </div>
                <div class="col-xs-9 text-right">
                <div class="huge">{{ $nbr['new'] }}</div>
                <div>{{ $name }}</div>
                </div>
            </div>
        </div>
        <a href="{{ $url }}">
        <div class="panel-footer">
            <span class="pull-left">{{ $nbr['total'] . ' ' . $total }}</span>
            <span class="pull-right fa fa-arrow-circle-right"></span>
            <div class="clearfix"></div>
        </div>
        </a>
    </div>
</div>

Ainsi avec les deux vues partielles la vue index de l'administration est légère et lisible :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
@extends('back.template')
 
@section('main')
 
    @include('back.partials.entete', ['title' => trans('back/admin.dashboard'), 'icone' => 'dashboard', 'fil' => trans('back/admin.dashboard')])
 
    <div class="row">
 
        @include('back/partials/pannel', ['color' => 'primary', 'icone' => 'envelope', 'nbr' => $nbrMessages, 'name' => trans('back/admin.new-messages'), 'url' => 'contact', 'total' => trans('back/admin.messages')])
 
        @include('back/partials/pannel', ['color' => 'green', 'icone' => 'user', 'nbr' => $nbrUsers, 'name' => trans('back/admin.new-registers'), 'url' => 'user', 'total' => trans('back/admin.users')])
 
        @include('back/partials/pannel', ['color' => 'yellow', 'icone' => 'pencil', 'nbr' => $nbrPosts, 'name' => trans('back/admin.new-posts'), 'url' => 'blog', 'total' => trans('back/admin.posts')])
 
        @include('back/partials/pannel', ['color' => 'red', 'icone' => 'comment', 'nbr' => $nbrComments, 'name' => trans('back/admin.new-comments'), 'url' => 'comment', 'total' => trans('back/admin.comments')])
 
    </div>
 
@stop

Organiser ainsi les vues n'est pas forcément facile parce que les combinaisons sont nombreuses. Il faut s'efforcer de faire le plus simple possible.

III. Remerciements

Nous remercions Maurice Chavelli qui nous autorise à publier ce tutoriel.

Nous tenons également à remercier Winjerome pour la mise au gabarit et Claude Leloup pour la correction 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 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.