Laravel

Un framework qui rend heureux

Voir cette catégorie
Vers le bas
Shopping : la maintenance
Mardi 26 mai 2020 15:07

Pour toute application on a forcément à faire de la maintenance : évolution du code, mises à jour... Il faut pouvoir rendre le site inaccessible par moment pour effectuer des changements impactants mais il faut quand même pouvoir accéder au site en tant qu'administrateur. D'autre part mettre en cache certaines choses (les routes et la configuration) améliore les performances. Nous allons nous occuper de ça dans cet article.

Vous pouvez télécharger un ZIP du projet ici.

Le mode maintenance

Laravel permet de mettre une application en mode maintenance avec une commande :
php artisan down
On tombe alors sur une page avec ce message : On ne peut pas vraiment dire que c'est explicite et en plus comment maintenant accéder au site ? On revient en mode normal avec cette commande :
php artisan up
On peut aussi préciser le texte de la page :
php artisan down --message="Site en maintenance. Un peu de patience"
On peut ajouter une adresse IP autorisée :
php artisan down --message="Site en maintenance. Un peu de patience" --allow=127.0.0.1

On peut aussi complètement changer le look de la page d'erreur en créant une page resources/views/errors/503.blade.php.

Mais toutes ces commande avec Artisan ne sont pas pratique, et il faut un accès SSH, alors on va faciliter tout ça avec une interface propre.

Contrôleur et route

On crée un nouveau contrôleur :
php artisan make:controller Back\MaintenanceController --resource
On ne garde que les méthodes edit et update. On ajoute les deux routes :
Route::prefix('admin')->middleware('admin')->namespace('Back')->group(function () {
    ...
    Route::name('maintenance.edit')->get('maintenance/modification', 'MaintenanceController@edit');
    Route::name('maintenance.update')->put('maintenance', 'MaintenanceController@update');
});
On code la méthode edit :
public function edit(Request $request)
{
    $active = app()->isDownForMaintenance();
    $ip = $request->ip();
    $message = config('messages.maintenance');     

    return view('back.maintenance.edit', compact('active', 'ip', 'message'));
}
On fait plusieurs choses :
  • on vérifie si le site est en mode maintenance
  • on lit l'adresse IP
  • on prépare le message à afficher sur la page de maintenance
On ajoute le message dans config.messages (en anticipant pour les messages de mise à jour) :
return [
    ...
    'maintenance' => 'La boutique est en mode maintenance.',
    'maintenanceupdated' => 'Le mode maintenance a bien été mis à jour.',
    'cacheupdated' => 'Le cache a bien été mis à jour.',
];

Un composant pour la vue

On crée un nouveau composant :
php artisan make:component CheckboxCustom
<?php

namespace App\View\Components;

use Illuminate\View\Component;

class CheckboxCustom extends Component
{
    public $name;
    public $label;
    public $value;
    public $off;
    public $on;

    /**
     * Create a new component instance.
     *
     * @return void
     */
    public function __construct($name, $label, $off, $on, $value = '')
    {
        $this->name = $name;
        $this->label = $label;
        $this->off = $off;
        $this->on = $on;
        $this->value = $value;
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\View\View|string
     */
    public function render()
    {
        return view('components.checkbox-custom');
    }
}
<div class="form-group">
  <div class="custom-control custom-switch custom-switch-off-{{ $off }} custom-switch-on-{{ $on }}">
    <input type="checkbox" class="custom-control-input" 
      id="{{ $name }}" 
      name="{{ $name }}" 
      @if(old($name, $value)) checked @endif>
    <label class="custom-control-label" for="{{ $name }}">{{ $label }}</label>
  </div>
</div>

La vue du formulaire

On crée une vue pour le formulaire :
@extends('back.layout')

@section('main') 
  <div class="container-fluid"> 
    @if(session()->has('alert'))
      <div class="alert alert-warning alert-dismissible fade show" role="alert">
        {{ session('alert') }}
        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
    @endif
    <div class="row">
      <div class="col-sm-12">
        <div class="card">
          <h5 class="card-header">Mode maintenance</h5>
        
          <form method="POST" action="{{ route('maintenance.update') }}">
            <div class="card-body">
              @method('PUT')
              @csrf

              <x-inputbs4
                name="ip"
                type="text"
                label="Adresse IP autorisée"
                :value="$ip"
                :required="true"
              ></x-inputbs4>

              <x-inputbs4
                name="message"
                type="text"
                label="Message à afficher"
                :value="$message"
                :required="true"
              ></x-inputbs4>

              <x-checkbox-custom
                name="active"
                label="Mode maintenance"
                off="success"
                on="danger"
                :value="$active"
              ></x-checkbox-custom>

              <div class="form-group row mb-0">
                <div class="col-md-12">
                   <button type="submit" class="btn btn-primary">Enregistrer</button>
                </div>
              </div>
              
            </div>            
          </form>

        </div>
      </div>
    </div>
  </div>
@endsection
On complète config.titles pour le titre de la page :
return [

    ...

    'maintenance' => [
        'edit' => 'Maintenance',
    ],
];
Et on obtient le formulaire avec l'url .../admin/maintenance/modification :

La mise à jour

Pour la mise à jour des données on code la méthode update du contrôleur :
use Illuminate\Support\Facades\Artisan;

...

public function update(Request $request)
{
    $request->validate([
        'ip' => 'required|ip',
        'message' => 'required|string|max:255',
    ]);

    Artisan::call($request->has('active') ? 'down --allow=' . $request->ip . ' --message="' . $request->message . '"' : 'up');

    return back()->withInput()->with('alert', config('messages.maintenanceupdated'));
}

On fait la validation pour mes deux entrées. On utilise Artisan pour la mise à jour. Au retour on affiche un message :

La mise en cache

On va maintenant s'occuper de la mise en cache des routes et de la configuration. Là aussi on dispose des commandes config:cache et route: cache avec leur réciproque config:clear et route:clear. Mais on va faire quelque chose de plus convivial.

Dans la méthode edit du contrôleur on ajoute des données pour le cache :
public function edit(Request $request)
{
    $active = app()->isDownForMaintenance();
    $ip = $request->ip();
    $message = config('messages.maintenance');

    $path = base_path('bootstrap/cache/');        
    $config = file_exists($path . 'config.php');
    $route = file_exists($path . 'routes-v7.php');

    return view('back.maintenance.edit', compact('active', 'ip', 'message', 'config', 'route'));
}
Les fichiers du cache sont ici : Leur présence indique que le cache est actif, c'est ce qu'on vérifie dans la méthode. On crée la méthode cache dans le contrôleur pour la mise à jour :
public function cache(Request $request)
{
    Artisan::call($request->has('config') ? 'config:cache' : 'config:clear');
    Artisan::call($request->has('route') ? 'route:cache' : 'route:clear');

    $request->session()->flash('alert', config('messages.cacheupdated'));

    return back();
}
Avec sa route :
Route::prefix('admin')->middleware('admin')->namespace('Back')->group(function () {
    ...
    Route::name('cache.update')->put('cache', 'MaintenanceController@cache');
});
Dans la vue on ajoute la partie pour le cache :
@extends('back.layout')

@section('main') 
    ...

      <div class="col-sm-12">
        <div class="card">
          <h5 class="card-header">Cache</h5>
        
          <form method="POST" action="{{ route('cache.update') }}">
            <div class="card-body">
              @method('PUT')
              @csrf

              <x-checkbox-custom
                name="config"
                label="Cache de la configuration"
                off="info"
                on="warning"
                :value="$config"
              ></x-checkbox-custom>

              <x-checkbox-custom
                name="route"
                label="Cache des routes"
                off="info"
                on="warning"
                :value="$route"
              ></x-checkbox-custom>

              <div class="form-group row mb-0">
                <div class="col-md-12">
                   <button type="submit" class="btn btn-primary">Enregistrer</button>
                </div>
              </div>
              
            </div>            
          </form>

        </div>
      </div>
    </div>
  </div>
@endsection
On affiche un message à la mise à jour :

Attention lorsque vous mettez en cache : les mises à jour du code ne sont plus actives !

Le menu

Il ne nous reste plus qu'à compléter le menu dans back.layout pour accéder à notre formulaire :
<li class="nav-item has-treeview {{ menuOpen(
      'shop.edit',
      'shop.update',
      'pays.index',
      'pays.edit',
      'pays.create',
      'plages.edit',
      'colissimos.edit',
      'etats.index', 
      'etats.edit', 
      'etats.create', 
      'etats.destroy.alert',
      'pages.index',
      'pages.edit',
      'pages.create',
      'pages.destroy.alert',
      'maintenance.edit'
  ) }}">
  <a href="#" class="nav-link {{ currentRouteActive(
      'shop.edit',
      'shop.update',
      'pays.index',
      'pays.edit',
      'pays.create',
      'plages.edit',
      'colissimos.edit',
      'etats.index', 
      'etats.edit', 
      'etats.create', 
      'etats.destroy.alert',
      'pages.index',
      'pages.edit',
      'pages.create',
      'pages.destroy.alert',
      'maintenance.edit'
    ) }}">
    <i class="nav-icon fas fa-cogs"></i>
    <p>
      Administration
      <i class="right fas fa-angle-left"></i>
    </p>
  </a>
  <ul class="nav nav-treeview">

    ...

    <x-menu-item :href="route('maintenance.edit')" :sub=true :active="currentRouteActive('maintenance.edit')">
      Maintenance
    </x-menu-item>

  </ul>
</li>

Conclusion

Avec cet article s'achève sans doute cette série sur la création d'une application de commerce en ligne en espérant qu'elle vous a plue.



Par bestmomo

Nombre de commentaires : 14