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

Tutoriel pour apprendre à utiliser le framework Laravel 4


précédentsommairesuivant

XXXV. Chapitre 35 : Les composeurs de vues

Je lisais récemment des articles sur la création de menus avec Laravel. Je n'y ai pas vraiment trouvé quelque chose pour me satisfaire. Alors je me suis penché sur la question. Les solutions possibles sont assez variées. Je me suis orienté vers un composeur de vues. Je vous donne le résultat de ma réflexion.

XXXV-A. Les composeurs de vues

Mais d'abord qu'est-ce qu'un un composeur de vue ? C'est une fonction anonyme ou une classe qui est appelée lorsqu'une vue est créée. Ça permet de préparer des données pour la vue de façon simple et systématique, et de localiser le code à un seul endroit.

Créer un composeur est facile :

 
Sélectionnez
1.
2.
3.
4.
View::composer('navigation', function($view)
{
    $view->with('menu', Menu::all());
});

Avec ce code chaque fois que la vue navigation est créée, elle reçoit la variable $menu avec les informations nécessaires.

On peut aussi utiliser une classe si on a d'autres traitements à effectuer :

 
Sélectionnez
View::composer('navigation', 'MenuComposer');

Avec une classe constituée de cette manière :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
class MenuComposer {
 
    public function compose($view)
    {
        $view->with('menu', Menu::all());
    }
}

Voilà pour la présentation générale. Venons-en maintenant au menu.

XXXV-B. Format du menu

La première question que je me suis posée est : sous quelle forme est-il judicieux de créer un menu ? Après quelques essais j'en suis venu à adopter le format JSON pour sa versatilité. Il est facile à manipuler autant en PHP qu'en JavaScript, et il est aussi très facile à transmettre.

Voici le menu qui va servir pour notre exemple :

 
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.
{
    "Accueil" : {
        "type": "url",
        "value": "/"
    },
    "A propos" : {
        "A propos de moi" : {
            "type": "route",
            "value": "apropos"
        },
        "A propos du site" : {
            "type": "route",
            "value": "aproposite"
        }
    },
    "Gallerie" : {
        "type": "url",
        "value": "gallerie"
    },
    "Liens" : {
            "Quelques liens :" : {
            "type" : "header"
        },
        "Les lieux" : {
            "type": "route",
            "value": "lieux"
        },
        "Les activités" : {
            "type": "route",
            "value": "activites",
            "state": "disabled"
        },
        "divider1" : {
            "type": "divider"
        },
        "Les ressources" : {
            "type": "route",
            "value": "ressources"
        }
    }
}

La structure est simple, on a :

  • le nom de l'item ;
  • son type (URL, route, header ou divider) ;
  • sa valeur éventuelle (pour l'URL ou la route) ;
  • son état éventuel (en fait juste s'il est désactivé).

Il suffit de mettre tout ça dans un fichier qu'on va nommer navigation.php. Mais où va-t-on le placer ?

XXXV-C. Organisation du code

Fidèle à mes habitudes j'ai créé un dossier app/Lib :

Image non disponible

On va faire le tour de tous ces fichiers. Pour que Laravel connaisse ce dossier et les classes contenues j'en informe Composer :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
"autoload": {
    "classmap": [
           ...
    ],
    "psr-0": {
        "Lib": "app"
    }  
},

Après un petit dumpautoload ça devrait aller. Pour que Laravel soit au courant que je positionne les vues dans le dossier app/Lib/views, je le lui dis dans le fichier app/config/views.php :

 
Sélectionnez
'paths' => array(__DIR__.'/../Lib/views'),

Le menu créé plus haut trouve sa place dans le dossier Lib/menus.

XXXV-D. Le composeur

Occupons-nous maintenant du composeur (Lib/composers/NavigationComposer.php) :

 
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.
<?php namespace Lib\Composers;
 
use Illuminate\Filesystem\Filesystem;
use Illuminate\View\View;
 
class NavigationComposer {
 
    protected $files;
 
    public function __construct(Filesystem $files)  {
        $this->files = $files;
    }
 
  public function compose(View $view) {
 
    $name = $view->getName();
    $fileName = app_path().'/Lib/menus/'.$name.'.php';
 
    if($this->files->exists($fileName)) {
      $file = $this->files->get($fileName);
      $items = json_decode($file, true);
      $view->items = $items;
    }
 
  }
 
}

Je m'arrange pour que la vue et le menu correspondant aient le même nom. Il me suffit donc de récupérer ce nom et de déterminer le chemin qui y mène. La fonction json_decode permet de transformer le JSON en tableau. Tant qu'à faire j'ai utilisé la classe Filesystem de Laravel pour la manipulation du fichier, plutôt que les fonctions de base de PHP. À la sortie je crée une variable $items pour la vue.

Pour que Laravel soit au courant que ce composeur existe et le relie à la vue, il faut un fournisseur de service (Lib/composers/ComposerServiceProvider.php) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
<?php namespace Lib\Composers;
  
use Illuminate\Support\ServiceProvider;
  
class ComposerServiceProvider extends ServiceProvider {
  
  public function register()
  {
    $this->app->view->composer('navigation', 'Lib\Composers\NavigationComposer');
  }
  
}

Si j'avais plusieurs menus je les déclarerais tous ici.

Et évidemment il faut déclarer le service dans app/config/app.php :

 
Sélectionnez
1.
2.
3.
4.
            ...
    'Illuminate\Workbench\WorkbenchServiceProvider',
    'Lib\Composers\ComposerServiceProvider'
),

XXXV-E. Les vues

XXXV-E-1. Le template

Il ne nous reste plus qu'à nous occuper des vues. D'abord le template (Lib/views/main.blade.php) :

 
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.
<!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>{{ trans('main.mon_joli_site') }}</title>
    {{ HTML::style('https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css') }}
    {{ HTML::style('https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css') }}
    <!--[if lt IE 9]>
      {{ HTML::style('https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js') }}
      {{ HTML::style('https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js') }}
    <![endif]-->
  </head>
  <body>
    @include('navigation')
    <div class="container">
      @yield('contenu')
    </div>
    {{ HTML::script('http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js') }}
    {{ HTML::script('http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js') }}
  </body>
</html>

XXXV-E-2. La navigation

C'est la vue (Lib/views/navigation.blade.php) qui va utiliser la variable transmise par le composeur :

 
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.
<nav class="navbar navbar-default" role="navigation">
  <div class="container">
    <div class="navbar-header">
      <a class="navbar-brand">Mon joli site</a>
    </div>
    <ul class="nav navbar-nav">
      @foreach($items as $key => $value)
        @if(isset($value['type']))
          <li {{ Request::is($value['value']) ? ' class="active"' : null }} {{ isset($value['state'])? 'class='.$v['state'] : null }} >
            @if($value['type'] == 'url')
              <a href="{{ URL::to($value['value']) }}">{{ $key }}</a>
            @else
              <a href="{{ URL::route($value['value']) }}">{{ $key }}</a>
            @endif
          </li>
        @else
          <li class="dropdown">
            <a class="dropdown-toggle" data-toggle="dropdown" href="#"> {{ $key }} <span class="caret"></span></a>
            <ul class="dropdown-menu">
              @foreach ($value as $k => $v)
                @if($v['type'] == 'header')
                  <li class="dropdown-header">{{ $k }}</li>
                @elseif($v['type'] == 'divider')
                  <li class="divider"></li>
                @else
                  <li {{ Request::is($v['value']) ? ' class="active"' : null }} {{ isset($v['state'])? 'class='.$v['state'] : null }} >
                    @if($v['type'] == 'url')
                      <a href="{{ URL::to($v['value']) }}">{{ $k }}</a>
                    @elseif($v['type'] == 'route')
                      <a href="{{ URL::route($v['value']) }}">{{ $k }}</a>
                    @endif
                  </li>
                @endif
              @endforeach
            </ul>
          </li>
        @endif
      @endforeach
    </ul>
  </div>
</nav>

Le tableau transmis est parcouru et le menu construit progressivement. J'ai utilisé Bootstrap par facilité (les classes sont toutes prêtes) mais j'aurais pu procéder différemment. Je fais un test de l'URL en cours pour rendre actif l'item correspondant.

XXXV-E-3. Les autres vues

Les autres vues sont toutes calquées sur le même modèle. Par exemple pour Lib/views/home.blade.php :

 
Sélectionnez
1.
2.
3.
4.
5.
@extends('main')
 
@section('contenu')
    Accueil
@stop

Pour mes essais j'ai juste changé le texte pour les autres vues.

XXXV-F. Les routes

Pour que tout fonctionne il ne nous manque plus que les routes :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
Route::get('/', function() { return View::make('home'); });
Route::get('apropos', array('as' => 'apropos', function() { return View::make('apropos'); }));
Route::get('aproposite', array('as' => 'aproposite', function() { return View::make('aproposite'); }));
Route::get('gallerie', function() { return View::make('gallerie'); });
Route::get('lieux', array('as' => 'lieux', function() { return View::make('lieux'); }));
Route::get('ressources', array('as' => 'ressources', function() { return View::make('ressources'); }));
Route::get('activites', array('as' => 'activites', function() { return View::make('activites'); }));

J'ai un peu mélangé les URL et les routes nommées pour les tests.

XXXV-G. Le résultat

Avec l'URL de base j'ai ce résultat :

Image non disponible

J'ai bien l'accueil actif et les sous-menus fonctionnent :

Image non disponible

Si je clique sur « Gallerie » :

Image non disponible

J'ai la bonne page qui s'affiche et l'item « Gallerie » devient actif.

L'item « Les activités » est bien désactivé comme demandé, et j'ai bien le titre et le séparateur :

Image non disponible

Finalement la structure du système est simple et fonctionnelle. Le fait d'avoir le menu en JSON permet de le créer de façon visuelle par exemple avec une interface en JavaScript.


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.