How to Create a Shopping Cart With Laravel Livewire

If you’re new to Laravel or web development in general and trying to build practice projects, it’s highly likely that you’ll want to build an e-commerce store of some sort. Being someone who has worked on a number of e-commerce platforms, I can tell that one of the most complicated features of an e-commerce store for a first-timer is the Laravel shopping cart.

Although it sounds pretty simple on paper, once you start to implement the cart yourself, you’ll quickly realize that there are a lot of cases that you have to account for, and there are lots of ways to create a cart.

In this article, I’ll show the Laravel developers how you can create a session-based Laravel Livewire shopping cart. Keep in mind, that I’m assuming that you already know the basics of Laravel and Livewire. I also have a reference project repository for this article that you may look at.

The Database Setup

Before jumping into coding the cart, you’ll have to set up the necessary models and migrations. In this example, all you need is a Product model and a migration for the products table. You can use artisan to create the model and the migrations. The code for the model is as follows:

<?php
namespace App\Models;
use App\Contracts\Cartable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
    use HasFactory;
    // you only live once
    protected $guarded = [];
    public function getUnitPriceAttribute($value)
    {
        return number_format($value, 2);
    }
}

The code for the migration file is as follows:

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateProductsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->text('description');
            $table->float('unit_price', 8, 2, true);
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('products');
    }
}

I also have factory for the products to start with some data on my database from the get go. The code for the factory as follows:

<?php
namespace Database\Factories;
use App\Models\Product;
use Illuminate\Database\Eloquent\Factories\Factory;
class ProductFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = Product::class;
    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            'name' => $this->faker->word(),
            'description' => $this->faker->paragraph(2),
            'unit_price' => $this->faker->randomFloat(2, 50, 5000),
        ];
    }
}

The aforementioned reference project comes with all of these built-in so I would suggest you use that as your starting point.

The Cart Service

Again, if you’re a beginner you may think that the best place to write the cart logic is a controller may be called CartController but I have a better idea. You see if you put a set of logic within a controller that logic can only be invoked by dispatching a request to the corresponding endpoint. But with Livewire, you want to put the logic somewhere that can be accessed even outside of a request. In this article, we’ll not have any controller whatsoever. We’ll have some Livewire Laravel components and a service class.

The idea of service classes is not something built into the framework or documented in the official docs. As a result, different people refer to them differently. At the end of the day, service classes are plain classes responsible for holding the business logic.

Now where to keep these classes is totally up to you, but I prefer putting it within the app/Services directory. I’m naming the file as CartService.php and start by following lines of code there:

<?php

namespace App\Services;

use Illuminate\Support\Collection;
use Illuminate\Session\SessionManager;

class CartService {
    const MINIMUM_QUANTITY = 1;
    const DEFAULT_INSTANCE = 'shopping-cart';

    protected $session;
    protected $instance;

    /**
     * Constructs a new cart object.
     *
     * @param Illuminate\Session\SessionManager $session
     */
    public function __construct(SessionManager $session)
    {
        $this->session = $session;
    }
}

As you may have guessed, we’ll build this class step by step. There are two protected variables and two constants in this class. The MINIMUM_QUANTITY is the minimum quantity of a product that has to exist in the cart. Assume someone has added multiple units of a product in the cart and then they want to discard some of those unit. The lowest they can go is the value of this constant.

The next constant is the DEFAULT_INSTANCE which is simply the name of the cart object in the session. Finally, the $session variable is going to be an instance of the Illuminate\Session\SessionManager class and the $instance is going to be a reference to the current instance of the cart.

If you don’t understand everything clearly at the moment don’t worry, you’ll soon understand all of it. There is also the class constructor that takes an instance of the Illuminate\Session\SessionManager class and saves it into the $session variable.

The next thing you want to do is create two protected methods. One named createCartItem which will accept a bunch of information about a cart item and return a well formatted collection to store in the cart.

The second protected method will be, getContent responsible for returning all the content of the cart as a collection. Update the code as follows:

<?php

namespace App\Services;

use Illuminate\Support\Collection;
use Illuminate\Session\SessionManager;

class CartService {
    const MINIMUM_QUANTITY = 1;
    const DEFAULT_INSTANCE = 'shopping-cart';

    protected $session;
    protected $instance;

    /**
     * Constructs a new cart object.
     *
     * @param Illuminate\Session\SessionManager $session
     */
    public function __construct(SessionManager $session)
    {
        $this->session = $session;
    }

    /**
     * Returns the content of the cart.
     *
     * @return Illuminate\Support\Collection
     */
    protected function getContent(): Collection
    {
        return $this->session->has(self::DEFAULT_INSTANCE) ? $this->session->get(self::DEFAULT_INSTANCE) : collect([]);
    }

    /**
     * Creates a new cart item from given inputs.
     *
     * @param string $name
     * @param string $price
     * @param string $quantity
     * @param array $options
     * @return Illuminate\Support\Collection
     */
    protected function createCartItem(string $name, string $price, string $quantity, array $options): Collection
    {
        $price = floatval($price);
        $quantity = intval($quantity);

        if ($quantity < self::MINIMUM_QUANTITY) {
            $quantity = self::MINIMUM_QUANTITY;
        }

        return collect([
            'name' => $name,
            'price' => $price,
            'quantity' => $quantity,
            'options' => $options,
        ]);
    }
}

The getContent function checks if the session has a cart object or not. If yes, returns the cart object and if not, returns an empty cart. Here, the cart object is an instance of the Illuminate\Support\Collection class.

The createCartItem method on the other hand, accepts the name, price, quantity and options of a given item and then returns them in the form of a collection. This returned object is what we consider a cart item. Considering the received values will be in string format, the price and quantity will have to be converted to float and integer respectively. We’ll talk about options later on.

Now that you have finished the two protected methods, it’s time to start writing the public methods. You’ll have six public methods, each performing a certain cart action and accessible from the Livewire Laravel components. Let’s begin with the add to cart functionality. Update the code as follows:

<?php

namespace App\Services;

use Illuminate\Support\Collection;
use Illuminate\Session\SessionManager;

class CartService {
    const MINIMUM_QUANTITY = 1;
    const DEFAULT_INSTANCE = 'shopping-cart';

    protected $session;
    protected $instance;

    /**
     * Constructs a new cart object.
     *
     * @param Illuminate\Session\SessionManager $session
     */
    public function __construct(SessionManager $session)
    {
        $this->session = $session;
    }

    /**
     * Adds a new item to the cart.
     *
     * @param string $id
     * @param string $name
     * @param string $price
     * @param string $quantity
     * @param array $options
     * @return void
     */
    public function add($id, $name, $price, $quantity, $options = []): void
    {
        $cartItem = $this->createCartItem($name, $price, $quantity, $options);

        $content = $this->getContent();

        if ($content->has($id)) {
            $cartItem->put('quantity', $content->get($id)->get('quantity') + $quantity);
        }

        $content->put($id, $cartItem);

        $this->session->put(self::DEFAULT_INSTANCE, $content);
    }

    /**
     * Returns the content of the cart.
     *
     * @return Illuminate\Support\Collection
     */
    protected function getContent(): Collection
    {
        return $this->session->has(self::DEFAULT_INSTANCE) ? $this->session->get(self::DEFAULT_INSTANCE) : collect([]);
    }

    /**
     * Creates a new cart item from given inputs.
     *
     * @param string $name
     * @param string $price
     * @param string $quantity
     * @param array $options
     * @return Illuminate\Support\Collection
     */
    protected function createCartItem(string $name, string $price, string $quantity, array $options): Collection
    {
        $price = floatval($price);
        $quantity = intval($quantity);

        if ($quantity < self::MINIMUM_QUANTITY) {
            $quantity = self::MINIMUM_QUANTITY;
        }

        return collect([
            'name' => $name,
            'price' => $price,
            'quantity' => $quantity,
            'options' => $options,
        ]);
    }
}

The add method receives the id, name, price, quantity, options for a product from the user. The options is initialized as an empty array. Once the method has the information, it creates a new cart item by calling the createCartItem protected method.

Then the method gets the cart content by calling the getContent protected method and checks if the product already exists in the cart. If yes, the method updates the quantity of the car item in the cart and puts and updates the cart object. Otherwise the method just puts the cart item in the cart and moves on.

The next method you’ll be working on is the update methods used for updating a cart item in the cart. This one’s going to be a bit more complicated than the add method. Update your code as follows:

<?php

namespace App\Services;

use Illuminate\Support\Collection;
use Illuminate\Session\SessionManager;

class CartService {
    const MINIMUM_QUANTITY = 1;
    const DEFAULT_INSTANCE = 'shopping-cart';

    protected $session;
    protected $instance;

    /**
     * Constructs a new cart object.
     *
     * @param Illuminate\Session\SessionManager $session
     */
    public function __construct(SessionManager $session)
    {
        $this->session = $session;
    }

    /**
     * Adds a new item to the cart.
     *
     * @param string $id
     * @param string $name
     * @param string $price
     * @param string $quantity
     * @param array $options
     * @return void
     */
    public function add($id, $name, $price, $quantity, $options = []): void
    {
        $cartItem = $this->createCartItem($name, $price, $quantity, $options);

        $content = $this->getContent();

        if ($content->has($id)) {
            $cartItem->put('quantity', $content->get($id)->get('quantity') + $quantity);
        }

        $content->put($id, $cartItem);

        $this->session->put(self::DEFAULT_INSTANCE, $content);
    }

    /**
     * Updates the quantity of a cart item.
     *
     * @param string $id
     * @param string $action
     * @return void
     */
    public function update(string $id, string $action): void
    {
        $content = $this->getContent();

        if ($content->has($id)) {
            $cartItem = $content->get($id);

            switch ($action) {
                case 'plus':
                    $cartItem->put('quantity', $content->get($id)->get('quantity') + 1);
                    break;
                case 'minus':
                    $updatedQuantity = $content->get($id)->get('quantity') - 1;

                    if ($updatedQuantity < self::MINIMUM_QUANTITY) {
                        $updatedQuantity = self::MINIMUM_QUANTITY;
                    }

                    $cartItem->put('quantity', $updatedQuantity);
                    break;
            }

            $content->put($id, $cartItem);

            $this->session->put(self::DEFAULT_INSTANCE, $content);
        }
    }

    /**
     * Returns the content of the cart.
     *
     * @return Illuminate\Support\Collection
     */
    protected function getContent(): Collection
    {
        return $this->session->has(self::DEFAULT_INSTANCE) ? $this->session->get(self::DEFAULT_INSTANCE) : collect([]);
    }

    /**
     * Creates a new cart item from given inputs.
     *
     * @param string $name
     * @param string $price
     * @param string $quantity
     * @param array $options
     * @return Illuminate\Support\Collection
     */
    protected function createCartItem(string $name, string $price, string $quantity, array $options): Collection
    {
        $price = floatval($price);
        $quantity = intval($quantity);

        if ($quantity < self::MINIMUM_QUANTITY) {
            $quantity = self::MINIMUM_QUANTITY;
        }

        return collect([
            'name' => $name,
            'price' => $price,
            'quantity' => $quantity,
            'options' => $options,
        ]);
    }
}

The update method can wither increase or decrease the quantity of a cart item. All it needs to know is the id of the cart item and whether you want to increase or decrease its quantity. Once it has those information, the method retrieves the cart content, checks if the given item exists or not. If it exists, then based on the attempted action type, it either increases or decreases the item quantity.

The next two public methods are the remove and the clear methods responsible for removing a simple item from the cart and all items from the cart respectively. Update your code as follows:

<?php

namespace App\Services;

use Illuminate\Support\Collection;
use Illuminate\Session\SessionManager;

class CartService {
    const MINIMUM_QUANTITY = 1;
    const DEFAULT_INSTANCE = 'shopping-cart';

    protected $session;
    protected $instance;

    /**
     * Constructs a new cart object.
     *
     * @param Illuminate\Session\SessionManager $session
     */
    public function __construct(SessionManager $session)
    {
        $this->session = $session;
    }

    /**
     * Adds a new item to the cart.
     *
     * @param string $id
     * @param string $name
     * @param string $price
     * @param string $quantity
     * @param array $options
     * @return void
     */
    public function add($id, $name, $price, $quantity, $options = []): void
    {
        $cartItem = $this->createCartItem($name, $price, $quantity, $options);

        $content = $this->getContent();

        if ($content->has($id)) {
            $cartItem->put('quantity', $content->get($id)->get('quantity') + $quantity);
        }

        $content->put($id, $cartItem);

        $this->session->put(self::DEFAULT_INSTANCE, $content);
    }

    /**
     * Updates the quantity of a cart item.
     *
     * @param string $id
     * @param string $action
     * @return void
     */
    public function update(string $id, string $action): void
    {
        $content = $this->getContent();

        if ($content->has($id)) {
            $cartItem = $content->get($id);

            switch ($action) {
                case 'plus':
                    $cartItem->put('quantity', $content->get($id)->get('quantity') + 1);
                    break;
                case 'minus':
                    $updatedQuantity = $content->get($id)->get('quantity') - 1;

                    if ($updatedQuantity < self::MINIMUM_QUANTITY) {
                        $updatedQuantity = self::MINIMUM_QUANTITY;
                    }

                    $cartItem->put('quantity', $updatedQuantity);
                    break;
            }

            $content->put($id, $cartItem);

            $this->session->put(self::DEFAULT_INSTANCE, $content);
        }
    }

    /**
     * Removes an item from the cart.
     *
     * @param string $id
     * @return void
     */
    public function remove(string $id): void
    {
        $content = $this->getContent();

        if ($content->has($id)) {
            $this->session->put(self::DEFAULT_INSTANCE, $content->except($id));
        }
    }

    /**
     * Clears the cart.
     *
     * @return void
     */
    public function clear(): void
    {
        $this->session->forget(self::DEFAULT_INSTANCE);
    }

    /**
     * Returns the content of the cart.
     *
     * @return Illuminate\Support\Collection
     */
    protected function getContent(): Collection
    {
        return $this->session->has(self::DEFAULT_INSTANCE) ? $this->session->get(self::DEFAULT_INSTANCE) : collect([]);
    }

    /**
     * Creates a new cart item from given inputs.
     *
     * @param string $name
     * @param string $price
     * @param string $quantity
     * @param array $options
     * @return Illuminate\Support\Collection
     */
    protected function createCartItem(string $name, string $price, string $quantity, array $options): Collection
    {
        $price = floatval($price);
        $quantity = intval($quantity);

        if ($quantity < self::MINIMUM_QUANTITY) {
            $quantity = self::MINIMUM_QUANTITY;
        }

        return collect([
            'name' => $name,
            'price' => $price,
            'quantity' => $quantity,
            'options' => $options,
        ]);
    }
}

The remove method takes a cart item id and removes it from the cart collection and the clear method simply drops the cart collection from the session completely. Finally there are two more public methods left. They are the total and content methods responsible for returning the cart total and the content of the cart to the front end. Update your code as follows:

<?php

namespace App\Services;

use Illuminate\Support\Collection;
use Illuminate\Session\SessionManager;

class CartService {
    const MINIMUM_QUANTITY = 1;
    const DEFAULT_INSTANCE = 'shopping-cart';

    protected $session;
    protected $instance;

    /**
     * Constructs a new cart object.
     *
     * @param Illuminate\Session\SessionManager $session
     */
    public function __construct(SessionManager $session)
    {
        $this->session = $session;
    }

    /**
     * Adds a new item to the cart.
     *
     * @param string $id
     * @param string $name
     * @param string $price
     * @param string $quantity
     * @param array $options
     * @return void
     */
    public function add($id, $name, $price, $quantity, $options = []): void
    {
        $cartItem = $this->createCartItem($name, $price, $quantity, $options);

        $content = $this->getContent();

        if ($content->has($id)) {
            $cartItem->put('quantity', $content->get($id)->get('quantity') + $quantity);
        }

        $content->put($id, $cartItem);

        $this->session->put(self::DEFAULT_INSTANCE, $content);
    }

    /**
     * Updates the quantity of a cart item.
     *
     * @param string $id
     * @param string $action
     * @return void
     */
    public function update(string $id, string $action): void
    {
        $content = $this->getContent();

        if ($content->has($id)) {
            $cartItem = $content->get($id);

            switch ($action) {
                case 'plus':
                    $cartItem->put('quantity', $content->get($id)->get('quantity') + 1);
                    break;
                case 'minus':
                    $updatedQuantity = $content->get($id)->get('quantity') - 1;

                    if ($updatedQuantity < self::MINIMUM_QUANTITY) {
                        $updatedQuantity = self::MINIMUM_QUANTITY;
                    }

                    $cartItem->put('quantity', $updatedQuantity);
                    break;
            }

            $content->put($id, $cartItem);

            $this->session->put(self::DEFAULT_INSTANCE, $content);
        }
    }

    /**
     * Removes an item from the cart.
     *
     * @param string $id
     * @return void
     */
    public function remove(string $id): void
    {
        $content = $this->getContent();

        if ($content->has($id)) {
            $this->session->put(self::DEFAULT_INSTANCE, $content->except($id));
        }
    }

    /**
     * Clears the cart.
     *
     * @return void
     */
    public function clear(): void
    {
        $this->session->forget(self::DEFAULT_INSTANCE);
    }

    /**
     * Returns the content of the cart.
     *
     * @return Illuminate\Support\Collection
     */
    public function content(): Collection
    {
        return is_null($this->session->get(self::DEFAULT_INSTANCE)) ? collect([]) : $this->session->get(self::DEFAULT_INSTANCE);
    }

    /**
     * Returns total price of the items in the cart.
     *
     * @return string
     */
    public function total(): string
    {
        $content = $this->getContent();

        $total = $content->reduce(function ($total, $item) {
            return $total += $item->get('price') * $item->get('quantity');
        });

        return number_format($total, 2);
    }

    /**
     * Returns the content of the cart.
     *
     * @return Illuminate\Support\Collection
     */
    protected function getContent(): Collection
    {
        return $this->session->has(self::DEFAULT_INSTANCE) ? $this->session->get(self::DEFAULT_INSTANCE) : collect([]);
    }

    /**
     * Creates a new cart item from given inputs.
     *
     * @param string $name
     * @param string $price
     * @param string $quantity
     * @param array $options
     * @return Illuminate\Support\Collection
     */
    protected function createCartItem(string $name, string $price, string $quantity, array $options): Collection
    {
        $price = floatval($price);
        $quantity = intval($quantity);

        if ($quantity < self::MINIMUM_QUANTITY) {
            $quantity = self::MINIMUM_QUANTITY;
        }

        return collect([
            'name' => $name,
            'price' => $price,
            'quantity' => $quantity,
            'options' => $options,
        ]);
    }
}

The content method checks if a cart collection exists and returns an empty collection if it doesn’t. The total method sums up the total cost of the cart and returns it rounded off to two decimal points. That’s pretty much it, this is the final service class. Next is creating the Livewire components.

The Livewire Components

Again, I’m assuming that you know the basics of Laravel and Livewire so I’ll not walk you through step by step on how to create Livewire components and so on. I’ve made two Laravel Livewire examples. The first one is the product component that represents a single product shown to the user and the cart component responsible for showing the state of the cart.

On the left, each box showing a product information is a product component and on the right side, the entire cart view uses the cart component. The code for the app/Http/Livewire/ProductComponent.php file is as follows:

<?php
namespace App\Http\Livewire;
use App\Facades\Cart;
use Livewire\Component;
use Illuminate\Contracts\View\View;
class ProductComponent extends Component
{
    public $product;
    public $quantity;
    /**
     * Mounts the component on the template.
     *
     * @return void
     */
    public function mount(): void
    {
        $this->quantity = 1;
    }
        /**
     * Renders the component on the browser.
     *
     * @return \Illuminate\Contracts\View\View
     */
    public function render(): View
    {
        return view('livewire.product');
    }
    /**
     * Adds an item to cart.
     *
     * @return void
     */
    public function addToCart(): void
    {
        Cart::add($this->product->id, $this->product->name, $this->product->getRawOriginal('unit_price'), $this->quantity);
        $this->emit('productAddedToCart');
    }
}

A very straight forward component that takes the product and the quantity of the product as public properties. The product here is a product retrieved from the database. There is an addToCart method that’ll be invoked by the Add To Cart buttons from the front end.

This method also emits an event called productAddedToCart so that the cart component can be updated whenever a new product is added. Finally setting the quantity to 1 in the mount method sets the default quantity to 1 in the front end.

The code for the resources/views/liveiwire/product.blade.php which serves as the template for this component is as follows:

<div class="p-5 mx-2 my-2 max-w-md rounded border-2">
    <h1 class="text-3xl mb-2">{{ $product->name }} - ${{ $product->unit_price }}</h1>
    <p class="text-lg mb-2">{{ $product->description }}</p>
    <input class="mb-2 border-2 rounded" type="number" min="1" wire:model="quantity">
    <button class="p-2 border-2 rounded border-blue-500 hover:border-blue-600 bg-blue-500 hover:bg-blue-600" wire:click="addToCart">Add To Cart</button>
</div>

This component will later be used in another template file. Now the code for the app/Http/Livewire/CartComponent.php is as follows:

<?php
namespace App\Http\Livewire;
use App\Facades\Cart;
use Livewire\Component;
use Illuminate\Contracts\View\View;
class CartComponent extends Component
{
    protected $total;
    protected $content;
    protected $listeners = [
        'productAddedToCart' => 'updateCart',
    ];
    /**
     * Mounts the component on the template.
     *
     * @return void
     */
    public function mount(): void
    {
        $this->updateCart();
    }
    /**
     * Renders the component on the browser.
     *
     * @return \Illuminate\Contracts\View\View
     */
    public function render(): View
    {
        return view('livewire.cart', [
            'total' => $this->total,
            'content' => $this->content,
        ]);
    }
    /**
     * Removes a cart item by id.
     *
     * @param string $id
     * @return void
     */
    public function removeFromCart(string $id): void
    {
        Cart::remove($id);
        $this->updateCart();
    }
    /**
     * Clears the cart content.
     *
     * @return void
     */
    public function clearCart(): void
    {
        Cart::clear();
        $this->updateCart();
    }
    /**
     * Updates a cart item.
     *
     * @param string $id
     * @param string $action
     * @return void
     */
    public function updateCartItem(string $id, string $action): void
    {
        Cart::update($id, $action);
        $this->updateCart();
    }
    /**
     * Rerenders the cart items and total price on the browser.
     *
     * @return void
     */
    public function updateCart()
    {
        $this->total = Cart::total();
        $this->content = Cart::content();
    }
}

This component has methods for calling the update, remove and clear methods from the cart service. These methods will be invoked by buttons from the front end. It has the total cost of the cart, the content of the cart as protected properties and it listens to the productAddedToCart event. Whenever this event is fired, the updateCart method will be called and the total cost of cart along with the cart content will be updated.

The code for the resources/views/liveiwire/cart.blade.php which serves as the template for this component is as follows:

<div class="w-1/4">
    <div class="p-5 mx-2 my-2 max-w-md rounded border-2">
        @if ($content->count() > 0)
        @foreach ($content as $id => $item)
        <p class="text-2xl text-right mb-2">
            <button class="text-sm p-2 border-2 rounded border-gray-200 hover:border-gray-300 bg-gray-200 hover:bg-gray-300" wire:click="updateCartItem({{ $id }}, 'minus')"> - </button>
            {{ $item->get('name') }} x {{ $item->get('quantity') }}
            <button class="text-sm p-2 border-2 rounded border-gray-200 hover:border-gray-300 bg-gray-200 hover:bg-gray-300" wire:click="updateCartItem({{ $id }}, 'plus')"> + </button>
            <button class="text-sm p-2 border-2 rounded border-red-500 hover:border-red-600 bg-red-500 hover:bg-red-600" wire:click="removeFromCart({{ $id }})">Remove</button>
        </p>
        @endforeach
        <hr class="my-2">
        <p class="text-xl text-right mb-2">Total: ${{ $total }}</p>
        <button class="w-full p-2 border-2 rounded border-red-500 hover:border-red-600 bg-red-500 hover:bg-red-600" wire:click="clearCart">Clear Cart</button>
        @else
        <p class="text-3xl text-center mb-2">cart is empty!</p>
        @endif
    </div>
</div>

Finally these two components are used in the resources/views/index.blade.php file as follows:

<x-guest-layout>
    <div class="flex">
        <div class="my-5 flex justify-center w-3/4"><h1 class="underline text-5xl">Products</h1></div>
        <div class="my-5 flex justify-center w-1/4"><h1 class="underline text-5xl">Cart</h1></div>
    </div>
    <div class="flex">
        <div class="flex flex-wrap justify-between w-3/4">
            @foreach ($products as $product)
                <livewire:product-component :product='$product' />
            @endforeach
        </div>
        <livewire:cart-component />
    </div>
</x-guest-layout>

This uses the resources/views/layouts/guest.blade.php as the layout and its code is as follows:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" content="{{ csrf_token() }}">
        <title>{{ config('app.name', 'Laravel') }}</title>
        <!-- Fonts -->
        <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap">
        <!-- Styles -->
        <link rel="stylesheet" href="{{ asset('css/app.css') }}">
        @livewireStyles
        <!-- Scripts -->
        <script src="{{ asset('js/app.js') }}" defer></script>
    </head>
    <body>
        <nav class="p-5 flex justify-between shadow">
            <a class="rounded text-3xl" href="{{ route('index') }}">Shopping Cart</a>
            <div>
                <a class="p-2 border-2 rounded bg-gray-200 hover:bg-gray-300" href="https://github.com/fhsinchy/laravel-livewire-shopping-cart" target="_blank" rel="noopener noreferrer">fhsinchy/laravel-livewire-shopping-cart</a>
            </div>
        </nav>
        <div class="font-sans text-gray-900 antialiased">
            {{ $slot }}
        </div>
        @livewireScripts
    </body>
</html>

That’s all. If you want to run the reference project execute the following set of commands:

git clone https://github.com/fhsinchy/laravel-livewire-shopping-cart.git

cd laravel-livewire-shopping-cart
cp .env.example .env

touch database/database.sqlite

composer install

php artisan key:generate
php artisan migrate --seed

php artisan serve

Now visit http://localhost:8000 and you’ll see the application running.

Conclusion

I would like to thank you for the time you’ve spent reading this quick tutorial. I know this is not the definitive way of implementing a cart system and based on your necessities, it can be so so much more complex. But once you’ve got the basic idea, you should be able to extend from there and get to the Laravel Livewire testing of your shopping cart in no time. If you’re stuck somewhere, feel free to reach out on LinkedIn or Twitter.

If you want to read more related content on Laravel, check out the following articles:

FAQs

Q: How do we handle coupon codes or discounts within this shopping cart?
To handle coupon codes or discounts in the shopping cart, add a feature in Laravel Livewire to apply discounts by validating coupon codes entered by users. This involves updating the cart's total based on the discount percentage or fixed amount specified by the coupon.
Q: What steps are needed to integrate payment processing with this shopping cart?
To integrate payment processing, select a gateway (e.g., Stripe, PayPal), install its Laravel package, configure API keys, create a payment route and controller, and implement the payment logic in your Livewire component to handle transactions securely.
Q: Can this cart setup handle product variations, like size or color, and how?
Yes, the cart can handle product variations like size or color. Implement this by adding variation options in your product model and database. In the Livewire component, allow users to select these options before adding items to the cart.
Farhan Hasin Chowdhury
Farhan Hasin Chowdhury
Senior Software Engineer

Farhan is a passionate full-stack developer and author. He's a huge fan of the open-source mindset and loves sharing his knowledge with the community.

Expertise
  • Laravel
  • MySQL
  • Vue.js
  • Node.js
  • AWS
  • DigitalOcean
  • Kubernetes
  • AWS RDS
  • MongoDB
  • Python
  • Elastic Beanstalk
  • AWS S3
  • AWS CloudFront
  • Redis
  • Express.js
  • Amazon EC2
  • PostgreSQL
  • FastAPI
  • GitLab CI/CD
  • JavaScript
  • PHP
  • +16

Ready to start?

Get in touch or schedule a call.