Laravel 5

Un site d’annonces – l’interface utilisateur

Notre site d’annonces est presque terminé. Il ne nous reste plus qu’à coder l’interface de l’utilisateur. Il doit pouvoir gérer ses annonces (voir celles qui sont en attentes, les actives, prolonger une annonce, la détruire). Il doit aussi avoie accès à ses données personnelles et éventuellement les changer.

Pour vous simplifier la vie vous pouvez télécharger le dossier complet pour le code de cet article. C’est d’ailleurs l’état final de ce projet. Vous pouvez aussi récupérer la version pour Laravel 8 mise à disposition par Everyways (merci à lui !).

Un middleware

De la même manière qu’on a créé un middleware pour filtrer les administrateurs on va en créer un pour filtrer les utilisateurs :

php artisan make:middleware User

Avec ce code :

public function handle($request, Closure $next)
{
    if ($request->user()) {
        return $next($request);
    }

    return redirect()->route('home');
}

Et on le déclare dans le kernel :

protected $routeMiddleware = [
    ...
    'user' => \App\Http\Middleware\User::class,
];

Les routes

On va avoir besoin de routes pour les actions de l’utilisateur :

  • Affichage de l’accueil
  • Gestion des annonces
    • Annonces actives
    • Annonces obsolètes
    • Annonces en attente de modération (donc non actives)
  • Profil
    • Affichage du profil
    • Affichage formulaire de modification de l’email
    • Mise à jour de l’email
    • Affichage des données personnelles

Voilà le code pour assurer toutes ces tâches :

Route::prefix('utilisateur')->middleware('user')->group(function () {
    Route::get('/', 'UserController@index')->name('user.index');
    Route::prefix('annonces')->group(function () {
        Route::get('actives', 'UserController@actives')->name('user.actives');
        Route::get('obsoletes', 'UserController@obsoletes')->name('user.obsoletes');
        Route::get('attente', 'UserController@attente')->name('user.attente');
    });
    Route::prefix('profil')->group(function () {
        Route::get('email', 'UserController@emailEdit')->name('user.email.edit');
        Route::put('email', 'UserController@emailUpdate')->name('user.email.update');
        Route::get('donnees', 'UserController@data')->name('user.data');
    });
});

L’accès à son compte

On va prévoir un accès à l’interface utilisateur à partir de la page d’accueil dans la barre de menu.

Dans le layout app on ajoute le lien :

@admin
    ...
@else
    <li class="nav-item">
        <a class="nav-link" href="{{ route('user.index') }}">Mon compte</a>
    </li>
@endadmin

UserController

On doit créer la méthode index dans le contrôleur pour récupérer les données et les envoyer sur la vue de l’accueil :

public function index(Request $request)
{
    $ads = $this->adRepository->getByUser($request->user());

    $adAttenteCount = $this->adRepository->noActiveCount($ads);
    $adActivesCount = $this->adRepository->activeCount($ads);
    $adPerimesCount = $this->adRepository->obsoleteCount($ads);

    return view('user.index', compact('adActivesCount', 'adPerimesCount', 'adAttenteCount'));
}

Est-ce qu’on a déjà toutes les méthodes dans le repository ? On voit qu’il nous manque getByUser et activeCount.

AdRepository

On va donc créer ces deux méthodes

  • pour aller chercher les annonces de l’utilisateur (getByUser)
  • pour connaître le nombre d’annonces actives (activeCount)
public function activeCount($ads)
{
    return $ads->where('active', true)->where('limit', '>=', Carbon::now())->count();
}

public function getByUser($user)
{
    return $user->ads()->get();
}

Layout user

On crée un layout pour les pages des utilisateurs :

Avec ce code :

@include('layouts.back-head')

<body id="page-top">

    <!-- Page Wrapper -->
    <div id="wrapper">

      <!-- Sidebar -->
      <ul class="navbar-nav bg-gradient-primary sidebar sidebar-dark accordion" id="accordionSidebar">

        <!-- Sidebar - Brand -->
        <a class="sidebar-brand d-flex align-items-center justify-content-center" href="{{ url('/') }}">
          <div class="sidebar-brand-icon rotate-n-15">
            <i class="fab fa-earlybirds fa-2x"></i>
          </div>
          <div class="sidebar-brand-text mx-3">Annonces</div>
        </a>

        <!-- Divider -->
        <hr class="sidebar-divider my-0">

        <!-- Nav Item - Dashboard -->
        <li class="nav-item @if(request()->route()->getName() == 'user.index') active @endif">
          <a class="nav-link" href="{{ route('user.index') }}">
            <i class="fas fa-fw fa-tachometer-alt"></i>
            <span>Panneau</span></a>
        </li>

        <!-- Divider -->
        <hr class="sidebar-divider">

        <!-- Heading -->
        <div class="sidebar-heading">
          Annonces
        </div>

        <li class="nav-item @if(request()->route()->getName() == 'user.actives') active @endif">
            <a class="nav-link" href="{{ route('user.actives') }}">
                <i class="fas fa-fw fa-hiking"></i>
            <span>Actives</span></a>
        </li>

        <li class="nav-item @if(request()->route()->getName() == 'user.attente') active @endif">
            <a class="nav-link" href="{{ route('user.attente') }}">
                <i class="fas fa-fw fa-hourglass-start"></i>
            <span>En attente</span></a>
        </li>

        <li class="nav-item @if(request()->route()->getName() == 'user.obsoletes') active @endif">
            <a class="nav-link" href="{{ route('user.obsoletes') }}">
                <i class="fas fa-fw fa-hourglass-end"></i>
            <span>Obsolètes</span></a>
        </li>

        <!-- Divider -->
        <hr class="sidebar-divider">

        <!-- Heading -->
        <div class="sidebar-heading">
          Profil
        </div>

        <li class="nav-item @if(request()->route()->getName() == 'user.email.edit') active @endif">
            <a class="nav-link" href="{{ route('user.email.edit') }}">
                <i class="fas fa-fw fa-at"></i>
            <span>Email</span></a>
        </li>

        <li class="nav-item @if(request()->route()->getName() == 'user.data') active @endif">
            <a class="nav-link" href="{{ route('user.data') }}">
                <i class="fas fa-fw fa-database"></i>
            <span>Mes données</span></a>
        </li>

        <!-- Divider -->
        <hr class="sidebar-divider d-none d-md-block">

        <!-- Sidebar Toggler (Sidebar) -->
        <div class="text-center d-none d-md-inline">
          <button class="rounded-circle border-0" id="sidebarToggle"></button>
        </div>

      </ul>
      <!-- End of Sidebar -->

      <!-- Content Wrapper -->
      <div id="content-wrapper" class="d-flex flex-column">

        <!-- Main Content -->
        <div id="content">

        <!-- Topbar -->
        <nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow">

            <!-- Sidebar Toggle (Topbar) -->
            <button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3">
              <i class="fa fa-bars"></i>
            </button>

            <!-- Topbar Navbar -->
            <ul class="navbar-nav ml-auto">

              <!-- Nav Item - User Information -->
              <li class="nav-item dropdown no-arrow">
                <a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                  <span class="d-lg-inline text-gray-600 small">{{ auth()->user()->name }}</span>
                </a>
                <!-- Dropdown - User Information -->
                <div class="dropdown-menu dropdown-menu-right shadow animated--grow-in" aria-labelledby="userDropdown">
                  <div class="dropdown-divider"></div>
                  <a class="dropdown-item" href="{{ route('logout') }}"
                        onclick="event.preventDefault(); document.getElementById('logout-form').submit();">
                    <i class="fas fa-sign-out-alt fa-sm fa-fw mr-2 text-gray-400"></i>
                    Déconnexion
                  </a>
                  <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                    @csrf
                  </form>
                </div>
              </li>

            </ul>

          </nav>
          <!-- End of Topbar -->

          <!-- Begin Page Content -->
          <div class="container-fluid">

            @yield('content')

          </div>
          <!-- /.container-fluid -->

        </div>
        <!-- End of Main Content -->

        <!-- Footer -->
        <footer class="sticky-footer bg-white">
          <div class="container my-auto">
            <div class="copyright text-center my-auto">
              <span>Copyright &copy; Annonces 2019</span>
            </div>
          </div>
        </footer>
        <!-- End of Footer -->

      </div>
      <!-- End of Content Wrapper -->

    </div>
    <!-- End of Page Wrapper -->

    @include('layouts.back-footer')

On inclut des vues pour l’entête et le bas de page qu’on a déjà créées pour l’adminstration.

Vue user.index

Il ne nous manque plus que la vue pour l’accueil :

Avec ce code :

@extends('layouts.user')

@section('content')

    <!-- Page Heading -->
    <div class="d-sm-flex align-items-center justify-content-between mb-4">
        <h1 class="h3 mb-0 text-gray-800">Tableau de bord</h1>
    </div>

    <!-- Content Row -->
    <div class="row">

        <div class="col-xl-4 col-md-6 mb-4">
            <div class="card border-left-success shadow h-100 py-2">
                <div class="card-body">
                <div class="row no-gutters align-items-center">
                    <div class="col mr-2">
                    <div class="text-xs font-weight-bold text-success text-uppercase mb-1">Annonces actives</div>
                    <div class="h5 mb-0 font-weight-bold text-gray-800">{{ $adActivesCount }}</div>
                    </div>
                    <div class="col-auto">
                        <i class="fas fa-hiking fa-2x text-gray-300"></i>
                    </div>
                </div>
                </div>
            </div>
        </div>

        <div class="col-xl-4 col-md-6 mb-4">
            <div class="card border-left-warning shadow h-100 py-2">
                <div class="card-body">
                <div class="row no-gutters align-items-center">
                    <div class="col mr-2">
                    <div class="text-xs font-weight-bold text-warning text-uppercase mb-1">Annonces en attente</div>
                    <div class="h5 mb-0 font-weight-bold text-gray-800">{{ $adAttenteCount }}</div>
                    </div>
                    <div class="col-auto">
                        <i class="fas fa-hourglass-start fa-2x text-gray-300"></i>
                    </div>
                </div>
                </div>
            </div>
        </div>

        <div class="col-xl-4 col-md-6 mb-4">
            <div class="card border-left-danger shadow h-100 py-2">
                <div class="card-body">
                <div class="row no-gutters align-items-center">
                    <div class="col mr-2">
                    <div class="text-xs font-weight-bold text-danger text-uppercase mb-1">Annonces obsolètes</div>
                    <div class="h5 mb-0 font-weight-bold text-gray-800">{{ $adPerimesCount }}</div>
                    </div>
                    <div class="col-auto">
                        <i class="fas fa-hourglass-end fa-2x text-gray-300"></i>
                    </div>
                </div>
            </div>
        </div>

    </div>

@endsection

Et on arrive enfin dans l’accueil de l’utilisateur :

Redirection après connexion

Tant qu’on y est on va faire en sorte que quand un utilisateur se connecte il arrive directement dans son interface personnelle. Et pour faire bonne mesure on va aussi s’arranger pour que les administrateurs arrivent eux aussi directement sur la page d’administration. POur faire ça il faut jouer avec la redirection après connexion.

Dans le contrôleur LoginController ajoutez cette méthode :

public function redirectTo()
{
    return auth()->user()->admin ? route('admin.index') : route('user.index');
}

Et maintenant la redirection fonctionne comme on le voulait !

Les annonces actives

Traitons maintenant le cas des annonces actives. L’utilisateur doit pouvoir :

  • voir son annonce
  • modifier son annonce (uniquement le titre et le texte pour simplifier)
  • prolonger son annonce d’une semaine
  • supprimer son annonce

On va évidemment réutiliser du code déjà en place pour toutes ces tâches.

AdRepository

Dans le repository on crée la méthode active qui va nous donnée les annonces actives paginées pour l’utilisateur en cours :

public function active($user, $nbr)
{
    return $user->ads()->whereActive(true)->where('limit', '>=', Carbon::now())->paginate($nbr);
}

UserController

Dans le contrôleur on utilise cette méthode et on envoie les données à la vue :

public function actives(Request $request)
{
    $ads = $this->adRepository->active($request->user(), 5);

    return view('user.actives', compact('ads'));
}

Vue user.actives

On crée enfin la vue :

Avec ce code :

@extends('layouts.user')

@section('content')

    @include('partials.alerts', ['title' => 'Annonces actives'])

    @include('partials.table-add-del-view', ['edit' => true])

@endsection

@section('script')

    @include('partials.script-add-del-view')

@endsection

Et par la magie des vues partielles on récupère tout le code qu’on a déjà écrit !

Et du coup on a déjà 3 commandes qui fonctionnent : voir une annonce, prolonger une annonce, supprimer une annonce.

Par contre il nous faut ajouter la modification d’une annonce. Comme on avait créé un contrôleur de ressource (AdController) avec les routes associées on a déjà une partie de l’intendance en place.

Modifier une annonce

AdController

Dans le contrôleur on complète les deux méthodes :

use App\Http\Requests\ { AdStore, AdUpdate };

...

public function edit(Ad $ad)
{
    $this->authorize('manage', $ad);

    return view('edit', compact('ad'));
}

public function update(AdUpdate $request, Ad $ad)
{
    $this->authorize('manage', $ad);

    $this->adRepository->update($ad, $request->all());

    $request->session()->flash('status', "L'annonce a bien été modifiée.");

    return back();
}

AdRepository

Dans le repository on ajoute la méthode update :

public function update($ad, $data)
{
    $ad->update($data);
}

Form request AdUpdate

Comme on va avoir une validation on ajoute une Form Request :

php artisan make:request AdUpdate

Avec ce code :

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class AdUpdate extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'title' => ['required', 'string', 'max:100'],
            'texte' => ['required', 'string', 'max:1000'],
        ];
    }
}

La vue edit

On crée la vue pour le formulaire de modification :

@extends('layouts.app')

@section('content')

<div class="container">

    <div class="card bg-light">
        <h5 class="card-header">Votre annonce</h5>
        <div class="card-body">

            <div class="alert alert-warning alert-dismissible fade show" role="alert">
                Vous ne pouvez modifier que le titre et le texte de votre annonce.
                <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>

            @if(session()->has('status'))
                <div class="alert alert-success alert-dismissible fade show" role="alert">
                    {{ session('status') }}
                    <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
            @endif

            <form method="POST" action="{{ route('annonces.update', $ad->id) }}">
                @method('PUT')
                @csrf

                @include('partials.form-group', [
                    'title' => 'Titre',
                    'type' => 'text',
                    'name' => 'title',
                    'value' => $ad->title,
                    'required' => true,
                ])

                <div class="form-group">
                    <label for="texte">Texte</label>
                    <textarea class="form-control{{ $errors->has('texte') ? ' is-invalid' : '' }}" id="texte" name="texte" rows="3" required>{{ old('texte', isset($value) ? $value : $ad->texte) }}</textarea>
                    @if ($errors->has('texte'))
                        <div class="invalid-feedback">
                            {{ $errors->first('texte') }}
                        </div>
                    @endif
                </div>

                <br>

                <button type="submit" class="btn btn-primary">Valider</button>
            </form>

        </div>
    </div>
</div>

@endsection

On prévoit un petit message pour préciser qu’on ne peut changer que le titre et le texte de l’annonce.

La validation doit fonctionner :

Si tout se passe bien on affiche l’alerte :

Les annonces en attente de modération

Traitons maintenant le cas des annonces en attente . L’utilisateur doit pouvoir :

  • voir son annonce
  • supprimer son annonce

AdRepository

Dans le repository on crée la méthode attente qui va nous donnée les annonces pas encore modérées pur l’utilisateur en cours :

public function attente($user, $nbr)
{
    return $user->ads()->whereActive(false)->paginate($nbr);
}

UserController

Dans le contrôleur on utilise cette méthode et on envoie les données à la vue :

public function attente(Request $request)
{
    $ads = $this->adRepository->attente($request->user(), 5);

    return view('user.waiting', compact('ads'));
}

Vue user.waiting

On crée enfin la vue :

@extends('layouts.user')

@section('content')

    @include('partials.alerts', ['title' => 'Annonces en attente'])

    @include('partials.table-add-del-view', ['noAdd' => true])

@endsection

@section('script')

    @include('partials.script-add-del-view')

@endsection

Là aussi on bénéficie de tout le code écrit précédemment…

Et on a déjà tout le code pour le fonctionnement !

Les annonces obsolètes

Traitons maintenant le cas des annonces en attente . L’utilisateur doit pouvoir :

  • voir son annonce
  • ajouter une semaine de délai
  • supprimer son annonce

AdRepository

Dans le repository on crée la méthode obsoleteForUser qui va nous donnée les annonces qui ont dépassé la date de validité :

public function obsoleteForUser($user, $nbr)
{
    return $user->ads()->where('limit', '<', Carbon::now())->latest('limit')->paginate($nbr);
}

UserController

Dans le contrôleur on utilise cette méthode et on envoie les données à la vue :

public function obsoletes(Request $request)
{
    $ads = $this->adRepository->obsoleteForUser($request->user(), 5);

    return view('user.obsoletes', compact('ads'));
}

Vue user.obsoletes

On crée enfin la vue :

@extends('layouts.user')

@section('content')

    @include('partials.alerts', ['title' => 'Annonces obsolètes'])

    @include('partials.table-add-del-view')

@endsection

@section('script')

    @include('partials.script-add-del-view')

@endsection

Et là aussi on a déjà tout le code pour le fonctionnement !

Changement de l’email

L’utilisateur doit pouvoir modifier son email. On va lui proposer un formulaire pour le faire.

UserController

Dans le contrôleur on crée les deux méthodes ;

use App\Http\Requests\ { MessageAd, EmailUpdate };

...


public function emailEdit()
{
    return view('user.email');
}

public function emailUpdate(EmailUpdate $request)
{
    auth()->user()->email = $request->email;
    auth()->user()->save();

    $request->session()->flash('status', "Votre email a bien été mis à jour.");

    return back();
}

Form request EmailUpdate

Comme on va avoir une validation on ajoute une Form Request :

php artisan make:request EmailUpdate

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class EmailUpdate extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'email' => [
                'required', 'string', 'email', 'max:255',
                Rule::unique('users')->ignore(auth()->id()),
            ],
        ];
    }
}

La vue user.email

On crée la vue pour le formulaire de modification :

@extends('layouts.user')

@section('content')

    <div class="container">

        <div class="card">
            <h5 class="card-header">Vous pouvez modifier votre email ici</h5>
            <div class="card-body">

                @if(session()->has('status'))
                    <div class="alert alert-success alert-dismissible fade show" role="alert">
                        {{ session('status') }}
                        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                @endif

                <form method="POST" action="{{ route('user.email.update') }}">
                    @method('PUT')
                    @csrf

                    @include('partials.form-group', [
                        'title' => '',
                        'type' => 'text',
                        'name' => 'email',
                        'value' => auth()->user()->email,
                        'required' => true,
                    ])

                    <br>

                    <button type="submit" class="btn btn-primary">Valider</button>
                </form>

            </div>
        </div>
    </div>

@endsection

On a un traitement classique sur lequel je ne m’étends pas.

Les données personnelles

Le RGPD oblige à fournir à un utilisateur, s’il le désire, toutes les données le concernant. On va le faire ici même s’il y a peu de données.

UserController

On code la méthode data dans le contrôleur :

public function data()
{
    $user = auth()->user();

    return view('user.data', compact('user'));
}

Vue user.data

On crée la vue :

@extends('layouts.user')

@section('content')

    <div class="container">

        <div class="d-sm-flex align-items-center justify-content-between mb-4">
            <h1 class="h3 mb-0 text-gray-800">Vos données personnelles</h1>
        </div>

        <div class="card">
            <div class="card-body">
                <h5 class="card-title">A propos</h5>
                <table class="table">
                    <tbody>
                        <tr>
                            <td>Rapport généré pour</td>
                            <td>{{ $user->name }}</td>
                        </tr>
                        <tr>
                            <td>Pour le site</td>
                            <td>Annonces</td>
                        </tr>
                        <tr>
                            <td>A l'url</td>
                            <td>annonces.oo</td>
                        </tr>
                        <tr>
                            <td>Le</td>
                            <td>{{ \Carbon\Carbon::now()->format('d-m-Y') }}</td>
                        </tr>
                    </tbody>
                </table>
                <em>Vous pouvez enregistrer cette page pour conserver vos données en utilisant le menu de votre navigateur.</em>
            </div>
        </div>

        <br>

        <div class="card">
            <div class="card-body">
                <h5 class="card-title">Utilisateur</h5>
                <table class="table">
                    <tbody>
                        <tr>
                            <td>ID</td>
                            <td>{{ $user->id }}</td>
                        </tr>
                        <tr>
                            <td>Nom de connexion</td>
                            <td>{{ $user->name }}</td>
                        </tr>
                        <tr>
                            <td>Email</td>
                            <td>{{ $user->email }}</td>
                        </tr>
                        <tr>
                            <td>Date d'inscription</td>
                            <td>{{ $user->created_at->format('d-m-Y') }}</td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>

    </div>

@endsection

On est ainsi en règle ! Enfin presque…

Les pages légales

Si vous ne savez pas ce que sont les mentions légales je vous coneille un petit tour sur le site officiel.

On a prévu des liens en bas de la page d’accueil de notre site pour les mentions légales :

Routes

On ajoute les routes :

Route::view('legal', 'legal')->name('legal');
Route::view('confidentialite', 'confidentialite')->name('confidentialite');

Layout app

On complète les liens dans le layout :

<nav class="navbar navbar-expand fixed-bottom navbar-dark">
    <div class="navbar-nav ml-auto">
        <a class="nav-item nav-link " href="{{ route('legal') }}">Mentions légales</a>
        <a class="nav-item nav-link " href="{{ route('confidentialite') }}">Politique de confidentialité</a>
    </div>
</nav>

Vue legal

@extends('layouts.app')

@section('content')
<div class="container">

        <div class="card text-white bg-secondary">
            <div class="card-body">
                <h5 class="card-title">Mentions légales</h5>
                <table class="table text-white">
                    <tbody>
                        <tr>
                            <td>Raison sociale</td>
                            <td>Association des petits dénicheurs</td>
                        </tr>
                        <tr>
                            <td>Email</td>
                            <td>cestici@parla.fr</td>
                        </tr>
                        <tr>
                            <td>Téléphone</td>
                            <td>00 00 00 00 00</td>
                        </tr>
                        <tr>
                            <td>Directeur de publication</td>
                            <td>Monsieur Durand François</td>
                        </tr>
                        <tr>
                            <td>Hébergeur du site</td>
                            <td>Je stocke tout à Petaouchnoc, téléphone 00 00 00 00 00</td>
                        </tr>
                    </tbody>
                </table>
            </div>
        </div>

</div>
@endsection

Politique de confidentialité

Le RGPD régit désormais la gesion des données personnelles. Là aussi si vous avez des lacunes je vous conseile d’aller sur le site de la CNIL. Ils ont d’ailleurs mis en place un atelier très bien pensé sur le sujet.

Vue confidentialite

@extends('layouts.app')

@section('content')
<div class="container">

        <div class="card text-white bg-secondary">
            <div class="card-body">
                <h5 class="card-title">Politique de confidentialité</h5>
                <div class="card-body">
                    <p>Ici le long texte que personne ne lit mais que vous devez obligatoirement prévoir si vous recueillez des données utilisateur et qui doit être conforme au RGPD.</p>
                    <p>Pour mémoire une adresse email est considrée comme une donnée personnelle au regard du RGPD.</p>
                </div>
            </div>
        </div>

</div>
@endsection

Conclusion

On arrive au bout de ce projet avec un site maintenant parfaitement fonctionnel. Je l’ai voulu suffisamment élaboré pour être réaliste sans le rendre toutefois trop chargé, un équilibre pas toujours facile à tenir. D’autre part je suis conscient de ne pas avoir tout détaillé et d’être même parfois passé assez rapidement sur certaines parties. Sinon le nombre d’articles aurait été trop important ! Je peux évidemment revenir sur certains points si certains le désirent. Bonne lecture !

Print Friendly, PDF & Email

30 commentaires

  • Drope

    Hello,
    Super tuto, hyper complet qui m’a servi de base pour développer une première appli Laravel.
    Je ne comprends pas dans ce chapitre pourquoi il n’y a pas de contrôle dans le middleware User. Dans le zip, il n’y a rien dans la fonction handle.
    Ne faut-il pas vérifier qu’on a bien un utilisateur ?
    Question complémentaire, pourquoi la route edit pour éditer une annonce n’est pas dans les routes contrôlées par ce même middleware ?

    Merci encore pour tout ce travail.

    • bestmomo

      Salut,

      Oui effectivement je n’avais pas mis le code du middleware, c’est réparé dans l’article et je vais aussi mettre à jour le ZIP. La route pour l’édition est déjà protégée par une autorisation alors le fait d’ajouter le middleware n’ajouterait rien.

      Cordialement

  • Filtox

    Bonjour bestmomo,

    Déjà bravo pour votre formation, cependant j’aurais une question, que je refasse toute la formation ou que je télécharge le dernier .zip, j’ai le même problème, c’est à dire que j’ai une erreur avec la base de donnée, quand je fais la migration, mes tables se crées mais il n’y a rien dedans, du coup j’exécute la commande « php artisan db:seed –class=DatabaseSeeder » pour les remplir (que j’ai trouvé dans un commentaire sous la partie « un site d’annonces – les annonces »), c’est effectivement ce qu’il se passe mais une erreur apparaît avant de terminer cette commande qui est :

    ErrorException : file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages:
    error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed

    at C:\Users\hatch\Documents\mesannonces8\database\factories\AdFactory.php:9
    5| use App\Models\Region;
    6|
    7| function getRandGeo($url)
    8| {
    > 9| $results = file_get_contents($url);
    10| $elements = json_decode($results, true);
    11| $id = rand(0, count($elements) – 1);
    12| return $elements[$id];
    13| }

    Exception trace:

    1 file_get_contents(« https://geo.api.gouv.fr/regions/27/departements »)
    C:\Users\hatch\Documents\mesannonces8\database\factories\AdFactory.php:9

    2 getRandGeo(« https://geo.api.gouv.fr/regions/27/departements »)
    C:\Users\hatch\Documents\mesannonces8\database\factories\AdFactory.php:20

    A cause de ce problème, dès que je mets une annonce en ligne, j’arrive sur un écran d’erreur. Peux-tu m’aider s’il-te-plait ?

    Merci d’avance

      • un_passant

        Merci pour ce bon très bon tuto ^^

        Tout marche, par contre, j’ai remarqué certaine choses:
        Un admin peut aussi déposer une annonce et donc pouvoir acceder/modifier ses propres annonces et donc à tout ce qui est possible de faire pour un utilisateur dans le panneau de configuration:
        actives
        en attente

        dans profil:
        email
        mes données

        quand on est connecté aussi on ne voit pas facilement sous quel compte on est connecté ^^

        • bestmomo

          Salut,

          Oui j’ai laissé l’administrateur en capacité de gérer aussi des annonces, ce n’est pas vraiment gênant. Pour l’identification du compte lorsqu’on est connecté il me semble m’être posé la question mais je vois pas pourquoi on aurait plusieurs comptes…

  • ewkilian

    Bonjour,
    Je te remercie vraiment pour ton travail de titan ! j’apprends énormément Merci !
    Je me heurte cependant à une difficulté dans la méthode update(AdUpdate $request, Ad $ad)
    tu ajoutes $ad à l’update « $this->adRepository->update($ad); » cependant les valeurs modifiées sont dans $request et non dans $ad? Ai-je mal compris quelques chose ?
    En te remerciant
    A bientôt

      • ewkilian

        Bonjour,
        Je suis « en partie » d’accord car tous n’est pas claire, comment Laravel (ou Eloquent) connait les modifications, si on n’envoie pas la modification dans la méthode Update.
        $this->adRepository->update($ad); -> $Ad contient l’ancienne info, et on envoie jamais les info de $Request.
        Egalement, la méthode Update, est-ce une méthode native de l’objet Model ? (Je ne trouve pas d’information sur le Web).
        Je te remercie bien
        Cordialement

      • ewkilian

        J’ai corrigé pour que ca fonctionne :
        AdController.php
        public function update(AdUpdate $request, Ad $ad)
        {
        ….
        $this->adRepository->update($ad, $request);
        ….
        }
        Et AdRepository.php
        public function update($ad, $request)
        {
        $ad->update($request->toArray());
        }

  • kouyate

    Bonjour bestmomo, je voulais vous poser une question qui n’est pas en rapport avec ce sujet mais je n’ai pas votre boite personnelle, donc excusez moi si je la pose ici, ben voila, je vais en fait faire une signature graphique, c’est a dire l’utilisateur signe, et je capture la signature ensuite l’appose sur un document pdf.
    je veux essayer de reproduire le processus sur ce site : https://www.app.universign.com.
    merci de m’orienter..

  • pascal78

    bonsoir Bestmomo,

    Félicitations pour votre site et cette formation,

    Un projet concret et des explications en français ça fait vraiment plaisir !

    A l’instar de Grafikart et Laracasts, milles mercis de faire partager votre savoir et vos expériences à tous ceux qui apprécient le framework laravel !

    J’ai hâte de voir de nouveaux tutoriels de votre part !

  • kouyate

    Merci, j’ai pu resoudre, effectivement la librairie a evolué, ce n’est plus swal mais swal.fire().
    merci pour votre aide, a propos j’ai un projet sur le corwdfounding, si vous pouvez faire un tuto sur un tel sujet ca sera super, neanmoiins si vous ne faites pas est ce que je pourrai vous contacter en cas de besoin.

    • bestmomo

      Salut,

      Tout dépend du problème, souvent entre le moment où je rédige un article et le moment où il est utilisé les librairies évoluent dans leur version et des différences apparaissent. Il faut alors aller jeter un oeil dans les documentations.

Laisser un commentaire