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.