Laravel E-Commerce Application Development – Checkout
Laravel E-Commerce Application Development ( 27 Lessons )
In this course, you’ll learn how to create an E-Commerce Website from scratch in Laravel. The process has never been easier I’ll take you from the very beginning stages of setting up Laravel till the last steps of adding products to the cart. If you’ve good understanding & experience in PHP & MySQL then this course is for you.
see full series- Laravel E-Commerce Application Development – Introduction
- Laravel E-Commerce Application Development – Initial Project Setup
- Laravel E-Commerce Application Development – Assets Setup Using Laravel Mix
- Laravel E-Commerce Application Development – Admin Model and Migration
- Laravel E-Commerce Application Development – Backend Admin Authentication
- Laravel E-Commerce Application Development – Base Controller and Repository
- Laravel E-Commerce Application Development – Settings Section Part 1
- Laravel E-Commerce Application Development – Settings Section Part 2
- Laravel E-Commerce Application Development – Categories Section Part 1
- Laravel E-Commerce Application Development – Categories Section Part 2
- Laravel E-Commerce Application Development – Attributes Section Part 1
- Laravel E-Commerce Application Development – Attributes Section Part 2
- Laravel E-Commerce Application Development – Attributes Section Part 3
- Laravel E-Commerce Application Development – Brands Section
- Laravel E-Commerce Application Development – Products Section Part 1
- Laravel E-Commerce Application Development – Products Section Part 2
- Laravel E-Commerce Application Development – Products Section Part 3
- Laravel E-Commerce Application Development – Products Section Part 4
- Laravel E-Commerce Application Development – Frontend Login & Registration
- Laravel E-Commerce Application Development – Categories Navigation
- Laravel E-Commerce Application Development – Catalog Listing
- Laravel E-Commerce Application Development – Product Details Page
- Laravel E-Commerce Application Development – Shopping Cart
- Laravel E-Commerce Application Development – Checkout
- Laravel E-Commerce Application Development – Payment Processing
- Laravel E-Commerce Application Development – Order Management
- Laravel E-Commerce Application Development – Wrap Up
This is part 23 of the Laravel E-Commerce Application Development series. In this part, we will start implementing the checkout functionality in our application.
I assume you should have the e-commerce application project on your machine or you can grab it from Laravel E-Commerce Application repository, we will start from where we left it in the last part.
Checkout Process Implementation
Before jumping into the checkout process, let me explain what I have planned to do this. The first thing a registered user can place an order so from now on we will protect all routes using the auth
middleware. When a user signin they can proceed for the checkout and fill in their address details for shipment purpose.
Next, when they hit place order, we will store the order and items in the shopping cart to our database.
After storing the order will redirect the customer to the PayPal page for payment processing. On successful payment, we show the customer the order number on the confirmation page.
So let’s start.
Creating Order Model and Migration
Let’s create the model and migration for the order. Open the command line terminal and run below command.
php artisan make:model Models\Order -m
Above command will generate a model and migration file. Open the migration file for order and update with the below content.
use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateOrdersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('orders', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('order_number')->unique(); $table->unsignedInteger('user_id'); $table->foreign('user_id')->references('id')->on('users'); $table->enum('status', ['pending', 'processing', 'completed', 'decline'])->default('pending'); $table->decimal('grand_total', 20, 6); $table->unsignedInteger('item_count'); $table->boolean('payment_status')->default(1); $table->string('payment_method')->nullable(); $table->string('first_name'); $table->string('last_name'); $table->text('address'); $table->string('city'); $table->string('country'); $table->string('post_code'); $table->string('phone_number'); $table->text('notes')->nullable(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('orders'); } }
Now open the Order model from app/Models folder and update with the below one.
namespace App\Models; use Illuminate\Database\Eloquent\Model; class Order extends Model { protected $table = 'orders'; protected $fillable = [ 'order_number', 'user_id', 'status', 'grand_total', 'item_count', 'payment_status', 'payment_method', 'first_name', 'last_name', 'address', 'city', 'country', 'post_code', 'phone_number', 'notes' ]; public function user() { return $this->belongsTo(User::class, 'user_id'); } public function items() { return $this->hasMany(OrderItem::class); } }
In our order migration file, we added the user_id
because every order will belongs to a user. In the model file you can see we have declared the user()
method which establish the relationship with the User model class.
Also, we have established a One To Many relationship for the OrderItem model which we will create in the next section.
Creating Order Item Model and Migration
Open the command line terminal and run below command to generate the OrderItem model and migration file.
php artisan make:model Models\OrderItem -m
Now open the migration file for OrderItem and update the content with the below one.
use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateOrderItemsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('order_items', function (Blueprint $table) { $table->bigIncrements('id'); $table->unsignedBigInteger('order_id')->index(); $table->unsignedBigInteger('product_id')->index(); $table->unsignedInteger('quantity'); $table->decimal('price', 20, 6); $table->foreign('order_id')->references('id')->on('orders')->onDelete('cascade'); $table->foreign('product_id')->references('id')->on('products')->onDelete('cascade'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('order_items'); } }
In this migration file we have added the order_id
and producr_id
fields with their foreign keys.
Now open the OrderItem model and update the model class with the below.
namespace App\Models; use Illuminate\Database\Eloquent\Model; class OrderItem extends Model { protected $table = 'order_items'; protected $fillable = [ 'order_id', 'product_id', 'quantity', 'price' ]; public function product() { return $this->belongsTo(Product::class, 'product_id'); } }
As you can see we have added the products()
relationship which means every ordered item will belong to a product.
Pretty simple, nothing fancy.
Creating Order Contract and Repository
Now we will create the OrderContract and OrderRepository classes.
Create a new file in app/Contracts
folder and name it OrderContract.php.
Now open this file and add below code in it.
namespace App\Contracts; interface OrderContract { public function storeOrderDetails($params); }
Next, create a new file in the app/Repositories
folder with the name OrderRepository.php. Add the below in it.
namespace App\Repositories; use Cart; use App\Models\Order; use App\Models\Product; use App\Models\OrderItem; use App\Contracts\OrderContract; class OrderRepository extends BaseRepository implements OrderContract { public function __construct(Order $model) { parent::__construct($model); $this->model = $model; } public function storeOrderDetails($params) { } }
Now open the RepositoryServiceProvidr from the app/Providers
folder and include the OrderContract and OrderRepository classes in it.
use App\Contracts\OrderContract; use App\Repositories\OrderRepository;
Next update the $repositories
array with the below one.
protected $repositories = [ CategoryContract::class => CategoryRepository::class, AttributeContract::class => AttributeRepository::class, BrandContract::class => BrandRepository::class, ProductContract::class => ProductRepository::class, OrderContract::class => OrderRepository::class, ];
Now we have order repository class which we can use on the multiple places.
Adding Checkout Routes
As I explained earlier from now on our all routes will be for only authenticated users, so we will use the auth
middleware in our routes group.
Open the routes/web.php
file and add the below routes defination in it.
Route::group(['middleware' => ['auth']], function () { Route::get('/checkout', 'Site\CheckoutController@getCheckout')->name('checkout.index'); Route::post('/checkout/order', 'Site\CheckoutController@placeOrder')->name('checkout.place.order'); });
Creating Checkout Controller
Now our above routes are pointing to CheckoutController so we will create it and inject the OrderContract in it.
php artisan make:controller Site\CheckoutController
Open the CheckoutController class and update with the below one.
namespace App\Http\Controllers\Site; use Illuminate\Http\Request; use App\Contracts\OrderContract; use App\Http\Controllers\Controller; class CheckoutController extends Controller { protected $orderRepository; public function __construct(OrderContract $orderRepository) { $this->orderRepository = $orderRepository; } public function getCheckout() { return view('site.pages.checkout'); } public function placeOrder(Request $request) { // Before storing the order we should implement the // request validation which I leave it to you $order = $this->orderRepository->storeOrderDetails($request->all()); dd($order); } }
In the above controller class we have two methods, first will load the checkout view when we will click on the proceed to checkout button and the second one will be called when we will submit the checkout form.
Updating Proceed to Checkout Button
Before creating the checkout view file, let’s add the checkout.index
route which we created earlier to our button.
Open the resources/views/site/pages/cart.blade.php
file and replace the below:
<a href="#" class="btn btn-success btn-lg btn-block">Proceed To Checkout</a>
with this:
<a href="{{ route('checkout.index') }}" class="btn btn-success btn-lg btn-block">Proceed To Checkout</a>
When a user will click this button, he/she will be redirected to the login page if not authenticated. After authentication user will be presented with the checkout view.
Adding Checkout Blade View
For checkout view, create a new file in resources/views/site/pages
folder and name it checkout.blade.php
. Now, open this file and add the below markup in it.
@extends('site.app') @section('title', 'Checkout') @section('content') <section class="section-pagetop bg-dark"> <div class="container clearfix"> <h2 class="title-page">Checkout</h2> </div> </section> <section class="section-content bg padding-y"> <div class="container"> <div class="row"> <div class="col-sm-12"> @if (Session::has('error')) <p class="alert alert-danger">{{ Session::get('error') }}</p> @endif </div> </div> <form action="{{ route('checkout.place.order') }}" method="POST" role="form"> @csrf <div class="row"> <div class="col-md-8"> <div class="card"> <header class="card-header"> <h4 class="card-title mt-2">Billing Details</h4> </header> <article class="card-body"> <div class="form-row"> <div class="col form-group"> <label>First name</label> <input type="text" class="form-control" name="first_name"> </div> <div class="col form-group"> <label>Last name</label> <input type="text" class="form-control" name="last_name"> </div> </div> <div class="form-group"> <label>Address</label> <input type="text" class="form-control" name="address"> </div> <div class="form-row"> <div class="form-group col-md-6"> <label>City</label> <input type="text" class="form-control" name="city"> </div> <div class="form-group col-md-6"> <label>Country</label> <input type="text" class="form-control" name="country"> </div> </div> <div class="form-row"> <div class="form-group col-md-6"> <label>Post Code</label> <input type="text" class="form-control" name="post_code"> </div> <div class="form-group col-md-6"> <label>Phone Number</label> <input type="text" class="form-control" name="phone_number"> </div> </div> <div class="form-group"> <label>Email Address</label> <input type="email" class="form-control" name="email" value="{{ auth()->user()->email }}" disabled> <small class="form-text text-muted">We'll never share your email with anyone else.</small> </div> <div class="form-group"> <label>Order Notes</label> <textarea class="form-control" name="notes" rows="6"></textarea> </div> </article> </div> </div> <div class="col-md-4"> <div class="row"> <div class="col-md-12"> <div class="card"> <header class="card-header"> <h4 class="card-title mt-2">Your Order</h4> </header> <article class="card-body"> <dl class="dlist-align"> <dt>Total cost: </dt> <dd class="text-right h5 b"> {{ config('settings.currency_symbol') }}{{ \Cart::getSubTotal() }} </dd> </dl> </article> </div> </div> <div class="col-md-12 mt-4"> <button type="submit" class="subscribe btn btn-success btn-lg btn-block">Place Order</button> </div> </div> </div> </div> </form> </div> </section> @stop
In this view file, we have a billing information form and a Place Order button. The form is pointing to the checkout.place.order
route.
Storing Order Details to Database
After successful submission of the checkout form, we want to store the order and cart items. So let’s add that implementation into the OrderRepository.
Opne the OrderRepository class and update the storeOrderDetails()
method with the below one.
public function storeOrderDetails($params) { $order = Order::create([ 'order_number' => 'ORD-'.strtoupper(uniqid()), 'user_id' => auth()->user()->id, 'status' => 'pending', 'grand_total' => Cart::getSubTotal(), 'item_count' => Cart::getTotalQuantity(), 'payment_status' => 0, 'payment_method' => null, 'first_name' => $params['first_name'], 'last_name' => $params['last_name'], 'address' => $params['address'], 'city' => $params['city'], 'country' => $params['country'], 'post_code' => $params['post_code'], 'phone_number' => $params['phone_number'], 'notes' => $params['notes'] ]); if ($order) { $items = Cart::getContent(); foreach ($items as $item) { // A better way will be to bring the product id with the cart items // you can explore the package documentation to send product id with the cart $product = Product::where('name', $item->name)->first(); $orderItem = new OrderItem([ 'product_id' => $product->id, 'quantity' => $item->quantity, 'price' => $item->getPriceSum() ]); $order->items()->save($orderItem); } } return $order; }
In this method, we are firstly creating an order record with the all required information. Next, if an order is created we are loading the shopping cart items and creating the new order item instances. Then we are using the save()
method to save and attach each item to the order. On completion we are returning the $order
to our controller.
If you now try in your browser and load the checkout you will have a similar view like below.

User Specific Shopping Cart
Currently we are not attaching the shopping cart to authenticated user. The package we have used to add shopping cart functionality does support the user-specific shopping cart. For that, you have to move the add to cart route and all shopping cart route under the route group which is using the auth
middleware.
I want to keep this series as simple as possible so won’t be going much deep into each and every detail. Check the package documentation for using user-specific shopping carts.
What’s Next
In the next post, we will start from where we are leaving now and process the payment using PayPal.
Code Repository
You can find the code base of this series on Laravel eCommerce Application repository.
If you have any question about this post, please leave a comment in the comment box below.