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">×</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 %}
|