Shopping : les états de commande

Les états de commande constitue une donnée essentielle pour le suivi, pour savoir si le règlement a été effectué, si l’expédition a eu lieu, s’il y a eu une annulation, en gros c’est la vie d’une commande. On va dans cet article gérer ces états, les modifier, les créer. C’est évidemment une tâche qu’on ne fait en principe qu’une fois à l’installation de la boutique mais il n’est pas exclu qu’on ajoute un état après coup.

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

Les données

Les données des états sont dans la table states :

On doit pouvoir les modifier, les supprimer (s’ils ne sont pas déjà attribués à une commandes)…

Contrôleur et routes

On crée un contrôleur :

php artisan make:controller Back\StateController --resource --model=Models\State

On utilisera toutes les méthodes sauf show.

On ajoute les routes :

Route::prefix('admin')->middleware('admin')->namespace('Back')->group(function () {
    ...
    Route::resource('etats', 'StateController')->except('show');
});

DataTable

On va utiliser un dataTable pour la gestion des états :

php artisan datatables:make StatesDataTable

<?php

namespace App\DataTables;

use App\Models\State;
use Yajra\DataTables\Html\Column;
use Yajra\DataTables\Services\DataTable;

class StatesDataTable extends DataTable
{
    /**
     * Build DataTable class.
     *
     * @param mixed $query Results from query() method.
     * @return \Yajra\DataTables\DataTableAbstract
     */
    public function dataTable($query)
    {
        return datatables()
            ->eloquent($query)
            ->addColumn('edit', function ($state) {
                return '<a href="' . route('etats.edit', $state->id) . '" class="btn btn-xs btn-warning btn-block">Modifier</a>';
            })
            ->addColumn('destroy', function ($state) {
                return '<a href="#" class="btn btn-xs btn-danger btn-block ' . ($state->orders->count() ? 'disabled' : '') .'">Supprimer</a>';
            })
            ->editColumn('color', function ($state) {
                return '<i class="fas fa-square fa-lg text-' . config('colors.' . $state->color) . '"></i>';
            })
            ->rawColumns(['edit', 'destroy', 'color']);
    }

    /**
     * Get query source of dataTable.
     *
     * @param \App\State $model
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function query(State $model)
    {
        return $model->with('orders')->newQuery();
    }

    /**
     * Optional method if you want to use html builder.
     *
     * @return \Yajra\DataTables\Html\Builder
     */
    public function html()
    {
        return $this->builder()
                    ->setTableId('states-table')
                    ->columns($this->getColumns())
                    ->minifiedAjax()
                    ->dom('Blfrtip')
                    ->orderBy(5)
                    ->lengthMenu()
                    ->language('//cdn.datatables.net/plug-ins/1.10.20/i18n/French.json');
    }

    /**
     * Get columns.
     *
     * @return array
     */
    protected function getColumns()
    {
        return [
            Column::make('id'),
            Column::make('name')->title('Nom'),
            Column::make('slug')->title('Slug'),
            Column::make('color')->title('Couleur')->addClass('text-center'),
            Column::make('indice')->title('indice'),
            Column::computed('edit')
              ->title('')
              ->width(60)
              ->addClass('text-center'),
            Column::computed('destroy')
              ->title('')
              ->width(60)
              ->addClass('text-center'),
        ];
    }

    /**
     * Get filename for export.
     *
     * @return string
     */
    protected function filename()
    {
        return 'States_' . date('YmdHis');
    }
}

Pour l’affichage des couleurs on crée un nouveau fichier de configuration :

<?php

return [
    'blue' => 'info',
    'red' => 'danger',
    'green' => 'success',
];

Le tableau

Pour afficher la liste des états on code la méthode index du contrôleur StateController :

use App\DataTables\StatesDataTable;

...

public function index(StatesDataTable $dataTable)
{
    return $dataTable->render('back.shared.index');
}

On renseigne le titre dans config.titles :

return [
    ...

    'etats' => [
        'index' => 'Gestion des états',
        'edit' => 'Modification d\'un état',
        'create' => 'Création d\'un état',
    ],
];

On ajoute le code pour faire apparaître le bouton de création d’un état dans la vue back.shared.index :

@section('main') 
  ...
  @if(Route::currentRouteName() === 'etats.index')
    <a class="btn btn-primary" href="{{ route('etats.create') }}" role="button">Créer un nouvel état</a>
  @endif
@endsection

Avec l’url …/admin/etats on a le tableau :

On peut trier par colonne.

Création d’un état

Le contrôleur

Pour modifier un état on code la méthode create du contrôleur :

public function create()
{
    return view('back.states.form');
}

La vue

On crée la vue du formulaire :

@extends('back.layout')

@section('main') 
  <div class="container-fluid"> 
    @if(session()->has('alert'))
      <div class="alert alert-success 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">
        
          <form method="POST" action="@isset($state) {{ route('etats.update', $state->id) }} @else {{ route('etats.store') }} @endisset">
            <div class="card-body">
              @isset($state) @method('PUT') @endisset
              @csrf
          
                <x-inputbs4
                  name="name"
                  type="text"
                  label="Nom"
                  :value="isset($state) ? $state->name : ''"
                  required="true"
                ></x-inputbs4>

                <x-inputbs4
                  name="slug"
                  type="text"
                  label="Slug"
                  :value="isset($state) ? $state->slug : ''"
                  required="true"
                ></x-inputbs4>

                <x-inputbs4
                  name="indice"
                  type="number"
                  label="Indice"
                  :value="isset($state) ? $state->indice : ''"
                  required="true"
                ></x-inputbs4>

                <label>Couleur</label>
                <select id="color" name="color" class="custom-select custom-select-md mb-3">
                  <option value="red" @if(isset($state) && $state->color === 'red') selected @endif>Rouge</option>
                  <option value="blue" @if(isset($state) && $state->color === 'blue') selected @endif>Bleu</option>
                  <option value="green" @if(isset($state) && $state->color === 'green') selected @endif>Vert</option>
                </select>

              </div>
            </div>

            <div class="form-group row mb-0">
              <div class="col-md-12">
                <a class="btn btn-primary" href="{{ route('etats.index') }}" role="button"><i class="fas fa-arrow-left"></i> Retour à la liste des états</a>
                <button type="submit" class="btn btn-primary">Enregistrer</button>
              </div>
            </div>
              
          </form>

        </div>
      </div>
    </div>
  </div>
@endsection

On obtient ce formulaire :

Le champ pour l’indice est de type number. Pour les couleurs on a une liste de choix.

La validation

Pour la validation on crée une requête de formulaire :

php artisan make:request StateRequest

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StateRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'required|string|max:100',
            'slug' => 'required|string|max:50',
            'color' => 'required|string|max:20',
            'indice' => 'required|numeric',
        ];
    }
}

Le contrôleur

On code la méthode store du contrôleur :

use App\Http\Requests\StateRequest;

...

public function store(StateRequest $request)
{      
    State::create($request->all());

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

On ajoute les messages dans config.messages :

return [
    ...
    'stateupdated' => 'L\'état a bien été mis à jour.',
    'statecreated' => 'L\'état a bien été créé.',
];

Quand on crée un état on a bien le message :

Et on le retrouve dans la liste :

Modification d’un état

Le contrôleur

Pour modifier un état on code la méthode edit du contrôleur :

public function edit(State $etat)
{
    return view('back.states.form', ['state' => $etat]);
}

On utilise la même vue mais cette fois on envoie les données.

Pour la modification dans la base on code la méthode update du contrôleur :

public function update(StateRequest $request, State $etat)
{
    $etat->update($request->all());

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

La validation est la même que pour la création.

On a un message lorsque la mise à jour a eu lieu :

Supprimer un état

La suppression d’un état n’est possible que si cet état n’est pas utilisé par une commande, dans ce cas le bouton n’est pas actif.

Le contrôleur

Pour supprimer un état on code la méthode destroy et on crée une méthode alert dans le contrôleur comme on l’avait fait pour les pays :

public function destroy(State $etat)
{
    $etat->delete();

    return redirect(route('etats.index'));
}

public function alert(State $etat)
{
    return view('back.states.destroy', ['state' => $etat]);
}

On ajoute la vue pour l’alerte :

@extends('back.layout')

@section('main') 
  <div class="container-fluid"> 
    <form id="deleteproduct" action="{{ route('etats.destroy', $state->id) }}" method="POST" style="display: none;">
      @csrf
      @method('DELETE')
    </form>
    <div class="row">
      <div class="col-sm-12 col-md-6 offset-md-3 col-lg-4 offset-lg-4">
        <div class="card text-white bg-dark mb-3">
          <div class="card-body">
            <h5 class="card-title text-center mb-3">Vous êtes sur le point de supprimer l'état "<strong>{{ $state->name }}</strong>"</h5>
            <p class="card-text">
              <a class="btn btn-danger btn-lg btn-block" href="#" role="button"
              onclick="event.preventDefault(); 
              $('#deleteproduct').submit();"
              >Je confirme la suppression</a>
            </p>
          </div>
        </div>
      </div>
    </div>
  </div>
@endsection

On crée la route :

Route::prefix('admin')->middleware('admin')->namespace('Back')->group(function () {
    ...
    Route::name('etats.destroy.alert')->get('etats/{etat}', 'StateController@alert');
});

On ajoute le lien dans les boutons de suppression dans StatesDataTable :

->addColumn('destroy', function ($state) {
    return '<a href="' . route('etats.destroy.alert', $state->id) . '" class="btn btn-xs btn-danger btn-block ' . ($state->orders->count() ? 'disabled' : '') .'">Supprimer</a>';
})

Quand on clique on a le message d’alerte :

Le menu

Il ne nous reste plus qu’à compléter le menu de l’administration (back.layout) :

<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'
  ) }}">
  <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'
    ) }}">
    <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('shop.edit')" :sub=true :active="currentRouteActive('shop.edit', 'shop.update')">
      Boutique
    </x-menu-item>

    <x-menu-item :href="route('etats.index')" :sub=true :active="currentRouteActive(
        'etats.index', 
        'etats.edit', 
        'etats.create', 
        'etats.destroy.alert'
      )">
      Etats de commande
    </x-menu-item>

    <x-menu-item :href="route('pays.index')" :sub=true :active="currentRouteActive(
        'pays.index', 
        'pays.edit',
        'pays.create'
      )">
      Pays
    </x-menu-item>

    ...

    </li>

  </ul>
</li>

Conclusion

Dans le prochain article nous verrons la gestion des pages d’information.

Print Friendly, PDF & Email

6 commentaires sur “Shopping : les états de commande

  1. Bonjour,
    Je n’arrive pas à afficher les états, j’obtiens le message suivant :

    DataTables warning: table id=states-table – Exception Message:
    Call to undefined relationship [states] on model [App\Models\State]

    Merci pour votre aide.

  2. Salut bestmomo, 🙂 je sais que j’embête pas mal ces temps ci, normal j’ai plus la saison de foot pour m’occuper…

    Tu n’aurais pas un package laravel tout prêt pour la présentation des produits ? Avec catégories, slider pour parcourir les items, recherche et tout ?

Laisser un commentaire