Step 1: tạo file xử lý Logic cho chức năng Chi tiết Sản phẩm

  • Tạo file frontend/product/detail.php
<?php
// Include file cấu hình ban đầu của `Twig`
require_once __DIR__ . '/../../bootstrap.php';

// Truy vấn database
// 1. Include file cấu hình kết nối đến database, khởi tạo kết nối $conn
include_once(__DIR__ . '/../../dbconnect.php');

/* --- 
   --- 2.Truy vấn dữ liệu Sản phẩm 
   --- Lấy giá trị khóa chính được truyền theo dạng QueryString Parameter key1=value1&key2=value2...
   --- 
*/
$sp_ma = $_GET['sp_ma'];
$sqlSelectSanPham = <<<EOT
    SELECT sp.sp_ma, sp.sp_ten, sp.sp_gia, sp.sp_giacu, sp.sp_mota_ngan, sp.sp_mota_chitiet, sp.sp_soluong, lsp.lsp_ten
    FROM `sanpham` sp
    JOIN `loaisanpham` lsp ON sp.lsp_ma = lsp.lsp_ma
    WHERE sp.sp_ma = $sp_ma
EOT;

// Thực thi câu truy vấn SQL để lấy về dữ liệu ban đầu của record 
$resultSelectSanPham = mysqli_query($conn, $sqlSelectSanPham);

// Khi thực thi các truy vấn dạng SELECT, dữ liệu lấy về cần phải phân tích để sử dụng
// Thông thường, chúng ta sẽ sử dụng vòng lặp while để duyệt danh sách các dòng dữ liệu được SELECT
// Ta sẽ tạo 1 mảng array để chứa các dữ liệu được trả về
$sanphamRow;
while ($row = mysqli_fetch_array($resultSelectSanPham, MYSQLI_ASSOC)) {
    $sanphamRow = array(
        'sp_ma' => $row['sp_ma'],
        'sp_ten' => $row['sp_ten'],
        'sp_gia' => $row['sp_gia'],
        'sp_gia_formated' => number_format($row['sp_gia'], 2, ".", ",") . ' vnđ',
        'sp_giacu_formated' => number_format($row['sp_giacu'], 2, ".", ",") . ' vnđ',
        'sp_mota_ngan' => $row['sp_mota_ngan'],
        'sp_mota_chitiet' => $row['sp_mota_chitiet'],
        'sp_soluong' => $row['sp_soluong'],
        'lsp_ten' => $row['lsp_ten']
    );
}
/* --- End Truy vấn dữ liệu Sản phẩm --- */

/* --- 
   --- 3.Truy vấn dữ liệu Hình ảnh Sản phẩm 
   --- 
*/
$sqlSelect = <<<EOT
    SELECT hsp.hsp_tentaptin
    FROM `hinhsanpham` hsp
    WHERE hsp.sp_ma = $sp_ma
EOT;

// Thực thi câu truy vấn SQL để lấy về dữ liệu ban đầu của record 
$result = mysqli_query($conn, $sqlSelect);

// Khi thực thi các truy vấn dạng SELECT, dữ liệu lấy về cần phải phân tích để sử dụng
// Thông thường, chúng ta sẽ sử dụng vòng lặp while để duyệt danh sách các dòng dữ liệu được SELECT
// Ta sẽ tạo 1 mảng array để chứa các dữ liệu được trả về
$danhsachhinhanh = [];
while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
    $danhsachhinhanh[] = array(
        'hsp_tentaptin' => $row['hsp_tentaptin']
    );
}
/* --- End Truy vấn dữ liệu Hình ảnh sản phẩm --- */

// Hiệu chỉnh dữ liệu theo cấu trúc để tiện xử lý
$sanphamRow['danhsachhinhanh'] = $danhsachhinhanh;

// Yêu cầu `Twig` vẽ giao diện được viết trong file `backend/sanpham/chitiet.html.twig`
// với dữ liệu truyền vào file giao diện được đặt tên là `sanpham`
// dd($sanphamRow);
echo $twig->render('frontend/sanpham/chitiet.html.twig', ['sanpham' => $sanphamRow]);

Step 2: tạo template giao diện cho trang chi tiết

  • Tạo file templates/frontend/product/chitiet.html.twig
{# Kế thừa layout frontend #}
{% extends "frontend/layouts/layout.html.twig" %}

{# Nội dung trong block title #}
{% block title %}
Sản phẩm {{ sanpham.sp_ten }}
{% endblock %}
{# End Nội dung trong block title #}

{# Nội dung trong block headline #}
{% block headline %}
Sản phẩm {{ sanpham.sp_ten }}
{% endblock %}
{# End Nội dung trong block headline #}

{% block customstyles %}
<style>
    body {
        font-family: 'open sans';
        overflow-x: hidden;
    }
    img {
        max-width: 100%;
    }
    .preview {
        display: -webkit-box;
        display: -webkit-flex;
        display: -ms-flexbox;
        display: flex;
        -webkit-box-orient: vertical;
        -webkit-box-direction: normal;
        -webkit-flex-direction: column;
        -ms-flex-direction: column;
        flex-direction: column;
    }
    @media screen and (max-width: 996px) {
        .preview {
            margin-bottom: 20px;
        }
    }
    .preview-pic {
        -webkit-box-flex: 1;
        -webkit-flex-grow: 1;
        -ms-flex-positive: 1;
        flex-grow: 1;
        max-height: 300px;
    }
    .preview-thumbnail.nav-tabs {
        border: none;
        margin-top: 15px;
    }
    .preview-thumbnail.nav-tabs li {
        width: 18%;
        margin-right: 2.5%;
    }
    .preview-thumbnail.nav-tabs li img {
        max-width: 100%;
        display: block;
    }
    .preview-thumbnail.nav-tabs li a {
        padding: 0;
        margin: 0;
    }
    .preview-thumbnail.nav-tabs li:last-of-type {
        margin-right: 0;
    }
    .tab-content {
        overflow: hidden;
    }
    .tab-content img {
        width: 100%;
        -webkit-animation-name: opacity;
        animation-name: opacity;
        -webkit-animation-duration: .3s;
        animation-duration: .3s;
    }
    .card {
        margin-top: 50px;
        background: #eee;
        padding: 3em;
        line-height: 1.5em;
    }
    @media screen and (min-width: 997px) {
        .wrapper {
            display: -webkit-box;
            display: -webkit-flex;
            display: -ms-flexbox;
            display: flex;
        }
    }
    .details {
        display: -webkit-box;
        display: -webkit-flex;
        display: -ms-flexbox;
        display: flex;
        -webkit-box-orient: vertical;
        -webkit-box-direction: normal;
        -webkit-flex-direction: column;
        -ms-flex-direction: column;
        flex-direction: column;
    }
    .colors {
        -webkit-box-flex: 1;
        -webkit-flex-grow: 1;
        -ms-flex-positive: 1;
        flex-grow: 1;
    }
    .product-title,
    .price,
    .sizes,
    .colors {
        text-transform: UPPERCASE;
        font-weight: bold;
    }
    .checked,
    .price span {
        color: #ff9f1a;
    }
    .product-title,
    .rating,
    .product-description,
    .price,
    .vote,
    .sizes {
        margin-bottom: 15px;
    }
    .product-title {
        margin-top: 0;
    }
    .size {
        margin-right: 10px;
    }
    .size:first-of-type {
        margin-left: 40px;
    }
    .color {
        display: inline-block;
        vertical-align: middle;
        margin-right: 10px;
        height: 2em;
        width: 2em;
        border-radius: 2px;
    }
    .color:first-of-type {
        margin-left: 20px;
    }
    .add-to-cart,
    .like {
        background: #ff9f1a;
        padding: 1.2em 1.5em;
        border: none;
        text-transform: UPPERCASE;
        font-weight: bold;
        color: #fff;
        -webkit-transition: background .3s ease;
        transition: background .3s ease;
    }
    .add-to-cart:hover,
    .like:hover {
        background: #b36800;
        color: #fff;
    }
    .not-available {
        text-align: center;
        line-height: 2em;
    }
    .not-available:before {
        font-family: fontawesome;
        content: "\f00d";
        color: #fff;
    }
    .orange {
        background: #ff9f1a;
    }
    .green {
        background: #85ad00;
    }
    .blue {
        background: #0076ad;
    }
    .tooltip-inner {
        padding: 1.3em;
    }
    @-webkit-keyframes opacity {
        0% {
            opacity: 0;
            -webkit-transform: scale(3);
            transform: scale(3);
        }
        100% {
            opacity: 1;
            -webkit-transform: scale(1);
            transform: scale(1);
        }
    }
    @keyframes opacity {
        0% {
            opacity: 0;
            -webkit-transform: scale(3);
            transform: scale(3);
        }
        100% {
            opacity: 1;
            -webkit-transform: scale(1);
            transform: scale(1);
        }
    }
</style>
{% endblock %}

{# Nội dung trong block content #}
{% block content %}
<div class="container mt-4">
    <div id="errors-container" class="alert alert-danger d-none face" role="alert">
        <div id="thongbao"></div>
        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
            <span aria-hidden="true">&times;</span>
        </button>
    </div>

    <div class="card">
        <div class="container-fliud">
            <form name="frmsanphamchitiet" id="frmsanphamchitiet" method="post"
                action="/frontend/giohang/themvaogiohang">
                {% set hinhsanphamdautien = sanpham.danhsachhinhanh|first %}
                <input type="hidden" name="sp_ma" id="sp_ma" value="{{ sanpham.sp_ma }}" />
                <input type="hidden" name="sp_ten" id="sp_ten" value="{{ sanpham.sp_ten }}" />
                <input type="hidden" name="sp_gia" id="sp_gia" value="{{ sanpham.sp_gia }}" />
                <input type="hidden" name="hinhdaidien" id="hinhdaidien"
                    value="{{ hinhsanphamdautien.hsp_tentaptin }}" />

                <div class="wrapper row">
                    <div class="preview col-md-6">
                        {% if sanpham.danhsachhinhanh|length > 0 %}
                        <div class="preview-pic tab-content">
                            {% for hinhsanpham in sanpham.danhsachhinhanh %}
                            <div class="tab-pane {{ (hinhsanpham == hinhsanphamdautien) ? 'active' : '' }}"
                                id="pic-{{ loop.index }}">
                                <img src="/assets/uploads/{{ hinhsanpham.hsp_tentaptin }}" />
                            </div>
                            {% endfor %}
                        </div>
                        <ul class="preview-thumbnail nav nav-tabs">
                            {% for hinhsanpham in sanpham.danhsachhinhanh %}
                            <li class="{{ (hinhsanpham == hinhsanphamdautien) ? 'active' : '' }}">
                                <a data-target="#pic-{{ loop.index }}" data-toggle="tab">
                                    <img src="/assets/uploads/{{ hinhsanpham.hsp_tentaptin }}" />
                                </a>
                            </li>
                            {% endfor %}
                        </ul>
                        {% else %}
                        <!-- Không có hình sản phẩm nào => lấy ảnh mặc định -->
                        <div class="preview-pic tab-content">
                            <div class="tab-pane active"
                                id="pic-1">
                                <img src="/assets/shared/img/default-image_600.png" />
                            </div>
                        </div>
                        <ul class="preview-thumbnail nav nav-tabs">
                            <li class="active">
                                <a data-target="#pic-1" data-toggle="tab">
                                    <img src="/assets/shared/img/default-image_600.png" />
                                </a>
                            </li>
                        </ul>
                        {% endif %}
                    </div>
                    <div class="details col-md-6">
                        <h3 class="product-title">{{ sanpham.sp_ten }}</h3>
                        <div class="rating">
                            <div class="stars">
                                <span class="fa fa-star checked"></span>
                                <span class="fa fa-star checked"></span>
                                <span class="fa fa-star checked"></span>
                                <span class="fa fa-star"></span>
                                <span class="fa fa-star"></span>
                            </div>
                            <span class="review-no">999 reviews</span>
                        </div>
                        <p class="product-description">{{ sanpham.sp_mota_ngan }}</p>
                        <small class="text-muted">Giá cũ: <s><span>{{ sanpham.sp_gia_formated }}</span></s></small>
                        <h4 class="price">Giá hiện tại: <span>{{ sanpham.sp_gia_formated }}</span></h4>
                        <p class="vote"><strong>100%</strong> hàng <strong>Chất lượng</strong>, đảm bảo <strong>Uy
                                tín</strong>!</p>
                        <h5 class="sizes">sizes:
                            <span class="size" data-toggle="tooltip" title="cỡ Nhỏ">s</span>
                            <span class="size" data-toggle="tooltip" title="cỡ Trung bình">m</span>
                            <span class="size" data-toggle="tooltip" title="cỡ Lớn">l</span>
                            <span class="size" data-toggle="tooltip" title="cỡ Đại">xl</span>
                        </h5>
                        <h5 class="colors">colors:
                            <span class="color orange not-available" data-toggle="tooltip" title="Hết hàng"></span>
                            <span class="color green"></span>
                            <span class="color blue"></span>
                        </h5>
                        <div class="form-group">
                            <label for="soluong">Số lượng đặt mua:</label>
                            <input type="number" class="form-control" id="soluong" name="soluong">
                        </div>
                        <div class="action">
                            <a class="add-to-cart btn btn-default" id="btnThemVaoGioHang">Thêm vào giỏ hàng</a>
                            <a class="like btn btn-default" href="#"><span class="fa fa-heart"></span></a>
                        </div>
                    </div>
            </form>
        </div>
    </div>
</div>

<div class="card">
    <div class="container-fluid">
        <h3>Thông tin chi tiết về Sản phẩm</h3>
        <div class="row">
            <div class="col">
                {{ sanpham.sp_mota_chitiet }}
            </div>
        </div>
    </div>
</div>
</div>
{% endblock %}
{# End Nội dung trong block content #}

{% block customscripts %}
<script>
    function addSanPhamVaoGioHang() {
        var dulieugoi = {
            sp_ma: $('#sp_ma').val(),
            sp_ten: $('#sp_ten').val(),
            sp_gia: $('#sp_gia').val(),
            hinhdaidien: $('#hinhdaidien').val(),
            soluong: $('#soluong').val(),
        };
        console.log((dulieugoi));
        $.ajax({
            url: '/frontend/ajax/giohang-themsanpham-ajax',
            method: "POST",
            dataType: 'json',
            data: dulieugoi,
            success: function (data) {
                console.log(data);
                var htmlString =
                    `Sản phẩm đã được thêm vào Giỏ hàng. <a href="/frontend/thanhtoan/giohang">Xem Giỏ hàng</a>.`;
                $('#thongbao').html(htmlString);
                // Hiện thông báo
                $('.alert').removeClass('d-none').addClass('show');
            },
            error: function (jqXHR, textStatus, errorThrown) {
                console.log(textStatus, errorThrown);
                var htmlString = `<h1>Không thể xử lý</h1>`;
                $('#thongbao').html(htmlString);
                // Hiện thông báo
                $('.alert').removeClass('d-none').addClass('show');
            }
        });
    };
    $('#btnThemVaoGioHang').click(function (event) {
        event.preventDefault();
        addSanPhamVaoGioHang();
    });
</script>
{% endblock %}