Biên soạn: Nguyễn Hoàng Tùng. Giấy phép CC BY-NC 4.0 Quốc tế.
PHẦN 9: CÀI ĐẶT GIỎ HÀNG
Lab 1: Giới thiệu các gói “Shopping cart” cho Laravel.
Gói gloudemans/shoppingcart chỉ hỗ trợ cho Laravel 6.x trở xuống.
Liên kết: />Gói bumbummen99/shoppingcart là một nhánh của gloudemans/shoppingcart, hỗ trợ từ Laravel 7.x trở lên.
Liên kết: />Ngồi 2 gói giới thiệu ở trên, có rất nhiều gói khác cũng được đánh giá tốt, các bạn có thể tìm kiếm và sử dụng thử.
Lab 2: Cài đặt gói bumbummen99/shoppingcart
Tiến hành cài đặt và cấu hình theo liên kết ở Lab 1.
Lệnh cài copy từ trang web:
composer require bumbummen99/shoppingcart
Tạo tập tin config/cart.php:
php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="config"
Các hàm thường dùng:
STT
Tên hàm
Mơ tả
Ví dụ
1
Cart::add()
Thêm sản phẩm vào giỏ.
Cart::add([
'id' => 1,
'name' => 'iPhone 69 Pro Max',
'price' => 123000000,
Lưu ý:
- 5 tham số đầu là cố định.
- Tham số options thứ 6 là 1 mảng tùy ý.
'qty' => 1,
'weight' => 420,
'options' => [
'image' => 'dienthoai/iphone-69-pro-max.jpg'
]
]);
2
Cart::get()
Lấy một sản phẩm trong giỏ.
$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709';
Cart::get($rowId);
3
Cart::update()
Cập nhật sản phẩm trong giỏ.
$row = Cart::get($rowId);
if($row->qty < 10)
{
Cart::update($row_id, $row->qty + 1);
}
4
Cart::remove()
5
Xóa một sản phẩm trong giỏ
$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709';
Cart::destroy()
Xóa toàn bộ giỏ hàng
Cart::destroy();
6
Cart::count()
Đếm số lượng sản phẩm trong giỏ
Cart::count();
7
Cart::content()
Lấy toàn bộ giỏ hàng
foreach(Cart::content() as $row)
Cart::remove($rowId);
{
echo $row->rowId . '
';
echo $row->id . '
';
echo $row->name . '
';
echo $row->price . '
';
echo $row->qty . '
';
echo $row->weight . '
';
}
8
Cart::priceTotal() Trả về tổng giá gốc của tất cả sản phẩm.
Cart::priceTotal();
9
Cart::discount()
Trả về tổng giảm giá của tất cả sản phẩm.
Cart::discount();
10
Cart::subtotal()
Trả về tổng giá đã trừ đi giảm giá của tất cả sản phẩm.
Cart::subtotal();
11
Cart::tax()
Trả về tổng tiền thuế của tất cả sản phẩm.
Cart::tax();
12
Cart::total()
Trả về tổng giá phải thanh toán (cộng thêm thuế).
Cart::total();
Lab 3: Chỉnh sửa cấu hình giỏ hàng bên trong config/cart.php
Thiết lập phần trăm thuế
'tax' => 10,
Thiết lập định dạng số lẻ thập phân, phân cách thập phân, phân cách hàng ngàn
'format' => [
'decimals' => 0,
'decimal_point' => ',',
'thousand_separator' => '.',
],
Lab 4: Hiển thị sản phẩm ra trang chủ và cập nhật liên kết “Add to cart”
Chỉnh sửa tập tin resources/views/frontend/index.blade.php
<div class="row shop_container list">
@foreach($sanpham as $value)
<div class="col-lg-3 col-md-4 col-6">
<div class="product">
<div class="product_img">
<a href="{{ route('frontend.sanpham.chitiet', ['tenloai_slug' => $value->LoaiSanPham->tenloai_slug, 'tensanpham_slug' => $value->tensanpham_slug]) }}">
<img src="{{ env('APP_URL') . '/storage/app/' . $value->hinhanh }}" />
</a>
<div class="product_action_box">
<ul class="list_none pr_action_btn">
<li class="add-to-cart">
<a href="{{ route('frontend.giohang.them', ['tensanpham_slug' => $value->tensanpham_slug]) }}"><i class="icon-basket-loaded"></i> Thêm vào giỏ hàng</a>
</li>
<li><a href="#" class="popup-ajax"><i class="icon-shuffle"></i></a></li>
<li><a href="#" class="popup-ajax"><i class="icon-magnifier-add"></i></a></li>
<li><a href="#"><i class="icon-heart"></i></a></li>
</ul>
</div>
</div>
<div class="product_info">
<a href="{{ route('frontend.sanpham.chitiet', ['tenloai_slug' => $value->LoaiSanPham->tenloai_slug, 'tensanpham_slug' => $value->tensanpham_slug]) }}">{{ $value->tensanpham }}</a>
<div class="product_price">
<span class="price">{{ number_format($value->dongia) }}<sup>đ</sup></span>
<del>{{ number_format($value->dongia * 1.1) }}<sup>đ</sup></del>
<div class="on_sale">
<span>Giảm giá 10%</span>
</div>
</div>
<div class="rating_wrap">
<div class="rating">
<div class="product_rate" style="width:80%"></div>
</div>
<span class="rating_num">(100)</span>
</div>
<div class="pr_desc">
Mô tả ngắn gọn về sản phẩm {{ $value->tensanpham }}.
</div>
<div class="pr_switch_wrap">
<div class="product_color_switch">
<span class="active" data-color="#87554B"></span>
<span data-color="#333333"></span>
<span data-color="#DA323F"></span>
</div>
</div>
<div class="list_product_action_box">
<ul class="list_none pr_action_btn">
<li class="add-to-cart">
<a href="{{ route('frontend.giohang.them', ['tensanpham_slug' => $value->tensanpham_slug]) }}"><i class="icon-basket-loaded"></i> Thêm vào giỏ hàng</a>
</li>
<li><a href="#" class="popup-ajax"><i class="icon-shuffle"></i></a></li>
<li><a href="#" class="popup-ajax"><i class="icon-magnifier-add"></i></a></li>
<li><a href="#"><i class="icon-heart"></i></a></li>
</ul>
</div>
</div>
</div>
</div>
@endforeach
</div>
Kết quả hiển thị trang chủ:
Lab 5: Chỉnh sửa hàm getHome bên trong tập tin app/Http/Controllers/HomeController.php
namespace App\Http\Controllers;
use
use
use
use
use
App\Models\SanPham;
App\Models\NguoiDung;
Illuminate\Http\Request;
Illuminate\Support\Facades\Auth;
Illuminate\Support\Str;
class HomeController extends Controller
{
public function getHome()
{
$sanpham = SanPham::paginate(16);
return view('frontend.index', compact('sanpham'));
}
}
Lab 6: Cài đặt giỏ hàng bên trong app/Http/Controllers/HomeController.php
namespace App\Http\Controllers;
use
use
use
use
use
use
App\Models\SanPham;
App\Models\NguoiDung;
Illuminate\Http\Request;
Illuminate\Support\Facades\Auth;
Illuminate\Support\Str;
Gloudemans\Shoppingcart\Facades\Cart;
class HomeController extends Controller
{
public function __construct()
{
}
public function getGioHang()
{
if(Cart::count() <= 0)
return view('frontend.giohang_rong');
else
return view('frontend.giohang');
}
public function getGioHang_Them($tensanpham_slug)
{
$sanpham = SanPham::where('tensanpham_slug', $tensanpham_slug)->first();
Cart::add([
'id' => $sanpham->id,
'name' => $sanpham->tensanpham,
'price' => $sanpham->dongia,
'qty' => 1,
'weight' => 0,
'options' => [
'image' => $sanpham->hinhanh
]
]);
return redirect()->route('frontend');
}
public function getGioHang_Xoa($row_id)
{
Cart::remove($row_id);
return redirect()->route('frontend.giohang');
}
public function getGioHang_XoaTatCa()
{
Cart::destroy();
return redirect()->route('frontend.giohang');
}
public function getGioHang_Giam($row_id)
{
$row = Cart::get($row_id);
if($row->qty > 1)
{
Cart::update($row_id, $row->qty - 1);
}
return redirect()->route('frontend.giohang');
}
public function getGioHang_Tang($row_id)
{
$row = Cart::get($row_id);
if($row->qty < 10)
{
Cart::update($row_id, $row->qty + 1);
}
return redirect()->route('frontend.giohang');
}
}
Lab 7: Chỉnh sửa các view liên quan đến giỏ hàng
- Chỉnh sửa tập tin resources/views/layouts/frontend.blade.php
<li class="dropdown cart_dropdown">
<a class="nav-link cart_trigger" href="#" data-toggle="dropdown">
<i class="linearicons-cart"></i><span class="cart_count">{{ Cart::count() ?? 0 }}</span>
</a>
@if(Cart::count())
<div class="cart_box dropdown-menu dropdown-menu-right">
<ul class="cart_list">
@foreach(Cart::content() as $value)
<li>
<a href="{{ route('frontend.giohang.xoa', ['row_id' => $value->rowId]) }}" class="item_remove"><i class="ion-close"></i></a>
<a href="#"><img src="{{ env('APP_URL') . '/storage/app/' . $value->options->image }}" />{{ $value->name }}</a>
<span class="cart_quantity"> {{ $value->qty }} x <span class="cart_amount">{{ number_format($value->price) }}</span><sup>đ</sup></span>
</li>
@endforeach
</ul>
<div class="cart_footer">
<strong>Tổng tiền sản phẩm:</strong> <span class="cart_price">{{ Cart::priceTotal() }}</span><sup>đ</sup>
<a href="{{ route('frontend.giohang') }}" class="btn btn-fill-line view-cart">GIỎ HÀNG</a>
<a href="{{ route('frontend.dathang') }}" class="btn btn-fill-out checkout">THANH TOÁN</a>
</div>
</div>
@endif
</li>
Kết quả hiển thị:
- Chỉnh sửa tập tin resources/views/layouts/giohang.blade.php
<table class="table">
<thead>
<tr>
<th class="product-thumbnail"> </th>
<th class="product-name">Sản phẩm</th>
<th class="product-price">Đơn giá</th>
<th class="product-quantity">Số lượng</th>
<th class="product-subtotal">Thành tiền</th>
<th class="product-remove">Xóa</th>
</tr>
</thead>
<tbody>
@foreach(Cart::content() as $value)
<tr>
<td class="product-thumbnail"><a href="#">
<img src="{{ env('APP_URL') . '/storage/app/' . $value->options->image }}" /></a>
</td>
<td class="product-name" data-title="Product"><a href="#">{{ $value->name }}</a></td>
<td class="product-price" data-title="Price">{{ number_format($value->price) }}<sup>đ</sup></td>
<td class="product-quantity" data-title="Quantity">
<div class="quantity">
<a class="minus" href="{{ route('frontend.giohang.giam', ['row_id' => $value->rowId]) }}">-</a>
<input type="text" name="qty" value="{{ $value->qty }}" class="qty" size="4" />
<a class="plus" href="{{ route('frontend.giohang.tang', ['row_id' => $value->rowId]) }}">+</a>
</div>
</td>
<td class="product-subtotal" data-title="Total">{{ number_format($value->price * $value->qty) }}<sup>đ</sup></td>
<td class="product-remove" data-title="Remove">
<a href="{{ route('frontend.giohang.xoa', ['row_id' => $value->rowId]) }}"><i class="ti-close"></i></a>
</td>
</tr>
@endforeach
</tbody>
<tfoot>
<tr>
<td colspan="6" class="px-0">
<div class="row no-gutters align-items-center">
<div class="col-lg-4 col-md-6 mb-3 mb-md-0">
<div class="coupon field_form input-group">
<input type="text" class="form-control form-control-sm" placeholder="Mã giảm giá?" />
<div class="input-group-append">
<button class="btn btn-fill-out btn-sm" type="submit">ÁP DỤNG</button>
</div>
</div>
</div>
<div class="col-lg-8 col-md-6 text-left text-md-right">
<a href="{{ route('frontend.giohang.xoatatca') }}" class="btn btn-line-fill btn-sm" type="submit">XÓA GIỎ HÀNG</a>
</div>
</div>
</td>
</tr>
</tfoot>
</table>
Và thông tin về tổng tiền:
<div class="col-12">
<div class="border p-3 p-md-4">
<div class="heading_s1 mb-3">
Tổng tiền giỏ hàng
</div>
<div class="table-responsive">
<table class="table table-bordered">
<tbody>
<tr>
<td class="cart_total_label">Tổng tiền sản phẩm</td>
<td class="cart_total_amount">{{ Cart::subtotal() }}<sup>đ</sup></td>
</tr>
<tr>
<td class="cart_total_label">Thuế VAT (10%)</td>
<td class="cart_total_amount">{{ Cart::tax() }}<sup>đ</sup></td>
</tr>
<tr>
<td class="cart_total_label">Phí vận chuyển</td>
<td class="cart_total_amount">Miễn phí vận chuyển</td>
</tr>
<tr>
<td class="cart_total_label">Tổng thanh toán</td>
<td class="cart_total_amount"><strong>{{ Cart::total() }}<sup>đ</sup></strong></td>
</tr>
</tbody>
</table>
</div>
<a href="{{ route('frontend.dathang') }}" class="btn btn-fill-out">TIẾN HÀNH THANH TOÁN</a>
</div>
</div>
Kết quả hiển thị:
- Chỉnh sửa tập tin resources/views/layouts/giohang_rong.blade.php
@extends('layouts.frontend')
@section('title', 'Giỏ hàng')
@section('content')
<div class="breadcrumb_section bg_gray page-title-mini">
<div class="container">
<div class="row align-items-center">
<div class="col-md-6">
<div class="page-title">
Giỏ hàng
</div>
</div>
<div class="col-md-6">
<ol class="breadcrumb justify-content-md-end">
<li class="breadcrumb-item"><a href="{{ route('frontend') }}">Trang chủ</a></li>
<li class="breadcrumb-item active">Giỏ hàng</li>
</ol>
</div>
</div>
</div>
</div>
<div class="main_content">
<div class="section">
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="text-center order_complete">
<i class="fal fa-shopping-cart"></i>
<div class="heading_s1">
Giỏ hàng chưa có sản phẩm!
</div>
Giỏ hàng của bạn đang rỗng, xin hãy lấp đầy nó bằng việc duyệt qua các sản phẩm của cửa hàng
và bỏ vào giỏ các sản phẩm mà bạn yêu thích và có ý định sẽ sở hữu nó.
<a href="{{ route('frontend') }}" class="btn btn-fill-out">TIẾP TỤC MUA SẮM</a>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
Kết quả hiển thị:
Lab 8: Xử lý đặt hàng
- Chỉnh sửa tập tin resources/views/layouts/dathang.blade.php
<div class="col-lg-6">
@guest
<div class="toggle_info">
<span>
<i class="fas fa-user"></i>Đã từng đăng ký?
<a href="#loginform" data-toggle="collapse" class="collapsed" aria-expanded="false">Nhấn vào để đăng nhập</a>
</span>
</div>
<div class="panel-collapse collapse login_form" id="loginform">
<div class="panel-body">
Nếu bạn đã đăng ký tài khoản, xin vui lòng đăng nhập.
<form action="{{ route('login') }}" method="post">
@csrf
<div class="form-group">
<input type="text" class="form-control{{ ($errors->has('email') || $errors->has('username')) ? ' is-invalid' : '' }}"
name="email" value="{{ old('email') }}" placeholder="Tên đăng nhập hoặc Email *" required />
@if ($errors->has('email') || $errors->has('username'))
<span class="invalid-feedback" role="alert">
<strong>{{ empty($errors->first('email')) ? $errors->first('username') : $errors->first('email') }}</strong>
</span>
@endif
</div>
<div class="form-group">
<input type="password" class="form-control @error('password') is-invalid @enderror" name="password" placeholder="Mật khẩu *" required />
@error('password')
<span class="invalid-feedback" role="alert"><strong>{{ $message }}</strong></span>
@enderror
</div>
<div class="login_footer form-group">
<div class="chek-form">
<div class="custome-checkbox">
<input type="checkbox" class="form-check-input" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }} />
<label class="form-check-label" for="remember"><span>Nhớ thông tin đăng nhập</span></label>
</div>
</div>
<a href="#">Quên mật khẩu?</a>
</div>
<div class="form-group">
<button type="submit" class="btn btn-fill-out btn-block">ĐĂNG NHẬP</button>
</div>
</form>
</div>
</div>
@else
<div class="toggle_info">
<span><i class="fas fa-user"></i>Bạn đã đăng nhập với tài khoản khách hàng <strong>{{ Auth::user()->name }}</strong>.</span>
</div>
@endguest
</div>
Kết quả hiển thị:
Tiếp tục các phần code bên dưới:
<div class="col-md-6">
<div class="heading_s1">
Thông tin giao hàng
</div>
<form action="{{ route('frontend.dathang') }}" method="post" id="checkoutform">
@csrf
<div class="form-group">
<input type="text" class="form-control" name="name" placeholder="Họ và tên *" value="{{ Auth::user()->name ?? '' }}" required />
</div>
<div class="form-group">
<input type="text" class="form-control" name="diachigiaohang" placeholder="Địa chỉ giao hàng *" required />
</div>
<div class="form-group">
<input type="text" class="form-control" name="dienthoaigiaohang" placeholder="Điện thoại *" required />
</div>
<div class="form-group">
<input type="text" class="form-control" name="email" placeholder="Địa chỉ Email *" value="{{ Auth::user()->email ?? '' }}" required />
</div>
@guest
<div class="form-group">
<div class="chek-form">
<div class="custome-checkbox">
<input type="checkbox" class="form-check-input" name="createaccount" id="createaccount" />
<label class="form-check-label label_info" for="createaccount"><span>Đăng ký tài khoản?</span></label>
</div>
</div>
</div>
<div class="form-group create-account">
<input type="password" class="form-control" placeholder="Mật khẩu" name="password" />
</div>
@endguest
<div class="ship_detail">
<div class="form-group">
<div class="chek-form">
<div class="custome-checkbox">
<input type="checkbox" class="form-check-input" name="differentaddress" id="differentaddress" />
<label class="form-check-label label_info" for="differentaddress"><span>Giao hàng tới địa chỉ khác?</span></label>
</div>
</div>
</div>
<div class="different_address">
<div class="form-group">
<input type="text" class="form-control" name="name2" placeholder="Họ và tên *" />
</div>
<div class="form-group">
<input type="text" class="form-control" name="diachigiaohang2" placeholder="Địa chỉ giao hàng *" />
</div>
<div class="form-group">
<input type="text" class="form-control" name="dienthoaigiaohang2" placeholder="Điện thoại *" />
</div>
<div class="form-group">
<input type="text" class="form-control" name="email2" placeholder="Địa chỉ Email *" />
</div>
</div>
</div>
<div class="heading_s1">
Ghi chú bổ sung
</div>
<div class="form-group mb-0">
<textarea rows="6" class="form-control" placeholder="Thông tin bổ sung khi giao hàng" name="ghichubosung"></textarea>
</div>
</form>
</div>
<div class="col-md-6">
<div class="order_review">
<div class="heading_s1">
Đơn hàng của bạn
</div>
<div class="table-responsive order_table">
<table class="table">
<thead>
<tr>
<th>Sản phẩm</th>
<th>Số tiền</th>
</tr>
</thead>
<tbody>
@foreach(Cart::content() as $value)
<tr>
<td>{{ $value->name }} <span class="product-qty">x {{ $value->qty }}</span></td>
<td>{{ number_format($value->price * $value->qty) }}<sup>đ</sup></td>
</tr>
@endforeach
</tbody>
<tfoot>
<tr>
<th>Tổng tiền sản phẩm</th>
<td class="product-subtotal">{{ Cart::subtotal() }}<sup>đ</sup></td>
</tr>
<tr>
<td>Thuế VAT (10%)</td>
<td>{{ Cart::tax() }}<sup>đ</sup></td>
</tr>
<tr>
<th>Phí vận chuyển</th>
<td>Miễn phí vận chuyển</td>
</tr>
<tr>
<th>Tổng thanh tốn</th>
<td class="product-subtotal">{{ Cart::total() }}<sup>đ</sup></td>
</tr>
</tfoot>
</table>
</div>
<div class="payment_method">
<div class="heading_s1">
Hình thức thanh tốn
</div>
<div class="payment_option">
<div class="custome-radio">
<input class="form-check-input" type="radio" name="payment_option" id="exampleRadios3" value="option3" checked />
<label class="form-check-label" for="exampleRadios3">COD</label>
Mơ tả hình thức thanh toán này tại đây.
</div>
<div class="custome-radio">
<input class="form-check-input" type="radio" name="payment_option" id="exampleRadios4" value="option4" />
<label class="form-check-label" for="exampleRadios4">Chuyển khoản ngân hàng</label>
Mô tả hình thức thanh tốn này tại đây.
</div>
<div class="custome-radio">
<input class="form-check-input" type="radio" name="payment_option" id="exampleRadios5" value="option5" />
<label class="form-check-label" for="exampleRadios5">Ví điện tử Momo</label>
Mơ tả hình thức thanh tốn này tại đây.
</div>
</div>
</div>
onclick="event.preventDefault();document.getElementById('checkoutform').submit();"
class="btn btn-fill-out btn-block">TIẾN HÀNH ĐẶT HÀNG</a>
</div>
</div>
Kết quả hiển thị:
- Cài đặt các hàm thanh toán bên trong app/Http/Controllers/HomeController.php
namespace App\Http\Controllers;
use
use
use
use
use
use
use
use
App\Models\SanPham;
App\Models\NguoiDung;
App\Models\DonHang;
App\Models\DonHang_ChiTiet;
Illuminate\Http\Request;
Illuminate\Support\Facades\Auth;
Illuminate\Support\Str;
Gloudemans\Shoppingcart\Facades\Cart;
class HomeController extends Controller
{
public function getDatHang()
{
return view('frontend.dathang');
}
public function postDatHang(Request $request)
{
$this->validate($request, [
'diachigiaohang' => ['required', 'max:255'],
'dienthoaigiaohang' => ['required', 'max:255'],
]);
// Lưu vào đơn hàng
$dh = new DonHang();
$dh->nguoidung_id = Auth::user()->id;
$dh->diachigiaohang = $request->diachigiaohang;
$dh->dienthoaigiaohang = $request->dienthoaigiaohang;
$dh->save();
// Lưu vào đơn hàng chi tiết
foreach(Cart::content() as $value)
{
$ct = new DonHang_ChiTiet();
$ct->donhang_id = $dh->id;
$ct->sanpham_id = $value->id;
$ct->soluongban = $value->qty;
$ct->dongiaban = $value->price;
$ct->save();
}
return redirect()->route('frontend.dathangthanhcong');
}
public function getDatHangThanhCong()
{
// Xóa giỏ hàng khi hồn tất đặt hàng?
Cart::destroy();
return view('frontend.dathangthanhcong');
}
}
- Chỉnh sửa tập tin resources/views/layouts/dathangthanhcong.blade.php
@extends('layouts.frontend')
@section('title', 'Đặt hàng thành công')
@section('content')
<div class="breadcrumb_section bg_gray page-title-mini">
<div class="container">
<div class="row align-items-center">
<div class="col-md-6">
<div class="page-title">
Đặt hàng thành công
</div>
</div>
<div class="col-md-6">
<ol class="breadcrumb justify-content-md-end">
<li class="breadcrumb-item"><a href="{{ route('frontend') }}">Trang chủ</a></li>
<li class="breadcrumb-item active">Đặt hàng thành công</li>
</ol>
</div>
</div>
</div>
</div>
<div class="main_content">
<div class="section">
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="text-center order_complete">
<i class="fal fa-check-circle"></i>
<div class="heading_s1">
Bạn đã đặt hàng thành công!
</div>
Cảm ơn bạn đã đặt hàng! Đơn hàng của bạn đang được xử lý và sẽ hồn thành trong vịng 3-6 giờ.
Bạn sẽ nhận được một email xác nhận khi đơn đặt hàng của bạn được hoàn thành.
<a href="{{ route('frontend') }}" class="btn btn-fill-out">TIẾP TỤC MUA SẮM</a>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
Kết quả hiển thị:
-- HẾT PHẦN 9 --