Shopping : les clients et les adresses

Dans une boutique en ligne on doit pouvoir consulter les données des clients : nom, prénom, raison sociale, email, adresses, pays, commandes… Mais nous n’avons aucune raison de modifier ces données qui appartiennent au clients.

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

Les données

Les données des clients sont dans la table users :

Les adresses sont dans la table addresses :

On ira aussi récupérer les informations de leurs commandes éventuelles dans la table orders :

Les clients

Contrôleur et routes

On crée un contrôleur :

php artisan make:controller Back\UserController --resource --model=Models\User

On n’utilisera que les méthodes index et show.

On ajoute les routes :

Route::prefix('admin')->middleware('admin')->namespace('Back')->group(function () {
    ...
    Route::resource('clients', 'UserController')->only(['index', 'show']);
});

DataTable

On va utiliser un dataTable pour la gestion des clients :

php artisan datatables:make UsersDataTable

<?php

namespace App\DataTables;

use App\Models\User;
use Yajra\DataTables\Html\Column;
use Yajra\DataTables\Services\DataTable;
use Cache;

class UsersDataTable extends DataTable
{
    /**
     * Build DataTable class.
     *
     * @param mixed $query Results from query() method.
     * @return \Yajra\DataTables\DataTableAbstract
     */
    public function dataTable($query)
    {
        return datatables()
            ->eloquent($query)
            ->editColumn('created_at', function ($user) {
                return $user->created_at->format('d/m/Y');
            })
            ->addColumn('action', function ($user) {
                return '<a href="' . route('clients.show', $user->id) . '" class="btn btn-xs btn-info btn-block">Voir</a>';
            })
            ->editColumn('newsletter', function ($user) {
                return $user->newsletter ? '<i class="fas fa-check text-success"></i>' : ''; 
            })
            ->addColumn('online', function ($user) {
                return Cache::has('user-is-online-' . $user->id) ? '<i class="fas fa-check text-success"></i>' : ''; 
            })
            ->addColumn('last', function ($user) {
                return $user->last_seen ? $user->last_seen->calendar() : ''; 
            })
            ->rawColumns(['newsletter', 'online', 'last', 'action']);
    }

    /**
     * Get query source of dataTable.
     *
     * @param \App\Models\User $model
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function query(User $model)
    {
        return $model->whereAdmin(false)->newQuery();
    }

    /**
     * Optional method if you want to use html builder.
     *
     * @return \Yajra\DataTables\Html\Builder
     */
    public function html()
    {
        return $this->builder()
                    ->setTableId('users-table')
                    ->columns($this->getColumns())
                    ->minifiedAjax()
                    ->dom('Blfrtip')
                    ->orderBy(1)
                    ->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('firstname')->title('Prénom'),
            Column::make('email'),
            Column::make('newsletter')->title('Lettre d\'information')->addClass('text-center'),
            Column::make('created_at')->title('Inscription'),
            Column::computed('online')
                  ->title('En ligne')
                  ->width(60)
                  ->addClass('text-center'),
            Column::computed('last')
                  ->title('Dernier vu')
                  ->width(100)
                  ->addClass('text-center'),
            Column::computed('action')
                  ->title('')
                  ->width(60)
                  ->addClass('text-center'),
        ];
    }

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

On exclut les administrateurs de la requête.

Le tableau

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

use App\DataTables\UsersDataTable;

...

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

On renseigne le titre dans config.titles :

return [

    ...

    'clients' => [
        'index' => 'Clients',
        'show' => 'Fiche client',
    ],
];

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

On peut trier par colonne et normalement la pagination fonctionne automatiquement, de même que le champ de recherche.

La fiche client

Quand on clique sur le bouton Voir on doit afficher la fiche du client.

Le contrôleur

Pour afficher la fiche client on va coder la méthode show du contrôleur :

public function show($id)
{
    $user = User::with('orders', 'orders.state', 'addresses')->findOrFail($id);

    return view('back.users.show', compact('user'));
}

La vue

On crée la vue pour la fiche :

@extends('back.layout')

@section('main') 
  <div class="card">
    <h5 class="card-header">Identité</h5>
    <div class="card-body">
      <dl class="row">
        <dt class="col-sm-3">Nom</dt>
        <dd class="col-sm-9">{{ $user->name }}</dd>      
        <dt class="col-sm-3">Prénom</dt>
        <dd class="col-sm-9">{{ $user->firstname }}</dd>      
        <dt class="col-sm-3">Email</dt>
        <dd class="col-sm-9"><a href="mailto:{{ $user->email }}">{{ $user->email }}</a></dd>      
        <dt class="col-sm-3 text-truncate">Date d'inscription</dt>
        <dd class="col-sm-9">{{ $user->created_at->format('d/m/Y') }}</dd>
        <dt class="col-sm-3 text-truncate">Dernière mise à jour</dt>
        <dd class="col-sm-9">{{ $user->updated_at->format('d/m/Y') }}</dd>        
        <dt class="col-sm-3">Lettre d'information</dt>
        <dd class="col-sm-9">@if($user->newsletter) Oui @else Non @endif</dd>
      </dl>
    </div>
  </div>

  @if($user->orders->isNotEmpty())
    <div class="card">
      <h5 class="card-header">Commandes</h5>
      <div class="card-body">
        <table class="table table-bordered table-hover">
          <thead>
            <tr>
              <th>Référence</th>
              <th>Date</th>
              <th>Prix total</th>
              <th>Paiement</th>
              <th>État</th>
              <th></th>
            </tr>
          </thead>    
          <tbody>
              @foreach($user->orders as $order)
              <tr>
                <td>{{ $order->reference }}</td>
                <td>{{ $order->created_at->calendar() }}</td>
                <td>{{ $order->total }} €</td>
                <td>{{ $order->payment_text }}</td>
                <td><span class="badge badge-{{ config('colors.' . $order->state->color) }}">{{ $order->state->name }}</span></td>
                <td style="text-align: center"><a href="#" class="btn btn-primary btn-sm">Voir</a></td>
              </tr>
            @endforeach
          </tbody>
        </table>
      </div>
    </div>
  @endif

  <div class="card">
    <h5 class="card-header">@if($user->addresses->count() === 1) Adresse @else Adresses @endif</h5>
    <div class="card-body">
      <div class="row">
        @foreach($user->addresses as $address)
          <div class="col-12 col-md-6 col-lg-4">
            <div class="card">
              <div class="card-body">
                @include('account.addresses.partials.address')
              </div>
            </div>
          </div>
        @endforeach
      </div>    
    </div>
  </div>

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

@endsection

On obtient la fiche avec l’url …/admin/clients/{id} :

On a 3 zones :

  1. l’identité du client
  2. les commandes éventuelles
  3. les adresses éventuelles

Pour le moment le bouton Voir pour les commandes ne fonctionne pas, on complètera le lien quand on traitera l’administration des commandes.

Les adresses

Contrôleur et routes

On crée un contrôleur :

php artisan make:controller Back\AddressController --resource --model=Models\Address

On n’utilisera que les méthodes index et show.

On ajoute les routes :

Route::prefix('admin')->middleware('admin')->namespace('Back')->group(function () {
    ...
    Route::resource('adresses', 'AddressController')->names([
        'index' => 'back.adresses.index',
        'show' => 'back.adresses.show',
    ])->only(['index', 'show']);
});

DataTable

On va utiliser un dataTable pour la gestion des adresses :

php artisan datatables:make AddressesDataTable

<?php

namespace App\DataTables;

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

class AddressesDataTable 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('show', function ($adresse) {
                return '<a href="' . route('back.adresses.show', $adresse->id) . '" class="btn btn-xs btn-warning btn-block">Voir</a>';
            })
            ->editColumn('country_id', function ($adresse) {
                return $adresse->country->name; 
            })
            ->rawColumns(['show']);
    }

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

    /**
     * Optional method if you want to use html builder.
     *
     * @return \Yajra\DataTables\Html\Builder
     */
    public function html()
    {
        return $this->builder()
                    ->setTableId('addresses-table')
                    ->columns($this->getColumns())
                    ->minifiedAjax()
                    ->dom('Blfrtip')
                    ->orderBy(1)
                    ->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('firstname')->title('Prénom'),
            Column::make('name')->title('Nom'),
            Column::make('company')->title('Raison sociale'), 
            Column::make('address')->title('Adresse'),            
            Column::make('postal')->title('Code postal'),
            Column::make('city')->title('Ville'),
            Column::make('country_id')->title('Pays'),
            Column::computed('show')
              ->title('')
              ->width(60)
              ->addClass('text-center'),
        ];
    }

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

Le tableau

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

use App\DataTables\AddressesDataTable;

...

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

On renseigne le titre dans config.titles :

return [

    'admin' => 'Tableau de bord',

    'back' => [
        'adresses' => [
            'index' => 'Gestion des adresses',
            'show' => 'Consultation d\'une adresse',
        ],
    ],

   ...

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

On peut trier par colonne et normalement la pagination fonctionne automatiquement, de même que le champ de recherche.

Consultation d’une adresse

Quand on clique sur le bouton Voir on doit afficher la fiche de l’adresse.

Le contrôleur

Pour afficher la fiche de l’adresse on va coder la méthode show du contrôleur :

public function show($id)
{
    $address = Address::findOrFail($id);

    return view('back.addresses.show', compact('address'));
}

La vue

On crée la vue pour la fiche :

@extends('back.layout')

@section('main') 
  <div class="card">
    <div class="card-body">
       @include('account.addresses.partials.address')
    </div>
  </div>

  <div class="form-group row mb-0">
    <div class="col-md-12">
      <a class="btn btn-primary" href="{{ route('back.adresses.index') }}" role="button"><i class="fas fa-arrow-left"></i> Retour à la liste des adresses</a>    
    </div>
  </div><br>
  
@endsection

On obtient la fiche avec l’url …/admin/adresses/{id} :

Le menu

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

<li class="nav-item has-treeview {{ menuOpen(
    'clients.index', 
    'clients.show', 
    'back.adresses.index', 
    'back.adresses.show' 
  )}}">
  <a href="#" class="nav-link {{ currentRouteActive(
      'clients.index', 
      'clients.show', 
      'back.adresses.index',
      'back.adresses.show'
    )}}">
    <i class="nav-icon fas fa-user-alt"></i>
    <p>
      Clients
      <i class="right fas fa-angle-left"></i>
    </p>
  </a>
  <ul class="nav nav-treeview">
    <x-menu-item :href="route('clients.index')" :sub=true :active="currentRouteActive('clients.index', 'clients.show')">
      Clients
    </x-menu-item>
    <x-menu-item :href="route('back.adresses.index')" :sub=true :active="currentRouteActive('back.adresses.index', 'back.adresses.show')">
      Adresses
    </x-menu-item>
  </ul>
</li>

Comme c’est une rubrique importante on crée un menu Clients et deux items : les clients et les adresses.

Conclusion

Dans le prochain article nous verrons la gestion des commandes.

Print Friendly, PDF & Email

Laisser un commentaire