Laravel

Un framework qui rend heureux

Voir cette catégorie
Vers le bas
Cours Laravel 5.5 – les vues
Mercredi 11 octobre 2017 18:48
Je vous ai déjà parlé des vues dans ce cours et de Blade et on a eu plusieurs exemples de code. Dans ce chapitre je vais faire un peu le point et montrer des possibilités intéressantes qui n'ont pas encore été évoquées. Dans mes précédents cours je mettais en avant le package Laravel Collective pour générer le HTML. C'est une possibilité élégante et performantes mais j'en suis revenu à du codage standard surtout au vu des améliorations de Blade. C'est juste une question de choix personnel et je vous invite à essayer le package en question pour voir s'il vous convient.

Les exemples de code de ce chapitre sont issus de cette application d'exemple.

Les vues générées par Blade sont en PHP et sont en cache jusqu'à ce qu'elles soient modifiées, ce qui assure de bonnes performances. Le cache se situe ici :   Si vous avez besoin de nettoyer ce cache ce n'est pas la peine d'aller supprimer ces fichiers dans le dossier, il y a une commande Artisan pour ça :
php artisan view:clear
On en a besoin parfois en cours de développement lorsqu'une vue n'est pas régénérée correctement.

L'héritage

On a vu qu'une vue peut en étendre une autre, c'est un héritage. Ainsi pour les vues du front on a un template (layout) de base : Ce template comporte la structure globale des pages et est déclaré comme parent par les autres vues :
@extends('front.layout')
Dans le template on prévoit 3 emplacements (@yield) pour que les vues enfants puissent placer leur code :
<!DOCTYPE html>

   @yield('css')

   ...
  
   @yield('main')

   ...

   @yield('scripts')

</html>
Le nom de ces emplacements est clair et on peut ainsi préciser des règles CSS, placer le code principal et ajouter du Javascript. Ainsi dans la vue front/posts.blade.php on utilise ces 3 emplacements :
@extends('front.layout')

@section('css')
    @if (Auth::check())
        <link rel="stylesheet" href="//cdn.jsdelivr.net/sweetalert2/6.3.8/sweetalert2.min.css">
    @endif
@endsection

@section('main')

   ...

@endsection

@section('scripts')

   ...

@endsection

On peut également déclarer une section dans le template avec @section lorsqu'on a du code dans le template qu'on veut soit utiliser, soit surcharger.

L'inclusion

On peut faire beaucoup de choses avec l'héritage mais il est souvent utile de pouvoir inclure une vue dans une autre, classiquement on parle de vue partielle (partial).

Les panneaux du tableau de bord

Il y a dans l'application un dossier partials pour le backend : On y trouve 4 vues partielles. La vue par défaut de l’administration (tableau de bord) est back/index.blade.php :
@extends('back.layout')

@section('main')
    @admin
        <div class="row">
            @each('back.partials.pannel', $pannels, 'pannel')
        </div>
    @endadmin
@endsection
On a peu de code dans cette vue qui étend le layout et renseigne sa section main. La directive @each permet d'itérer dans un tableau ou une collection. Ici on reçoit du contrôleur la variable $pannels qui contient les renseignements pour les panneaux d'information de l'administration : Pour chaque panneau on utilise la vue back/partials/pannel. en lui transmettant une variable $pannel :
<div class="col-lg-3 col-xs-6">
    <!-- small box -->
    <div class="small-box bg-{{ $pannel->color }}">
        <div class="inner">
            <h3>{{ $pannel->nbr }}</h3>

            <p>{{ $pannel->name }}</p>
        </div>
        <div class="icon">
            <span class="fa fa-{{ $pannel->icon }}"></span>
        </div>
        <a href="{{ $pannel->url }}" class="small-box-footer">
            @lang('More info') <span class="fa fa-arrow-circle-right"></span>
        </a>
    </div>
</div>
On a ici la mise en forme du panneau et le renseignement des éléments grâce à la variable.

Les menu latéral de l'administration

Le menu latéral de l'administration comporte des icônes, du texte et des sous-menus éventuels : Dans le layout (back/layout.blade.php) on trouve des appels à la vue partielle back.partials.treeview :
@include('back.partials.treeview', [
  'icon' => 'user',
  'type' => 'user',
  'items' => [
    [
      'route' => route('users.index'),
      'command' => 'list',
      'color' => 'blue',
    ],
    [
      'route' => route('users.index', ['new' => 'on']),
      'command' => 'new',
      'color' => 'yellow',
    ],
  ],
])
On lui transmet un tableau de valeurs (icône, texte, route, couleur...). Ces informations sont exploitées dans la vue partielle pour créer l’élément de menu :
<li class="treeview">
    <a href="#"><i class="fa fa-fw fa-{{ $icon }}"></i> <span>@lang('admin.menu.' . $type . 's')</span>
        <span class="pull-right-container">
            <span class="fa fa-angle-left pull-right"></span>
        </span>
    </a>
    <ul class="treeview-menu">
        @foreach ($items as $item)
            <li><a href="{{ $item['route'] }}"><span class="fa fa-fw fa-circle-o text-{{ $item['color'] }}"></span> <span>@lang('admin.menu.' . $item['command'])</span></a></li>
        @endforeach
    </ul>
</li>

On dispose aussi des directives @includeIf et @includeWhen.

Les composants

Comme alternative à l'inclusion on dispose des composants qui sont apparus plus récemment. Voyons de quoi il s'agit. Dans les dossiers des vues de l'application d'exemple vous trouvez pour le backend un dossier components : Voyons le code de la vue alert :
<div class="alert alert-{{ $type }} alert-dismissible">
    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
    <h4><span class="icon fa fa-check"></span>{{ $slot }}</h4>
</div>
On a le code HTML classique d'une barre d'alerte équipée d'un bouton de fermeture. Ce qui est intéressant se situe au niveau de la déclaration des classes et du texte :
class="alert alert-{{ $type }} alert-dismissible"
...
{{ $slot }}
On a deux variables :
  • $type : pour le type d'alerte (danger, succès...)
  • $slot : pour le texte de l'alerte
Voyons comment on utilise ce composant... Si vous regardez dans le vue back/users/edit.blade.php vous trouvez ce code :
@if (session('user-updated'))
    @component('back.components.alert')
        @slot('type')
            success
        @endslot
        {!! session('user-updated') !!}
    @endcomponent
@endif
On teste si la session contient une variable user-updated et si c'est le cas on affiche le composant (@component) back.components.alert en renseignant les deux variables :
  • $type : on utilise la directive $slot en précisant le nom de la variable et son contenu
  • $slot : on aura dans cette variable tout ce qui n'est pas contenu dans une directive @slot, donc dans notre cas le contenu de la variable user-updated en session
Vous pouvez regarder le code des deux autres composants qui sont basés sur le même principe.

On peut ajouter si besoin une information à transmettre au composant avec cette syntaxe :

@component('alert', ['element' => 'info'])

Cette possibilité n'a pas été utilisée dans l'application.

Les structures de contrôle

@if

La directive @if permet d'introduire une condition. Par exemple dans la vue front/contact.blade.php on trouve ce code :
@if ($errors->has('name'))
  @component('front.components.error')
    {{ $errors->first('name') }}
  @endcomponent
@endif
Cette partie du code gère l'éventuel message d'erreur de validation : On voit qu'il est fait usage d'un composant pour l'affichage de l'erreur (front/components/error.blade.php) :
<span class="red">
    <strong>{{ $slot }}</strong>
</span>
Un composant tout simple avec juste le slot.

@else

La directive @else permet de compléter la logique. Par exemple dans la vue front/pagination.blade.php :
@if ($paginator->onFirstPage())
    <span class="page-numbers prev inactive">@lang('pagination.previous')</span>
@else
    <a href="{{ $paginator->previousPageUrl() }}" class="page-numbers prev" rel="prev">@lang('pagination.previous')</a>
@endif
On a pas le même affichage selon la page.

@unless

La directive @unless est l'inverse de @if, par exemple dans la vue front/comments/comments-base.blase.php :
@unless ($comment->isLeaf())
    @php
        $level++;
    @endphp
    <ul class="children">
        @include('front/comments/comments', ['comments' => $comment->getImmediateDescendants()])
    </ul>
@endunless
On aurait pu écrire :
@if (!$comment->isLeaf())
Mais c'est moins élégant... Remarquez au passage la directive @php qui permet d'insérer du code PHP dans la vue.

@isset et @empty

La directive @isset est l'équivalent de la fonction PHP isset(). Dans la vue back/components/box.blade.php on trouve ce code :
@isset($footer)
    <div class="box-footer">
        {{ $footer }}
    </div>
@endisset
Si on a un footer on l'affiche. Il existe aussi la directive @empty équivalente de la fonction PHP empy() qui n'est pas utilisée dans l'application.

@auth et @guest

Il est fréquent d'avoir à tester si un utilisateur est connecté et on peut écrire :
@if (Auth::check())
Mais il est plus élégant d'utiliser @auth. de la même manière on dispose de @guest.

Il existe aussi la directive @switch équivalente de la directive PHP switch.

Condition personnalisée

On peut aussi se construire une condition personnalisée. Regardez ce code dans AppServiceProvider:
public function boot()
{
    ...

    Blade::if('admin', function () {
        return auth()->user()->role === 'admin';
    });

    Blade::if('redac', function () {
        return auth()->user()->role === 'redac';
    });

    Blade::if('request', function ($url) {
        return request()->is($url);
    });
}
On crée 3 directives :
  • admin : vérifie qu'un utilisateur est administrateur
  • redac : vérifie qu'un utilisateur est rédacteur
  • request : vérifie que la requête correspond à une certaine url
Du coup dans la vue front/layout.blade.php on peut écrire :
@admin
  <li>
    <a href="{{ url('admin') }}">@lang('Administration')</a>
  </li>
@endadmin
@redac
  <li>
    <a href="{{ url('admin/posts') }}">@lang('Administration')</a>
  </li>
@endredac
Et dans la même vue :
@request('password/reset')
  <li class="current">
    <a href="{{ request()->url() }}">@lang('Password')</a>
  </li>
@endrequest
@request('password/reset/*')
  <li class="current">
    <a href="{{ request()->url() }}">@lang('Password')</a>
  </li>
@endrequest

Les boucles

Pour les boucles on dispose de @for, @foreach, @each, @forelse et @while. La logique est la même que les directives équivalentes de PHP. Voyons un exemple dans la vue back/layout.blade.php :
@foreach ($breadcrumbs as $item)
  <li @if ($loop->last && $item['url'] === '#') class="active" @endif>
    @if ($item['url'] !== '#')
      <a href="{{ $item['url'] }}">
    @endif
    @isset($item['icon'])
      <span class="fa fa-{{ $item['icon'] }}"></span>
    @endisset
    {{ $item['name'] }}
    @if ($item['url'] !== '#')
      </a>
    @endif
  </li>
@endforeach
C'est la partie du code qui gère le breadcrumb : Avec @foreach on itère dans les éléments de la variables @breadcrumbs. Avec la directive @foreach on dispose de la variable interne $loop qui offre des informations sur l'index en cours. Voici tout ce qui est disponible :
Propriété Description
$loop->index L'index en cours (commence à 0)
$loop->iteration L'itération courante (commence à 1)
$loop->remaining Les itérations restantes
$loop->count Le nombre total d'itérations
$loop->first Indique si c'est le premier élément
$loop->last Indique si c'est le dernier élément
$loop->depth Le niveau d'imbrication actuel
$loop->parent La variable d'itération parente si imbrication
Dans notre code on vérifie si on a le dernier élément avec $loop->last. On utilise alors quelques directives qu'on a déjà vues.

L'injection de service

Normalement on envoie à une vue toutes les informations nécessaires à partir du contrôleur mais parfois on a besoin d'un service spécifique, on peut alors utiliser la directive @inject. Vous pouvez trouver un exemple dans la vue back/settings.blade.php :
@inject('envRepository', 'App\Repositories\EnvRepository')
Le service est fourni par la classe App\Repositories\EnvRepository (c'est un repository pour interagir avec le fichier .env) dont une instance va se trouver dans la variable $envRepository. On peut ainsi écrire ensuite dans la vue ce code :
$envRepository->get('APP_URL')
Ici on peut récupérer la valeur pour l'élément APP_URL du fichier .env. Elle est ainsi affichée dans les réglages :

Les composeurs de vues (view composers)

Les composeurs de vues sont des fonctions de rappel ou des classes qui sont appelée lorsqu'une vue est générée, ce qui permet de localiser du code. Voyons une exemple dans l'application. Regardez ce code dans AppServiceProvider (on aurait pu créer un provider spécifique) :
public function boot()
{
    ...

    view()->composer('front.layout',MenuComposer::class);

    view()->composer('back.layout',HeaderComposer::class);

    ...
}
Avec la première ligne on dit que lorsque la vue front/layout.blade.php est générée on doit appeler la classe App\Http\ViewComposers\MenuComposer. Les classes pour les composeurs de vues sont toutes rangées dans ce dossier : Voyons cette classe :
<?php

namespace App\Http\ViewComposers;

use Illuminate\View\View;
use App\Models\Category;

class MenuComposer
{
    /**
     * Bind data to the view.
     *
     * @param  View  $view
     * @return void
     */
    public function compose(View $view)
    {
        $view->with('categories', Category::select('title', 'slug')->get());
    }
}
On voit qu'on envoie (with) dans la vue une variable $categories avec les titres des catégories. C'est pour renseigner ce sous-menu : On a vu que la vue front/layout.blade.php est le template de base pour toutes les vues du frontend. Si on ne passait pas par l'intermédiaire d'un composeur de vue il faudrait envoyer les catégories dans toutes les vues ! Ici on a le code en un seul endroit. C'est la même logique pour la classe HeaderComposer pour le template du backend, elle est juste un peu plus chargée parce qu'on doit gérer : le breadcrumb, les titres et les notifications.

En résumé

Avec Blade on peut :
  • utiliser l'héritage de vue,
  • inclure une vue,
  • utiliser un composant,
  • utiliser de nombreuses directives pour organiser le code,
  • utiliser des boucles,
  • injecter un service.
D'autre part les composeurs de vues permettent de localiser du code facilement.


Par bestmomo

Aucun commentaire