Tutoriel pour apprendre à créer une application Laravel

Des vues propres

Maurice Chavelli

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.

Commentez

- Titre : Tutoriel pour apprendre à créer une application Laravel

- Auteur : Maurice Chavelli

- Parution : 5 janvier 2018

- Licence : 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.

Sommaire

- I. FormBuilder

- I-A. Installation

- I-B. Les macros

- I-C. Étendre FormBuilder

- I-D. Les méthodes ajoutées

- I-D-1. Bouton de soumission

- I-D-2. Bouton de suppression

- I-D-3. Les contrôles

- I-D-4. Case à cocher

- I-D-5. Liste

- II. Les vues partielles

- III. Remerciements

I - FormBuilder

I-A - Installation

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

1. "require": {

2. "laravel/framework": "5.1.*",

3. "laravelcollective/html": "5.1.*"

4. },

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 :

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

1. 'providers' => [

2. // ...

3. Collective\Html\HtmlServiceProvider::class,

4. // ...

5. ],

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 :

1. 'aliases' => [

2. // ...

3. 'Form' => Collective\Html\FormFacade::class,

4. 'Html' => Collective\Html\HtmlFacade::class,

5. // ...

6. ],

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 :

1. class FormBuilder {

3. use Macroable;

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

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

1. Form::macro('specialField', function()

2. {

3. return '<input type="special">';

4. });

On peut alors l'utiliser dans une vue :

{!! 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 :

La classe FormBuilder étend celle du composant :

1. <?php namespace App\Services\Html;

3. class FormBuilder extends \Collective\Html\FormBuilder {

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

1. <?php namespace App\Services\Html;

3. class HtmlServiceProvider extends \Collective\Html\HtmlServiceProvider {

5. /**

6. * Register the form builder instance.

7. *

8. * @return void

9. */

10. protected function registerFormBuilder()

11. {

12. $this->app->bindShared('form', function($app)

13. {

14. $form = new FormBuilder($app['html'], $app['url'], $app['session.store']->getToken());

16. return $form->setSessionStore($app['session.store']);

17. });

18. }

20. }

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 :

1. 'providers' => [

2. // ...

3. App\Services\Html\HtmlServiceProvider::class,

4. // ...

5. ],

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 :

1. <div class="form-group *">

2. <input class="btn btn-default" type="submit" value="Envoyer">

3. </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 :

1. public function submit($value = null, $options = [])

2. {

3. return sprintf('

4. <div class="form-group %s">

5. %s

6. </div>',

7. empty($options) ? '' : $options[0],

8. parent::submit($value, ['class' => 'btn btn-default'])

9. );

10. }

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é :

{!! 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 :

<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 :

1. public function destroy($text, $message, $class = null)

2. {

3. return parent::submit($text, ['class' => 'btn btn-danger btn-block ' . ($class? $class:''), 'onclick' => 'return confirm(\'' . $message . '\')']);

4. }

Avec un cas d'utilisation :

{!! 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 :

1. <div class="form-group *">

2. <label class="control-label" for="*">*</label>

3. <input id="*" class="form-control" type="*" name="*" placeholder="*">

4. </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 :

1. public function control($type, $colonnes, $nom, $errors, $label = null, $valeur = null, $pop = null, $placeholder = '')

2. {

3. $attributes = ['class' => 'form-control', 'placeholder' => $placeholder];

4. return sprintf('

5. <div class="form-group %s %s">

6. %s

7. %s

8. %s

9. %s

10. </div>',

11. ($colonnes == 0)? '': 'col-lg-' . $colonnes,

12. $errors->has($nom) ? 'has-error' : '',

13. $label ? $this->label($nom, $label, ['class' => 'control-label']) : '',

14. $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>' : '',

15. call_user_func_array(['Form', $type], ($type == 'password')? [$nom, $attributes] : [$nom, $valeur, $attributes]),

16. $errors->first($nom, '<small class="help-block">:message</small>')

17. );

18. }

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

1. {!! Form::control('text', 6, 'name', $errors, trans('front/contact.name')) !!}

2. {!! Form::control('email', 6, 'email', $errors, trans('front/contact.email')) !!}

3. {!! Form::control('textarea', 12, 'message', $errors, trans('front/contact.message')) !!}

Ce qui génère ceci :

1. <div class="form-group col-lg-6 ">

2. <label class="control-label" for="name">Votre nom</label>

3. <input id="name" class="form-control" type="text" name="name" placeholder="">

4. </div>

5. <div class="form-group col-lg-6 ">

6. <label class="control-label" for="email">Votre Email</label>

7. <input id="email" class="form-control" type="email" name="email" placeholder="">

8. </div>

9. <div class="form-group col-lg-12 ">

10. <label class="control-label" for="message">Votre message</label>

11. <textarea id="message" class="form-control" rows="10" cols="50" name="message" placeholder=""></textarea>

12. </div>

I-D-4 - Case à cocher

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

1. <div class="checkbox col-lg-12">

2. <label>

3. <input type="checkbox" value="" name="*"> *

4. </label>

5. </div>

Voici la méthode :

1. public function check($name, $label)

2. {

3. return sprintf('

4. <div class="checkbox col-lg-12">

5. <label>

6. %s%s

7. </label>

8. </div>',

9. parent::checkbox($name),

10. $label

11. );

12. }

Et un cas d'utilisation :

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

I-D-5 - Liste

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

1. <select id="*" class="form-control" name="*">

2. <option value="*">*</option>

3. ...

4. </select>

Voici la méthode :

1. public function selection($nom, $list = [], $selected = null, $label = null)

2. {

3. return sprintf('

4. <div class="form-group" style="width:200px;">

5. %s

6. %s

7. </div>',

8. $label ? $this->label($nom, $label, ['class' => 'control-label']) : '',

9. parent::select($nom, $list, $selected, ['class' => 'form-control'])

10. );

11. }

Et un cas d'utilisation :

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

Avec ce code généré :

1. <select id="role" class="form-control" name="role">

2. <option value="1">Administrator</option>

3. <option value="2">Redactor</option>

4. <option selected="selected" value="3">User</option>

5. </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 :

On y trouve le code :

1. <main role="main" class="container">

2. @if(session()->has('ok'))

3. @include('partials/error', ['type' => 'success', 'message' => session('ok')])

4. @endif

5. @if(isset($info))

6. @include('partials/error', ['type' => 'info', 'message' => $info])

7. @endif

8. @yield('main')

9. </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 :

1. <div class="alert alert-{{ $type }} alert-dismissible" role="alert">

2. <button type="button" class="close" data-dismiss="alert">

3. <span aria-hidden="true">×</span>

4. <span class="sr-only">Close</span>

5. </button>

6. {!! $message !!}

7. </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 :

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 :

1. <div class="col-lg-4 col-md-6">

2. <div class="panel panel-{{ $color }}">

3. <div class="panel-heading">

4. <div class="row">

5. <div class="col-xs-3">

6. <span class="fa fa-{{ $icone }} fa-5x"></span>

7. </div>

8. <div class="col-xs-9 text-right">

9. <div class="huge">{{ $nbr['new'] }}</div>

10. <div>{{ $name }}</div>

11. </div>

12. </div>

13. </div>

14. <a href="{{ $url }}">

15. <div class="panel-footer">

16. <span class="pull-left">{{ $nbr['total'] . ' ' . $total }}</span>

17. <span class="pull-right fa fa-arrow-circle-right"></span>

18. <div class="clearfix"></div>

19. </div>

20. </a>

21. </div>

22. </div>

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

1. @extends('back.template')

3. @section('main')

5. @include('back.partials.entete', ['title' => trans('back/admin.dashboard'), 'icone' => 'dashboard', 'fil' => trans('back/admin.dashboard')])

7. <div class="row">

9. @include('back/partials/pannel', ['color' => 'primary', 'icone' => 'envelope', 'nbr' => $nbrMessages, 'name' => trans('back/admin.new-messages'), 'url' => 'contact', 'total' => trans('back/admin.messages')])

11. @include('back/partials/pannel', ['color' => 'green', 'icone' => 'user', 'nbr' => $nbrUsers, 'name' => trans('back/admin.new-registers'), 'url' => 'user', 'total' => trans('back/admin.users')])

13. @include('back/partials/pannel', ['color' => 'yellow', 'icone' => 'pencil', 'nbr' => $nbrPosts, 'name' => trans('back/admin.new-posts'), 'url' => 'blog', 'total' => trans('back/admin.posts')])

15. @include('back/partials/pannel', ['color' => 'red', 'icone' => 'comment', 'nbr' => $nbrComments, 'name' => trans('back/admin.new-comments'), 'url' => 'comment', 'total' => trans('back/admin.comments')])

17. </div>

19. @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.