Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Creazione e gestione del carrello

In questo capitolo analizzeremo le operazioni necessarie per creare il carrello del nostro e-commerce basato su Laravel e le azioni ad esso associate
In questo capitolo analizzeremo le operazioni necessarie per creare il carrello del nostro e-commerce basato su Laravel e le azioni ad esso associate
Link copiato negli appunti

Parliamo ora di come creare il carrello del nostro e-commerce e le azioni ad esso associate. Cominciamo col rispondere ad una domanda: quale ruolo assegneremo al carrello? Ovviamente sarà una classe, ma quale tipo di classe?

Per rispondere a questa domanda soffermiamoci sul terzo componente del pattern MVC, il Controller.

In Inglese la parola controller indica "a person or thing that directs or regulates something". Il controller lavora quindi sui dati in input e produce un risultato. Nel nostro caso il risultato sarà la modifica della sessione PHP di Laravel per permettere ai prodotti e al totale del carrello di sopravvivere al cambio di pagina (il protocollo HTTP è stateless, quindi non permette by design di avere una persistenza dei dati).

I metodi del carrello

Definiamo quindi il controller Cart.

namespace App\Http\Controllers;
use App\Product;
class Cart {
    private $items;
    private $total;
    public function __construct() {
        $this->items = [];
        $this->total = 0.00;
    }
    //...
}

Partiamo sempre da un carrello vuoto in modo da agevolare la sincronizzazione con la sessione. Il totale e i prodotti sono membri privati in quanto vogliamo preservare l'incapsulamento. Per svuotare il carrello si azzera il totale e si resetta l'array dei prodotti:

public function emptyCart() {
        $this->items = [];
        $this->total = 0.00;
}

A questo punto definiamo i metodi getter e setter per l'array di prodotti nel carrello.

public function setItems($items) {
        $this->items = $items;
    }
    public function getItems() {
        $items = [];
        if($this->hasItems()) {
            foreach($this->items as $item) {
                $items[] = [
                        'id' => $item['id'],
                        'name' => $item['name'],
                        'description' => $item['description'],
                        'price' => $item['price'],
                        'manufacturer' => $item['manufacturer'],
                        'image' => $item['image'],
                        'quantity' => $item['quantity'],
                        'subtotal' => $item['subtotal'],
                        'slug' => $item['slug']
                ];
            }
        }
        return $items;
}

Quindi creiamo i metodi getter e setter per il totale.

public function setTotal($value) {
        $this->total = $value;
}
public function getTotal() {
        return $this->total;
}

Il metodo per l'aggiornamento del carrello richiede un modello del prodotto e la nuova quantità scelta dall'utente. Si tratta quindi di aggiornare quantità e subtotale modificando un array PHP.

public function updateCart(Product $product, $quantity) {
        if($this->hasItems()) {
            foreach($this->items as &$item)  {
                if($product->id == $item['id']) {
                    $item['quantity'] = $quantity;
                    $item['subtotal'] = ($product->price * $quantity);
                    $this->calculateTotal();
                }
            }
        }
    }

Il metodo per la rimozione dal carrello richiede un modello di prodotto e consiste nell'eliminazione di un elemento da un array PHP.

public function removeFromCart(Product $product) {
        if($this->hasItems()) {
            $i = -1;
            foreach($this->items as $item) {
                $i++;
                if($product->id == $item['id']) {
                    unset($this->items[$i]);
                    $this->calculateTotal();
                }
            }
        }
    }

Il metodo per l'aggiunta di un prodotto richiede il modello di un prodotto e la quantità selezionata. Se la quantità non è valida o il prodotto è già presente l'operazione viene annullata.

public function addToCart(Product $product, $quantity) {
        if($quantity < 1 || $this->isInCart($product)) {
            return;
        }
        $item = [
            'id' => $product->id,
            'name' => $product->title,
            'description' => $product->description,
            'price' => $product->price,
            'manufacturer' => $product->manufacturer,
            'image' => $product->image,
            'quantity' => $quantity,
            'subtotal' => ($product->price * $quantity),
            'slug' => $product->slug
        ];
        $this->items[] = $item;
        $this->calculateTotal();
    }

Il metodo che segue verifica se un prodotto è già presente nel carrello per evitare duplicati.

private function isInCart(Product $product) {
        if( $this->hasItems()) {
           foreach( $this->items as $item ) {
               if($item['id'] == $product->id) {
                   return true;
               }
           }
           return false;
        } else {
            return false;
        }
    }

Il metodo per calcolare il totale sempre da zero ed esegue la somma di tutti i totali parziali.

private function calculateTotal() {
        $this->total = 0.00;
        if($this->hasItems()) {
            $tot = 0.00;
            foreach($this->items as $item) {
                $tot += $item['subtotal'];
            }
            $this->total = $tot;
        }
    }

Infine, l'ultimo metodo della classe verifica se effettivamente sono presenti prodotti nel carrello.

private function hasItems() {
    return ( count( $this->items ) > 0 );
}

Logica di lettura, scrittura e sincronizzazione

Teniamo a mente un aspetto fondamentale: quando usiamo questa classe stiamo operando sulla memoria di PHP (in memory), quindi i dati sono ancora volatili.

Per rendere tali dati permanenti dobbiamo necessariamente eseguire delle operazioni di lettura e scrittura nella sessione corrente. In altre parole dobbiamo sempre sincronizzare i dati presenti in memoria con quellinella sessione.

Partiamo dall'aggiunta di un prodotto al carrello. Il client invia l'ID del prodotto e la quantità selezionata ed il metodo del nostro controller delle route dedicate al carrello effettua l'operazione.

public function add(Request $request, $id = '0')
    {
        $product_id = $id;
        $quantity = 1;
        if ($request->has(['id', 'quantity'])) {
            $product_id = $request->input('id');
            $quantity = (int) $request->input('quantity');
        }
        $product = Product::find($product_id);
        if(is_null($product)) {
            return redirect()->route('home');
        }
        $cart = new Cart();
        $sessionCart = $request->session()->get('cart');
        if(!$sessionCart) {
            $cart->addToCart($product, $quantity);
            $request->session()->put(['cart' => ['items' => $cart->getItems(), 'total' => $cart->getTotal()]]);
        } else {
            $cart->setItems($sessionCart['items']);
            $cart->setTotal($sessionCart['total']);
            $cart->addToCart($product, $quantity);
            $request->session()->put(['cart' => ['items' => $cart->getItems(), 'total' => $cart->getTotal()]]);
        }
        return redirect()->route('cart');
    }

Come si può notare dall'implementazione di questo metodo, prima viene effettuata l'operazione in memoria tramite i metodi del controller Cart e solo in un secondo momento i dati vengono salvati e sincronizzati con la sessione tramite i metodi dell'oggetto session() di Laravel.

Il metodo funziona in due modalità: l'aggiunta al carrello dall'elenco dei prodotti (con quantità fissa pari a 1) e l'aggiunta con quantità variabile dalla pagina del singolo prodotto. In entrambi i casi il client passa l'ID del prodotto in modo che Laravel reperisca il dato tramite il metodo find() del modello.

Il funzionamento del metodo che rimuove un prodotto dal carrello è analogo nel design ma con esito opposto.

public function cartRemove(Request $request)
    {
        $id = (int) $request->input('id');
        $product = Product::find($id);
        $cart = new Cart();
        $sessionCart = $request->session()->get('cart');
        $cart->setItems($sessionCart['items']);
        $cart->setTotal($sessionCart['total']);
        $cart->removeFromCart($product);
        $request->session()->put(['cart' => ['items' => $cart->getItems(), 'total' => $cart->getTotal()]]);
        return redirect()->route('cart');
    }

Come sempre, viene istanziata la classe Cart, vengono impostati i suoi dati prendendoli da quelli presenti nella sessione corrente e quindi viene invocato il suo metodo specifico per la rimozione di un prodotto dal carrello. Infine vengono nuovamente sincronizzati i dati del carrello con quelli presenti in sessione.

L'aggiornamento del carrello non è altro che la terza applicazione della nostra logica di lettura/scrittura/sincronizzazione.

public function cartUpdate(Request $request)
    {
        $qty = $request->input('qty');
        $parts = explode(',', $qty);
        $cart = new Cart();
        $sessionCart = $request->session()->get('cart');
        $cart->setItems($sessionCart['items']);
        $cart->setTotal($sessionCart['total']);
        foreach($parts as $part) {
            $qtys = explode('-', $part);
            $id = (int) $qtys[0];
            $quantity = (int) $qtys[1];
            $product = Product::find($id);
            $cart->updateCart($product, $quantity);
        }
        $request->session()->put(['cart' => ['items' => $cart->getItems(), 'total' => $cart->getTotal()]]);
        return redirect()->route('cart');
    }

Conclusione

La gestione e la sincronizzazione della memoria con la sessione corrente sono i requisiti fondamentali per poter creare e gestire un carrello evitando disallineamenti tra le operazioni e i risultati mostrati nel frontend.

Ti consigliamo anche