Step 1: tạo file xử lý Logic trang Tìm kiếm Sản phẩm

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

// Truy vấn database để lấy danh sách
// 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 Loại Sản phẩm 
   --- 
*/
$sqlSelectLoaiSanPham = <<<EOT
    SELECT lsp.lsp_ma, lsp.lsp_ten, COUNT(*) soluongsanpham
    FROM `loaisanpham` lsp
    LEFT JOIN `sanpham` sp ON lsp.lsp_ma = sp.lsp_ma
    GROUP BY lsp.lsp_ma, lsp.lsp_ten
EOT;

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

// 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ề
$loaisanphamData = [];
while ($row = mysqli_fetch_array($resultSelectLoaiSanPham, MYSQLI_ASSOC)) {
    $loaisanphamData[] = array(
        'lsp_ma' => $row['lsp_ma'],
        'lsp_ten' => $row['lsp_ten'],
        'soluongsanpham' => $row['soluongsanpham'],
    );
}
/* --- End Truy vấn dữ liệu Loại Sản phẩm --- */

/* --- 
   --- 3.Truy vấn dữ liệu Nhà sản xuất
   --- 
*/
$sqlSelectNhaSanXuat = <<<EOT
    SELECT nsx.nsx_ma, nsx.nsx_ten, COUNT(*) soluongsanpham
    FROM `nhasanxuat`nsx
    LEFT JOIN `sanpham` sp ON nsx.nsx_ma = sp.nsx_ma
    GROUP BY nsx.nsx_ma, nsx.nsx_ten
EOT;

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

// 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ề
$nhasanxuatData = [];
while ($row = mysqli_fetch_array($resultSelectNhaSanXuat, MYSQLI_ASSOC)) {
    $nhasanxuatData[] = array(
        'nsx_ma' => $row['nsx_ma'],
        'nsx_ten' => $row['nsx_ten'],
        'soluongsanpham' => $row['soluongsanpham'],
    );
}
/* --- End Truy vấn dữ liệu Nhà sản xuất --- */

/* --- 
   --- 4.Truy vấn dữ liệu Khuyến mãi
   --- 
*/
$sqlSelectKhuyenMai = <<<EOT
    SELECT km.km_ma, km.km_ten, km_noidung, km_tungay, km_denngay, COUNT(*) soluongsanpham
    FROM `khuyenmai` km
    LEFT JOIN `sanpham` sp ON km.km_ma = sp.km_ma
    GROUP BY km.km_ma, km.km_ten, km_noidung, km_tungay, km_denngay
EOT;

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

// 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ề
$khuyenmaiData = [];
while ($row = mysqli_fetch_array($resultSelectKhuyenMai, MYSQLI_ASSOC)) {
    $khuyenmaiData[] = array(
        'km_ma' => $row['km_ma'],
        'km_ten' => $row['km_ten'],
        'km_noidung' => $row['km_noidung'],
        'km_tungay' => $row['km_tungay'],
        'km_denngay' => $row['km_denngay'],
        'soluongsanpham' => $row['soluongsanpham'],
    );
}
/* --- End Truy vấn dữ liệu Nhà sản xuất --- */

/* --- 
   --- 5.Truy vấn dữ liệu Sản phẩm theo keyword tìm kiếm
   --- 
*/
// Giữ lại keyword mà người dùng tìm kiếm
$keyword_tensanpham = isset($_GET['keyword_tensanpham']) ? $_GET['keyword_tensanpham'] : '';
$keyword_loaisanpham = isset($_GET['keyword_loaisanpham']) ? $_GET['keyword_loaisanpham'] : [];
$keyword_nhasanxuat = isset($_GET['keyword_nhasanxuat']) ? $_GET['keyword_nhasanxuat'] : [];
$keyword_khuyenmai = isset($_GET['keyword_khuyenmai']) ? $_GET['keyword_khuyenmai'] : [];
$keyword_sotientu = isset($_GET['keyword_sotientu']) ? $_GET['keyword_sotientu'] : 0;
$keyword_sotienden = isset($_GET['keyword_sotienden']) ? $_GET['keyword_sotienden'] : 50000000;

// Câu lệnh query động tùy theo yêu cầu tìm kiếm của người dùng
$sqlDanhSachSanPham = <<<EOT
    SELECT sp.sp_ma, sp.sp_ten, sp.sp_gia, sp.sp_giacu, sp.sp_mota_ngan, sp.sp_soluong, lsp.lsp_ten, MAX(hsp.hsp_tentaptin) AS hsp_tentaptin
    FROM `sanpham` sp
    JOIN `loaisanpham` lsp ON sp.lsp_ma = lsp.lsp_ma
    LEFT JOIN `hinhsanpham` hsp ON sp.sp_ma = hsp.sp_ma
    LEFT JOIN `nhasanxuat` nsx ON sp.nsx_ma = nsx.nsx_ma
    LEFT JOIN `khuyenmai` km ON sp.km_ma = km.km_ma

EOT;

// Tìm theo tên sản phẩm
$sqlWhereArr = [];
if (!empty($keyword_tensanpham)) {
    $sqlWhereArr[] = "sp.sp_ten LIKE '%$keyword_tensanpham%'";
}
// Tìm theo loại sản phẩm
if (!empty($keyword_loaisanpham)) {
    $value = implode(',', $keyword_loaisanpham);
    $sqlWhereArr[] = "lsp.lsp_ma IN ($value)";
}
// Tìm theo nhà sản xuất
if (!empty($keyword_nhasanxuat)) {
    $value = implode(',', $keyword_nhasanxuat);
    $sqlWhereArr[] = "nsx.nsx_ma IN ($value)";
}
// Tìm theo khuyến mãi
if (!empty($keyword_khuyenmai)) {
    $value = implode(',', $keyword_khuyenmai);
    $sqlWhereArr[] = "km.km_ma IN ($value)";
}
// Tìm theo khoảng giá tiền
if (!empty($keyword_sotientu) && !empty($keyword_sotienden)) {
    $sqlWhereArr[] = "sp.sp_gia BETWEEN $keyword_sotientu AND $keyword_sotienden";
}

// Câu lệnh cuối cùng
if (count($sqlWhereArr) > 0) {
    $sqlWhere = "WHERE " . implode(' AND ', $sqlWhereArr);
    $sqlDanhSachSanPham .= $sqlWhere;
}
$sqlDanhSachSanPham .= <<<EOT
    GROUP BY sp.sp_ma, sp.sp_ten, sp.sp_gia, sp.sp_giacu, sp.sp_mota_ngan, sp.sp_soluong, lsp.lsp_ten
EOT;

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

// 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ề
$dataDanhSachSanPham = [];
while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
        $dataDanhSachSanPham[] = array(
            'sp_ma' => $row['sp_ma'],
            'sp_ten' => $row['sp_ten'],
            'sp_gia' => number_format($row['sp_gia'], 2, ".", ",") . ' vnđ',
            'sp_giacu' => number_format($row['sp_giacu'], 2, ".", ","),
            'sp_mota_ngan' => $row['sp_mota_ngan'],
            'sp_soluong' => $row['sp_soluong'],
            'lsp_ten' => $row['lsp_ten'],
            'hsp_tentaptin' => $row['hsp_tentaptin'],
        );
    }
// dd($sqlWhereArr, $sqlWhere, $sqlDanhSachSanPham, $dataDanhSachSanPham);

// Yêu cầu `Twig` vẽ giao diện được viết trong file `frontend/product/search.html.twig`
echo $twig->render(
    'frontend/product/search.html.twig',
    [
        // Danh mục tiêu chí tìm kiếm
        'danhsachloaisanpham' => $loaisanphamData,
        'danhsachnhasanxuat' => $nhasanxuatData,
        'danhsachkhuyenmai' => $khuyenmaiData,
        'danhsachsanpham' => $dataDanhSachSanPham,

        // Keyword người dùng đã tìm kiếm
        'keyword_tensanpham' => $keyword_tensanpham,
        'keyword_loaisanpham' => $keyword_loaisanpham,
        'keyword_nhasanxuat' => $keyword_nhasanxuat,
        'keyword_khuyenmai' => $keyword_khuyenmai,
        'keyword_sotientu' => $keyword_sotientu,
        'keyword_sotienden' => $keyword_sotienden,
    ]
);

Step 2: tạo template giao diện trang Tìm kiếm

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

{# Nội dung trong block title #}
{% block title %}
Liên hệ
{% endblock %}
{# End Nội dung trong block title #}

{# Nội dung trong block headline #}
{% block headline %}
Liên hệ
{% endblock %}
{# End Nội dung trong block headline #}

{# Nội dung trong block content #}
{% block content %}
<div class="container mt-4">
    <form name="frmTimKiem" method="get" action="/project-nentang/frontend/product/search.php">
        <h1 class="text-center">Tìm kiếm sản phẩm</h1>
        <div class="row">
            <div class="col col-md-12">
                <h5 class="text-center">Cung cấp kiến thức nền tảng về Lập trình, Thiết kế Web, Cơ sở dữ liệu</h5>
                <h5 class="text-center">Giúp các bạn có niềm tin, hành trang kiến thức vững vàng trên con đường trở
                    thành Nhà phát triển Phần mềm</h5>
                <div class="text-center">
                    <button type="button" id="btnReset" class="btn btn-warning">Xóa bộ lọc</button>
                    <button class="btn btn-primary btn-lg">Tìm kiếm <i class="fa fa-forward"
                            aria-hidden="true"></i></button>
                </div>
            </div>
        </div>
        <div class="row">
            <aside class="col-sm-4">
                <p>Bộ lọc </p>
                <div class="card">
                    <!-- Tìm kiếm theo tên sản phẩm -->
                    <article class="card-group-item">
                        <header class="card-header">
                            <h6 class="title">Tên sản phẩm </h6>
                        </header>
                        <div class="filter-content">
                            <div class="card-body">
                                <input class="form-control" type="text" placeholder="Tìm kiếm" aria-label="Search"
                                    name="keyword_tensanpham"
                                    value="{{ keyword_tensanpham ? keyword_tensanpham : '' }}">
                            </div> <!-- card-body.// -->
                        </div>
                    </article> <!-- // Tìm kiếm theo Tên sản phẩm -->

                    <!-- Tìm kiếm theo Loại sản phẩm -->
                    <article class="card-group-item">
                        <header class="card-header">
                            <h6 class="title">Loại sản phẩm </h6>
                        </header>
                        <div class="filter-content">
                            <div class="card-body">
                                {% for loaisanpham in danhsachloaisanpham %}
                                <div class="custom-control custom-checkbox">
                                    <span
                                        class="float-right badge badge-light round">{{ loaisanpham.soluongsanpham }}</span>
                                    <input type="checkbox" class="custom-control-input" name="keyword_loaisanpham[]"
                                        value="{{ loaisanpham.lsp_ma }}" id="chk-loaisanpham-{{ loaisanpham.lsp_ma }}"
                                        {{ (loaisanpham.lsp_ma in keyword_loaisanpham) ? 'checked' : '' }}>
                                    <label class="custom-control-label"
                                        for="chk-loaisanpham-{{ loaisanpham.lsp_ma }}">{{ loaisanpham.lsp_ten }}</label>
                                </div> <!-- form-check.// -->
                                {% endfor %}
                            </div> <!-- card-body.// -->
                        </div>
                    </article> <!-- // Tìm kiếm theo Loại sản phẩm -->

                    <!-- Tìm kiếm theo Nhà sản xuất -->
                    <article class="card-group-item">
                        <header class="card-header">
                            <h6 class="title">Nhà sản xuất </h6>
                        </header>
                        <div class="filter-content">
                            <div class="card-body">
                                {% for nhasanxuat in danhsachnhasanxuat %}
                                <div class="custom-control custom-checkbox">
                                    <span
                                        class="float-right badge badge-light round">{{ nhasanxuat.soluongsanpham }}</span>
                                    <input type="checkbox" class="custom-control-input" name="keyword_nhasanxuat[]"
                                        value="{{ nhasanxuat.nsx_ma }}" id="chk-nhasanxuat-{{ nhasanxuat.nsx_ma }}"
                                        {{ (nhasanxuat.nsx_ma in keyword_nhasanxuat) ? 'checked' : '' }}>
                                    <label class="custom-control-label"
                                        for="chk-nhasanxuat-{{ nhasanxuat.nsx_ma }}">{{ nhasanxuat.nsx_ten }}</label>
                                </div> <!-- form-check.// -->
                                {% endfor %}
                            </div> <!-- card-body.// -->
                        </div>
                    </article> <!-- // Tìm kiếm theo Nhà sản xuất -->

                    <!-- Tìm kiếm theo Khuyến mãi -->
                    <article class="card-group-item">
                        <header class="card-header">
                            <h6 class="title">Khuyến mãi </h6>
                        </header>
                        <div class="filter-content">
                            <div class="card-body">
                                {% for khuyenmai in danhsachkhuyenmai %}
                                <div class="custom-control custom-checkbox">
                                    <span
                                        class="float-right badge badge-light round">{{ khuyenmai.soluongsanpham }}</span>
                                    <input type="checkbox" class="custom-control-input" name="keyword_khuyenmai[]"
                                        value="{{ khuyenmai.km_ma }}" id="chk-khuyenmai-{{ khuyenmai.km_ma }}"
                                        {{ (khuyenmai.km_ma in keyword_khuyenmai) ? 'checked' : '' }}>
                                    <label class="custom-control-label"
                                        for="chk-khuyenmai-{{ khuyenmai.km_ma }}">{{ khuyenmai.km_ten }}</label>
                                </div> <!-- form-check.// -->
                                {% endfor %}
                            </div> <!-- card-body.// -->
                        </div>
                    </article> <!-- // Tìm kiếm theo Nhà sản xuất -->

                    <!-- Tìm kiếm theo khoảng giá tiền -->
                    <article class="card-group-item">
                        <header class="card-header">
                            <h6 class="title">Khoảng tiền </h6>
                        </header>
                        <div class="filter-content">
                            <div class="card-body">
                                <div class="form-row">
                                    <div class="form-group col-md-6">
                                        <label>Từ</label>
                                        <input type="range" class="custom-range" min="0" max="50000000" step="100000"
                                            id="sotientu" name="keyword_sotientu"
                                            value="{{ keyword_sotientu ? keyword_sotientu : 0 }}">
                                        <span><span id="sotientu-text"></span></span>
                                    </div>
                                    <div class="form-group col-md-6 text-right">
                                        <label>Đến</label>
                                        <input type="range" class="custom-range" min="0" max="50000000" step="100000"
                                            id="sotienden" name="keyword_sotienden"
                                            value="{{ keyword_sotienden ? keyword_sotienden : 50000000 }}">
                                        <span><span id="sotienden-text"></span></span>
                                    </div>
                                </div>
                            </div> <!-- card-body.// -->
                        </div>
                    </article> <!-- // Tìm kiếm theo khoảng giá tiền -->

                    <!-- Tìm kiếm theo màu sắc sản phẩm -->
                    <article class="card-group-item">
                        <header class="card-header">
                            <h6 class="title">Màu sắc (tùy chọn thêm)</h6>
                        </header>
                        <div class="filter-content">
                            <div class="card-body">
                                <label class="btn btn-danger">
                                    <input class="" type="checkbox" value="">
                                    <span class="form-check-label">Đỏ</span>
                                </label>
                                <label class="btn btn-success">
                                    <input class="" type="checkbox" value="">
                                    <span class="form-check-label">Xanh lá</span>
                                </label>
                                <label class="btn btn-primary">
                                    <input class="" type="checkbox" value="">
                                    <span class="form-check-label">Xanh dương</span>
                                </label>
                            </div> <!-- card-body.// -->
                        </div>
                    </article> <!-- // Tìm kiếm theo màu sắc sản phẩm -->
                </div> <!-- card.// -->
            </aside> <!-- col.// -->

            <!-- Giải thuật duyệt và render Danh sách sản phẩm theo dòng, cột của Bootstrap -->
            {% set counter = 0 %}
            {% set group = 2 %}
            {% set limit = danhsachsanpham|length %}
            {% set col_class_name = (12 / group) %}
            <div class="col-sm-8 mt-2">
                <div class="row">
                    <div class="col-md-12">
                        <h6>Tìm kiếm được {{ danhsachsanpham|length }} sản phẩm</h6>
                    </div>
                </div>
                <div class="row">
                    {% if danhsachsanpham|length >0 %}

                    {% for sanpham in danhsachsanpham %}
                    <div class="col-md-{{ col_class_name }}">
                        <div class="card mb-4 shadow-sm">
                            {% if sanpham.hsp_tentaptin %}
                            <a href="/project-nentang/frontend/sanpham/chitiet?sp_ma={{ sanpham.sp_ma }}">
                                <img class="bd-placeholder-img card-img-top" width="100%" height="350"
                                    src="/project-nentang/assets/uploads/{{ sanpham.hsp_tentaptin }}" />
                            </a>
                            {% else %}
                            <a href="/project-nentang/frontend/sanpham/chitiet?sp_ma={{ sanpham.sp_ma }}">
                                <img class="bd-placeholder-img card-img-top" width="100%" height="350"
                                    src="/project-nentang/assets/shared/img/default-image_600.png" />
                            </a>
                            {% endif %}
                            <div class="card-body">
                                <a href="/project-nentang/frontend/sanpham/chitiet?sp_ma={{ sanpham.sp_ma }}">
                                    <h5>{{ sanpham.sp_ten }}</h5>
                                </a>
                                <h6>{{ sanpham.lsp_ten }}</h6>
                                <p class="card-text">{{ sanpham.sp_mota_ngan }}</p>
                                <div class="d-flex justify-content-between align-items-center">
                                    <div class="btn-group">
                                        <a class="btn btn-sm btn-outline-secondary"
                                            href="/project-nentang/frontend/sanpham/chitiet?sp_ma={{ sanpham.sp_ma }}">Xem
                                            chi tiết</a>
                                    </div>
                                    <small class="text-muted text-right">
                                        <s>{{ sanpham.sp_giacu }}</s>
                                        <b>{{ sanpham.sp_gia }}</b>
                                    </small>
                                </div>
                            </div>
                        </div>
                    </div>
                    {% set counter = counter + 1 %}
                    {% if (counter % group == 0 and counter < limit) %}
                </div>
                <div class="row">
                    {% endif %}

                    {% endfor %}
                    {% else %}
                    <div class="col-md-12">
                        <h2>Xin lỗi, không tìm thấy sản phẩm nào theo yêu cầu!</h2>
                    </div>
                    {% endif %}
                </div>
            </div>
        </div> <!-- row.// -->
    </form>
</div>
{% endblock %}
{# End Nội dung trong block content #}

{% block customscripts %}
<script>
    $(document).ready(function () {
        $('#sotientu').on('change', function (e) {
            var id = e.target.value;
            document.getElementById("sotientu-text").innerHTML = id;
        });
        $('#sotientu').change();

        $('#sotienden').on('change', function (e) {
            var id = e.target.value;
            document.getElementById("sotienden-text").innerHTML = id;
        });
        $('#sotienden').change();

        function clearForm() {
            var frm_elements = frmTimKiem.elements;
            for (i = 0; i < frm_elements.length; i++) {
                field_type = frm_elements[i].type.toLowerCase();
                switch (field_type) {
                    case "text":
                    case "password":
                    case "textarea":
                    case "hidden":
                        frm_elements[i].value = "";
                        break;
                    case "radio":
                    case "checkbox":
                        if (frm_elements[i].checked) {
                            frm_elements[i].checked = false;
                        }
                        break;
                    case "select-one":
                    case "select-multi":
                        frm_elements[i].selectedIndex = -1;
                        break;
                    case "range":
                        if (frm_elements[i].name == 'keyword_sotientu') {
                            frm_elements[i].value = frm_elements[i].min;
                            document.getElementById("sotientu-text").innerHTML = frm_elements[i].min;
                        } else if (frm_elements[i].name == 'keyword_sotienden') {
                            frm_elements[i].value = frm_elements[i].max;
                            document.getElementById("sotienden-text").innerHTML = frm_elements[i].max;
                        }
                        break;
                    default:
                        break;
                }
            }
        }

        $('#btnReset').click(function (e) {
            clearForm();
        });
    });
</script>
{% endblock %}