Shopping : la commande

Notre boutique sait afficher des produit et gérer un panier. Dans cet article on va border la commande.

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

Contrôleur et route

On crée un contrôleur pour la création d’une commande :

php artisan make:controller OrderController --resource

On ne va conserver que les méthodes create et store.

Pour les routes on veut que ce soit seulement accessible aux utilisateurs authentifiés :

// Utilisateur authentifié
Route::middleware('auth')->group(function () {
  // Commandes
  Route::prefix('commandes')->group(function () {
      Route::resource('/', 'OrderController')->names([
          'create' => 'commandes.create',
          'store' => 'commandes.store',
      ])->only(['create', 'store']);
  });
});

On est obligé de spécifier les noms parce que celui de la ressource est vide.

J’espère que le mélange anglais/français dans les appellations ne vous dérange pas parce que je mélange souvent les deux au gré du code…

Par contre je m’attache à créer toutes les urls en français pour cette application et là ce n’est pas le cas !

Dans la méthode boot de AppServiceProvider on va ajouter ce code pour la traduction automatique :

use Illuminate\Support\Facades\Route;

...

Route::resourceVerbs([
    'edit' => 'modification',
    'create' => 'creation',
]);

Maintenant ça va mieux :

On peut maintenant ajouter le lien dans la vue views/cart/index.blade.php :

<a href="{{ route('commandes.create') }}">Commander</a>

D’autre part lorsque l’utilisateur n’est pas authentifié et qu’il clique sur le bouton « Commander » dans le panier il est renvoyé sur la page de connexion. Il est judicieux alors de lui afficher un message explicatif. On complète la vue views/auth/loging.blade.php :

<form  method="POST" action="{{ route('login') }}">
  @if(url()->previous() === route('panier.index'))
    <div class="col s12">
      <div class="card purple darken-3">
        <div class="card-content white-text center-align">
          Vous devez être connecté pour passer une commande, si vous n'avez pas encore de compte vous pouvez en créer un en utilisant le lien sous ce formulaire.
        </div>
      </div>
    </div>
  @endif

Un service pour les frais d’envoi

Il va falloir calculer plusieurs fois les frais d’envoi en plusieurs emplacements, on va créer un service pour ça :

Avec ce code :

<?php

namespace App\Services;
use App\Models\Range;
use Cart;

class Shipping
{
    public function compute($country_id)
    {
        $items = Cart::getContent();

        $weight = $items->sum(function ($item) {
            return $item->quantity * $item->model->weight;
        });

        $range = Range::orderBy('max')->where('max', '>=', $weight)->first();
        
        return $range->countries()->where('countries.id', $country_id)->first()->pivot->price;
    }
}

On commence par récupérer le contenu du panier actif.

Ensuite on calcule le poids total des produits en utilisant la méthode sum de la collection.

La plage est ensuite définie à partir du poids.

Enfin le tarif est déterminé en fonction du pays prévu pour l’expédition.

Les données

Dans le contrôleur OrderController on va récupérer toutes les données utiles et ouvrir la vue :

public function create(Request $request, Shipping $ship)
{        
    $addresses = $request->user()->addresses()->get();

    if($addresses->isEmpty()) {
        // Là il faudra renvoyer l'utilisateur sur son compte quand on l'aura créé
    }

    $country_id = $addresses->first()->country_id;

    $shipping = $ship->compute($country_id);
    
    $content = Cart::getContent();

    $total = Cart::getTotal();

    $tax = Country::findOrFail($country_id)->tax;

    return view('command.index', compact('addresses', 'shipping', 'content', 'total', 'tax'));
}

Il nous faut toutes ces informations :

  • les adresses de l’utilisateur
  • l’identifiant du pays de la première adresse pour initialiser les frais de ports et la TVA
  • les frais d’expédition
  • les contenu du panier
  • le coût total du panier
  • la valeur de ta TVA

On va aussi partager les données de la boutique dans AppServiceProvider :

use App\Models\Shop;

...

public function boot()
{
    View::share('shop', Shop::firstOrFail());

Les vues

On va créer 3 vues pour la commande :

Les adresses

On aura pas mal de vues pour le compte de l’utilisateur, on prépare le terrain avec une vue partielle pour afficher une adresse :

Avec ce code :

<ul class="list-unstyled">
  @isset($address->name)
    <li>{{ "$address->civility $address->name $address->firstname" }}</li>
  @endif
  @if($address->company)
    <li>{{ $address->company }}</li>
  @endif            
  <li>{{ $address->address }}</li>
  @if($address->addressbis)
    <li>{{ $address->addressbis }}</li>
  @endif
  @if($address->bp)
    <li>{{ $address->bp }}</li>
  @endif
  <li>{{ "$address->postal $address->city" }}</li>
  <li>{{ $address->country->name }}</li>
  <li>{{ $address->phone }}</li>
</ul>

Maintenant on crée une vue pour afficher les adresses dans le formulaire de création de commande (partials/addresses.blade.php) qui utilise la vue partielle créée ci-dessus :

@if(!$addresses->count())
  <p>Vous n'avez pas encore créé d'adresse dans votre compte, vous devez en créer au moins une.</p>
  <br>
  <p><a href="#" style="width: 100%" class="btn waves-effect waves-light">Je crée une adresse</a>
  </p>
@else
  <div class="row">
    @foreach($addresses as $address)
      <div class="col m12 l6">
        <div class="card">                          
          <div class="card-content address">
            <p><label><input name="{{ $name }}" value="{{ $address->id }}" type="radio" @if($loop->first) checked @endif><span></span></label></p>
            @include('account.addresses.partials.address')
          </div>
        </div>
      </div>
    @endforeach
  </div>                  
@endif

Le détail de la commande

On crée aussi une vue partielle pour afficher le détail de la commande (partials/detail.blade.php) :

<h5>Détails de ma commande</h5>
@foreach ($content as $item)
  <hr><br>
  <div class="row">
    <div class="col m6 s12">
      {{ $item->name }} ({{ $item->quantity }} @if($item->quantity > 1) exemplaires) @else exemplaire) @endif
    </div>
    <div class="col m6 s12"><strong>{{ number_format($item->total_price_gross ?? ($tax > 0 ? $item->price : $item->price / 1.2) * $item->quantity, 2, ',', ' ') }} €</strong></div>
  </div>
@endforeach
<hr><br>
<div class="row" style="background-color: lightgrey">
  <div class="col s6">
    Livraison en Colissimo
  </div>
  <div class="col s6">
    <strong>{{ number_format($shipping, 2, ',', ' ') }} €</strong>
  </div>
</div>
@if($tax > 0)
  <div class="row" style="background-color: lightgrey">
    <div class="col s6">
      TVA à {{ $tax * 100 }}%
    </div>
    <div class="col s6">
      <strong>{{ number_format($total / (1 + $tax) * $tax, 2, ',', ' ') }} €</strong>
    </div>
  </div>
@endif
<div class="row" style="background-color: lightgrey">
  <div class="col s6">
    Total TTC
  </div>
  <div class="col s6">
    <strong>{{ number_format($total + $shipping, 2, ',', ' ') }} €</strong>
  </div>
</div>

Le formulaire

Enfin on a la vue du formulaire (command/index.blade.php) :

@extends('layouts.app')

@section('content')
<div class="container">
  <form id="form" action="{{ route('commandes.store') }}" method="POST">
    @csrf      
    @if(session()->has('message'))
      <h5 class="center-align red-text">{{ session('message') }}</h5>
      <br>
    @endif
    <ul class="collection with-header">

      <li class="collection-header"><h4>Ma commande</h4></li>

      <div id="wrapper">

      <li class="collection-item">
        <h5>Adresse de facturation <span id="solo">et de livraison</span></h5>
        @include('command.partials.addresses', ['name' => 'facturation'])
        <div class="row">
          <div class="col s12">
            <a href="#" class="btn" style="width: 100%"><i class="material-icons left">location_on</i>Gérer mes Adresses</a>
          </div>
        </div>
        <div class="row">
          <div class="col s12">
            <label>
              <input id="different" name="different" type="checkbox" @if($addresses->count() === 1)  disabled="disabled" @endif>
              <span>
                @if($addresses->count() === 1)
                  Vous n'avez qu'une adresse enregistrée, si vous voulez une adresse différente pour la livraison <a href="{{ route('adresses.create') }}">vous pouvez en créer une autre</a>.
                @else
                  Mon adresse de livraison est différente de mon adresse de facturation
                @endif
              </span>
            </label>
          </div>
        </div>
      </li>

      <li id="liLivraison" class="collection-item hide">
        <h5>Adresse de livraison</h5>
        @include('command.partials.addresses', ['name' => 'livraison'])      
      </li>

      <li class="collection-item">
        <h5>Mode de livraison</h5>
        <p>
          <label>
            <input name="expedition" type="radio" value="colissimo" checked>
            <span>Colissimo</span>
          </label>
        </p>
        <p>
          <label>
            <input name="expedition" type="radio" value="retrait">
            <span>Retrait sur place</span>
          </label>
        </p>
      </li>

      <li class="collection-item">
        <h5>Paiement</h5>
        @if($shop->card)
          <p>
            <label>
              <input class="payment" name="payment" type="radio" value="carte" checked>
              <span>Carte bancaire</span>
            </label>
          </p>
          <p style="margin-left: 40px" class="hide">
            Vous devrez renseigner un formulaire de paiement sur la page de confirmation de cette commande.
          </p>
        @endif
        @if($shop->mandat)
          <p>
            <label>
              <input class="payment" name="payment" type="radio" value="mandat">
              <span>Mandat administratif</span>
            </label>
          </p>
          <p style="margin-left: 40px" class="hide">
            Envoyez un bon de commande avec la mention "Bon pour accord". Votre commande sera expédiée dès réception de ce bon de commande. N'oubliez pas de préciser la référence de la commande dans votre bon.
          </p>
        @endif
        @if($shop->transfer)         
          <p>
            <label>
              <input class="payment" name="payment" type="radio" value="virement">
              <span>Virement bancaire</span>
            </label>
          </p>
          <p style="margin-left: 40px" class="hide">
            Il vous faudra transférer le montant de la commande sur notre compte bancaire. Vous recevrez votre confirmation de commande comprenant nos coordonnées bancaires et le numéro de commande. Les biens seront mis de côté 30 jours pour vous et nous traiterons votre commande dès la réception du paiement. 
          </p>
        @endif
        @if($shop->check)
          <p>
            <label>
              <input class="payment" name="payment" type="radio" value="cheque">
              <span>Chèque</span>
            </label>
          </p>
          <p style="margin-left: 40px" class="hide">
            Il vous faudra nous envoyer un chèque du montant de la commande. Vous recevrez votre confirmation de commande comprenant nos coordonnées bancaires et le numéro de commande. Les biens seront mis de côté 30 jours pour vous et nous traiterons votre commande dès la réception du paiement. 
          </p>
        @endif
      </li>

      <li id="detail" class="collection-item">
        @include('command.partials.detail')      
      </li> 
        
      <li class="collection-item">
        <h5>Veuillez vérifier votre commande avant le paiement !</h5>
        <br>
        <div class="row">
          <div class="col s12">
            <label>
              <input id="ok" name="ok" type="checkbox">
              <span>J'ai lu <a href="#" target="_blank">les conditions générales de vente et les conditions d'annulation</a> et j'y adhère sans réserve. </span>
            </label>
          </div>
        </div>
      </li>

      </div>

      <div id="loader" class="hide">
        <div class="loader"></div>
      </div>

    </ul>
    <div class="row">
      <div class="col s12">
        <button id="commande" type="submit" class="btn disabled" style="width: 100%">Commande avec obligation de paiement</button>
      </div>
    </div>
  </form>
</div>
@endsection

@section('javascript')
  <script>

    const changePayment = () => {
      document.querySelectorAll('.payment').forEach(payment => {
        const list = payment.parentNode.parentNode.nextElementSibling.classList;
        if(payment.checked) {
          list.remove('hide');
        } else {
          list.add('hide');
        } 
      });      
    };

    const getDetails = async () => {
      document.querySelector('#wrapper').classList.add('hide');
      document.querySelector('#loader').classList.remove('hide');
      const response = await fetch('#', { 
        method: 'POST',
        headers: { 
          'X-CSRF-TOKEN': '{{ csrf_token() }}', 
          'Content-Type': 'application/json' 
        },
        body: JSON.stringify({ 
          facturation: document.querySelector('input[type=radio][name=facturation]:checked').value, 
          livraison: document.querySelector('input[type=radio][name=livraison]:checked').value,
          different: document.querySelector('#different').checked,
          pick: document.querySelector('input[type=radio][name=expedition]:checked').value == 'retrait'
        })
      });
      const data = await response.json();
      document.querySelector('#detail').innerHTML = data.view;
      document.querySelector('#loader').classList.add('hide');
      document.querySelector('#wrapper').classList.remove('hide');      
    };

    document.addEventListener('DOMContentLoaded', () => {
      
      document.querySelector('#different').checked = false;
      
      document.querySelector('#ok').checked = false;
      
      document.querySelector('#different').addEventListener('change', () => {
        document.querySelector('#liLivraison').classList.toggle('hide');
        document.querySelector('#solo').classList.toggle('hide');
        getDetails();
      });

      document.querySelectorAll('.payment').forEach(payment => {
        payment.addEventListener('change', () => changePayment());
      });
      
      document.querySelector('#ok').addEventListener('change', () => document.querySelector('#commande').classList.toggle('disabled'));
      
      document.querySelectorAll('input[type=radio][name=facturation]').forEach(input => {
        input.addEventListener('change', () => getDetails());
      });

      document.querySelectorAll('input[type=radio][name=livraison]').forEach(input => {
        input.addEventListener('change', () => getDetails());
      });

      document.querySelectorAll('input[type=radio][name=expedition]').forEach(input => {
        input.addEventListener('change', () => {
          if(document.querySelector('input[type=radio][name=expedition][value=retrait]').checked) {            
            if(document.querySelector('#different').checked) {
              document.querySelector('#different').checked = false;              
              document.querySelector('#liLivraison').classList.toggle('hide');
            }
            document.querySelector('#different').disabled = true;  
            document.querySelector('#solo').classList.add('hide');          
          }
          if(document.querySelector('input[type=radio][name=expedition][value=colissimo]').checked) {            
            document.querySelector('#different').disabled = false;
            if(document.querySelector('#different').checked) {
              document.querySelector('#solo').classList.add('hide');
            } else {
              document.querySelector('#solo').classList.remove('hide');
            }          
          } 
          getDetails()
        });
      });

      document.querySelector('#form').addEventListener('submit', () => {
        const button = document.querySelector('#commande');
        button.classList.toggle('disabled');
        button.textContent = 'Confirmation de la commande en cours, ne fermez pas cette fenêtre...'
      });

      changePayment();
    });

  </script>
@endsection

C’est un peu chargé forcément parce qu’il y a pas mal d’informations et que j’ai opté pour un affichage globale sur une seule page.

Dans la partie supérieure on a les adresses :

On pourra préciser l’adresse de livraison différente. On verra ça plus loin, la case à cocher est en place. Le bouton de gestion des adresses n’est pas acif non plus parce qu’on a pas créé le compte client.

Au-dessous on a le mode de livraison :

On a deux options : livraison en Colissimo ou retrait sur place.

Ensuite on a le mode de paiement :

On peut déjà cliquer pour changer d’option, ça affiche à chaque fois un petit texte explicatif.

Vient ensuite le détail de la commande qui doit être calculé dynamiquement selon les options choisies :

Enfin dans la partie inférieure la case à cocher pour confirmer la lecture des conditions générales de vente et le bouton d’envoi actif si la case est cochée :

Calcul du détail

Le détail de la commande change si :

  • l’adresse d’expédition change (frais de port et éventuellement TVA différente)
  • le mode de livraison change (pas de frais de port en cas de retrait sur place)

Il faut donc prévoir un calcul dynamique. Le Javascript est déjà en place dans la vue, j’ai juste pas précisé la route pour éviter une erreur d’exécution.

On crée un contrôleur pour ce calcul :

php artisan make:controller DetailsController

On n’aura qu’une fonction :

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Services\Shipping;
use App\Models\ { Address, Country };
use Cart;

class DetailsController extends Controller
{
    /**
     * Show the order details
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Services\Shipping  $shipping
     * @return \Illuminate\Http\Response
     */
    public function __invoke(Request $request, Shipping $ship)
    {
        // Facturation
        $country_facturation = Address::findOrFail($request->facturation)->country;

        // Livraison
        $country_livraison = $request->different ? Address::findOrFail($request->livraison)->country : $country_facturation;
        $shipping = $request->pick ? 0 : $ship->compute($country_livraison->id);

        // TVA
        $tvaBase = Country::whereName('France')->first()->tax;
        $tax = $request->pick ? $tvaBase : $country_livraison->tax; 
        
        // Panier
        $content = Cart::getContent();
        $total = $tax > 0 ? Cart::getTotal() : Cart::getTotal() / (1 + $tvaBase);              

        return response()->json([ 
            'view' => view('command.partials.detail', compact('shipping', 'content', 'total', 'tax'))->render(), 
        ]);
    }
}

On génère et renvoie le HTML pour la partie du détail.

On ajoute la route :

// Utilisateur authentifié
Route::middleware('auth')->group(function () {
  // Commandes
  Route::prefix('commandes')->group(function () {
      Route::name('commandes.details')->post('details', 'DetailsController');

Il ne reste plus qu’à ajouter la route dans la vue command/index.blade.php au niveau du Javascript :

const response = await fetch('{{ route("commandes.details") }}', {

Fonctionnement

Maintenant tout doit fonctionner dans le formulaire de commande.

On peut préciser une adresse de livraison différente (s’il y a au moins deux adresses) :

A chaque changement l’affichage total du formulaire est remplacé par une image d’attente animée (la même qu’on a déjà utilisée précédemment tant qu’à faire) jusqu’à réponse du serveur. Ca évite que l’utilisateur ne clique encore ailleurs et lui indique que le traitement de son action est en cours.

En cas de changement de mode de livraison on a aussi un recalcul et une mise à jour. D’autre part si on choisit le retrait sur place le choix éventuel d’une adresse de livraison différente est supprimé et la case à cocher correspondante inactivée.

On va aussi prévoir à la fin du Javascript le déclenchement systématique du calcul du détail en cas de rechargement de la page alors que des options ont été changées :

  changePayment();
  getDetails();
});

Je ne rentre pas dans le détail du code pour ne pas trop alourdir cet article.

La création de la commande

Maintenant qu’on a notre formulaire il ne reste plus qu’à enregistrer la commande lors de la soumission. On code la méthode create du contrôleur OrderController :

use App\Models\ { Address, Country, State, Shop, Product, User };

...

/**
 * Store a newly created resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \App\Services\Shipping  $ship
 * @return \Illuminate\Http\Response
 */
public function store(Request $request, Shipping $ship)
{
    // Vérification du stock
    $items = Cart::getContent();
    foreach($items as $row) {
        $product = Product::findOrFail($row->id);
        if($product->quantity < $row->quantity) {
            $request->session()->flash('message', 'Nous sommes désolés mais le produit "' . $row->name . '" ne dispose pas d\'un stock suffisant pour satisfaire votre demande. Il ne nous reste plus que ' . $product->quantity . ' exemplaires disponibles.');
            return back();
        }
    }

    // Client
    $user = $request->user();

    // Facturation
    $address_facturation = Address::with('country')->findOrFail($request->facturation);

    // Livraison
    $address_livraison = $request->different ? Address::with('country')->findOrFail($request->livraison) : $address_facturation;
    $shipping = $request->expedition === 'colissimo' ? $ship->compute($address_livraison->country->id) : 0;

    // TVA
    $tvaBase = Country::whereName('France')->first()->tax;
    $tax = $request->expedition === 'colissimo' ? $address_livraison->country->tax : $tvaBase;

    // Enregistrement commande
    $order = $user->orders()->create([
        'reference' => strtoupper(Str::random(8)),
        'shipping' => $shipping,
        'tax' => $tax,
        'total' => $tax > 0 ? Cart::getTotal() : Cart::getTotal() / (1 + $tvaBase),
        'payment' => $request->payment,
        'pick' => $request->expedition === 'retrait',
        'state_id' => State::whereSlug($request->payment)->first()->id,
    ]);

    // Enregistrement adresse de facturation
    $order->adresses()->create($address_facturation->toArray());

    // Enregistrement éventuel adresse de livraison
    if($request->different) {
        $address_livraison->facturation = false;
        $order->adresses()->create($address_livraison->toArray());
    }

    // Enregistrement des produits
    foreach($items as $row) {
        $order->products()->create(
            [
                'name' => $row->name,
                'total_price_gross' => ($tax > 0 ? $row->price : $row->price / (1 + $tvaBase)) * $row->quantity,
                'quantity' => $row->quantity,
            ]
        );        
        // Mise à jour du stock
        $product = Product::findOrFail($row->id);
        $product->quantity -= $row->quantity;
        $product->save();
        // Alerte stock
        if($product->quantity <= $product->quantity_alert) {
            // Notifications à prévoir pour les administrateurs 
        }
    }

    // On vide le panier
    Cart::clear();
    Cart::session($request->user())->clear();

    // Notifications à prévoir pour les administrateurs et l'utilisateur

    //return redirect(route('commandes.confirmation', $order->id));
}

Je viens de me rendre compte que je n’avais pas prévu encore la propriété $fillable dans le modèle Order, on va l’ajouter :

protected $fillable = [
    'shipping', 'tax', 'user_id', 'state_id', 'payment', 'reference', 'pick', 'total',
];

On accomplit un certain nombre d’actions dans le contrôleur :

  • vérification du stock, si c’est insuffisant on retourne le formulaire avec une alerte :

  • on récupère l’adresse de facturation
  • on détermine l’adresse de livraison et on calcule les frais de port
  • on détermine le taux TVA
  • on enregistre la commande avec :
    • une référence générée aléatoirement
    • les frais de port
    • le taux de TVA
    • le coût total
    • le mode de paiement
    • le mode de livraison
    • l’état selon le mode de paiement
  • on enregistre l’adresse de facturation
  • on enregistre une éventuelle adresse de livraison
  • on enregistre les produits
  • on met à jour le stock
  • on vide le panier
  • on verra plus tard les notifications (alerte stock, commande créée pour les administrateurs et le client)

Chez moi ça fonctionne, la commande :

Les adresses :

Les produits :

Au retour on renverra une vue de confirmation de commande.

Conclusion

On a avancé dans la développement de la boutique. On peut maintenant passer une commande avec toutes les options disponibles. Dans le prochain article on verra la confirmation de la commande et le paiement par carte bancaire avec Stripe.

 

Print Friendly, PDF & Email

38 commentaires sur “Shopping : la commande

  1. Hello 🙂

    Très bon cours ! Merci Bestmomo pour le partage de vos connaissances, c’est grâce à ce site que je fais du Laravel depuis un certains temps maintenant et que j’adore ce framework !

    Je viens de terminer le cours et en lançant la commande php artisan migrate j’obtiens cette erreur

    SQLSTATE[42S02]: Base table or view not found: 1146 Table ‘shopping.shops’ doesn’t exist (SQL: select * from `shops` limit 1)

    Après un peu de recherche, l’erreur vient de l’appel à View::share(‘shop’, Shop::firstOrFail()); dans le fichier fichier App/Services/AppServiceProvider.php

    J’ai obtenu cette erreur en relançant le projet sur un environnement, pour la corriger j’ai englobé tout le contenu de la méthode boot dans cette condition

    if(!$this->app->runningInConsole()) {

    }

    Si il y en a dans le même cas que moi j’espère que ça aidera 🙂

    Bon dev à tous 🙂

  2. Salut bestmomo tout d’abord félicitation pour ce tuto … Quel travail …
    Pour ma part, au clic du bouton commander, il m’affiche cette erreur:
    Trying to get property ‘name’ of non-object
    Ayant déjà eu l’erreur ( Trying to get property ‘country_id’ of non-object) je me suis connecté via un profil déjà présent dans la bdd mais rien a faire l’erreur persiste.

  3. Bonsoir bestmomo le problème de ma page 404 not found vient au niveau de AppServiceProvider quand j’ajoute View::share(‘shop’, Shop::firstOrFail()); dans la function boot () mais quand j’enlève ça marche la page vient. Est ce que si je mets cette partie ça pourra poser des problèmes plus tard avec l’application c’est à dire est ce que obligatoire de le mettre ?

    1. bestmomo je comprends maintenant pourquoi avec View::share(‘shop’, Shop::firstOrFail()); ça faisait 404 not found
      en voyant la définition de firstOrFaill qui envoie le premier enregistrement trouvé dans la base de données. Si aucun modèle correspondant n’existe, il renvoie une erreur donc j’ai insérer un enregistrement dans la table shops directement dans phpMyAdmin j’aimerais savoir si on pouvait faire des enregistrement sans aller dans phpMyAdmin sans faire la partie backend

  4. Bonjour je débute sur laravel je suis entrain de suivre vos cours sur laravel 7 j’ai tout fais du début arrivé au niveau des panier mes produits ne s’affiche pas sur la page et depuis que je suis arrivé sur la partie commande rien se s’affiche sur ma page quand je tape localhost/shopping/public on me renvoit 404 not found j’ai même télécharger le zip du projet toujours pareille on m’affiche 404 not found si vous pouvez m’aider. Merci

          1. j’ai mis sur laragon toujours la même erreur 404 not found pour pouvoir y accéder L’URL c’est localhost/shopping/public non ?

          2. Laragon crée des hôtes avec un suffixe spécial, donc l’url c’est shopping.oo (là il faut utiliser le suffixe de Laragon, c’est peut-être test par défaut, je sais plus).

          3. oui j’ai regarder un peu la documentation de laragon on dit qu’il faut faire {name du projet}.test quand je fais shopping.test on m’affiche la page d’accueil de laragon n’importe quel dossier si je tape nom_du_projet.test on m’affiche ça si vous pouvez m’aider parce que je me sens perdu. Merdi

          4. j’ai pu ouvrir le dossier avec laravel avec l’url shopping.test on m’affiche toujours même erreur 404 not found

          5. avec localhost ça marche non plus toujours not found c’est depuis que je suis arrivée de la partie commande que ça affiche 404 not found sinon du début juste à la partie panier ça vient juste les produits qui s’affiche pas

  5. bonjour !
    j’ai une question mais avant tout je salue votre véritable geste et expérience , franchement parlant ce projet de shopping m’a aidé.
    comment la classe Services/Shipping a-t-il été crée par une cammande ou manuellement? car je l’ai faite manuellement mais après les importations j’ai eu l’erreur suivante : Trying to get property ‘country_id’ of non-object. Or cet attribut a été déclaré sur cette classe

    1. Salut,

      Il n’y a pas de commande Artisan pour créer un service, il faut le créer manuellement. Quant à l’erreur il semblerait que l’utilisateur n’ait pas créé d’adresse et comme on n’a pas encore créé la redirection vers son compte ça lance une erreur.

Laisser un commentaire