membuat multi user sudah sekarang saatnya untuk menyempurnakan aplikasi dengan mengadakan fitur pagination, search, filter, dan sort
fitur-fitur diatas berguna untuk menyari data dan menyortirnya, berikut spesifikasi kode yang dibuat
- Menggunakan Ajax
- Custom Pagination
Membuat Seed User & Product
karena kita membutuhkan data yang banyak untuk melihat hasil paginationnya maka kita gunakan seeder untuk mengisi data dengan cepat
Membuat Seed User
jalankan perintah spark, php spark make:seeder User
isi file seeder user yang terletak di 'app/Database/Seeds/User.php' dengan kode dibawah ini
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class User extends Seeder
{
public function run()
{
$replacement = function ($matches) {
$array = explode("|", $matches[1]);
return $array[array_rand($array)];
};
// build random data
$data = [];
for ($i=0; $i < 10 ; $i++) {
$username = "{alexander|bizku|ciku|lazzy|waaazu|keila|omen}";
$username_spin = preg_replace_callback("/\{([^}]+)\}/", $replacement, $username);
$username_spin = $username_spin.$i;
$data[] = [
'username' => $username_spin,
'password' => password_hash(1234, PASSWORD_DEFAULT),
'email' => $username_spin.'@gmail.com',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
$this->db->table('user')->insertBatch($data);
}
}
jalankan seeder dengan spark, php spark db:seed user
Membuat Seed Product
jalankan perintah spark, php spark make:seeder Product
isi file seeder product dengan kode dibawah ini
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class Product extends Seeder
{
public function run()
{
$replacement = function ($matches) {
$array = explode("|", $matches[1]);
return $array[array_rand($array)];
};
// get all user id
$user_ids = $this->db->table('user')->select('id')->get()->getResultArray();
// if not exist user_ids
if (!$user_ids) {
$user_ids = [1]; // set id_user to 1
}
// build random data
$data = [];
for ($i=0; $i < 100 ; $i++) {
$name = "{Jual|Jasa|Tutor|Script|Great} {membuat|menjadi|pasti} {A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z}";
$name_spin = preg_replace_callback("/\{([^}]+)\}/", $replacement, $name);
$category = "{Script|Account|Jasa|Tutorial}";
$category_spin = preg_replace_callback("/\{([^}]+)\}/", $replacement, $category);
$data[] = [
'id_user' => array_rand($user_ids) + 1,
'name' => $name_spin,
'category' => $category_spin,
'price' => rand(10000,1000000),
'photo' => 'https://via.placeholder.com/265x150/83b5ff/000000?text=Product%20Image',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
}
$this->db->table('product')->insertBatch($data);
}
}
jalankan seeder dengan spark, php spark db:seed product
Mengubah Controller Homepage
buka file controller homepage yang terletak di 'app/Controllers/Homepage.php'
ubah isinya dengan kode dibawah ini
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
class Homepage extends BaseController
{
public function index()
{
// get parameter
$page = $this->request->getGet('page_product');
$search = esc($this->request->getGet('q'));
$data['search'] = $search;
$sort = esc($this->request->getGet('sort'));
$data['sort'] = $sort;
$category = esc($this->request->getGet('category'));
$data['category'] = $category;
// load model
$productModel = new \App\Models\Product();
// data category
$data['categories'] = $productModel->getCategories();
// detect if have search parameter
if ($search) {
$data['products'] = $productModel->getData(12, 'product', $sort, $search);
}elseif ($category) {
$data['products'] = $productModel->getData(12, 'product', $sort, null, $category);
}else{
$data['products'] = $productModel->getData(12, 'product', $sort);
}
$data['pager'] = $productModel->pager;
// build title
if ($search) {
if ($page) {
$data['title'] = 'Search : '.$search . ' Page '.$page;
}else{
$data['title'] = 'Search : '. $search;
}
}elseif ($category) {
if ($page) {
$data['title'] = 'Category : '.$category . ' Page '.$page;
}else{
$data['title'] = 'Category : '. $category;
}
}else{
if ($page) {
$data['title'] = 'Product - Page '.$page;
}else{
$data['title'] = 'Homepage';
}
}
// build response
if ($this->request->isAjax()) {
// resposne for json
return $this->response->setJSON([
'title' => $data['title'],
'content' => view('homepage/product', $data)
]);
}else{
// respnse for html
return view('homepage', $data);
}
}
}
Mengubah Model Product
buka file model product yang terletak di 'app/Models/Product.php'
ubah isinya dengan kode dibawah ini
<?php
namespace App\Models;
use CodeIgniter\Model;
class Product extends Model
{
protected $table = 'product';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $protectFields = false;
// Dates
protected $useTimestamps = true;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
public function getData($limit, $type, $sort, $search = null, $category = null)
{
$builder = $this->select('
product.id,
product.name,
product.category,
product.price,
product.photo,
user.username
')
->join('user', 'product.id_user = user.id');
if (isset($search)) {
$builder->Like('product.name', $search);
}elseif (isset($category)) {
$builder->where('category', $category);
}
if (empty($sort) OR $sort == 'latest') {
$builder->orderBy('product.id','DESC');
}elseif ($sort == 'oldest') {
$builder->orderBy('product.id','ASC');
}elseif ($sort == 'high-price') {
$builder->orderBy('product.price','DESC');
}elseif ($sort == 'low-price') {
$builder->orderBy('product.price','ASC');
}
$products = $builder->paginate($limit, $type);
// load helper
helper('number');
// build data
$data = [];
foreach ($products as $product) {
// if photo as url
$photo = (filter_var($product['photo'], FILTER_VALIDATE_URL)) ? $product['photo'] : base_url('uploads/'.$product['photo']);
$data[] = array(
'id' => $product['id'],
'name' => $product['name'],
'category' => $product['category'],
'price' => number_to_currency($product['price'], "IDR", "id", 0),
'photo' => $photo,
'owner' => $product['username'],
);
}
return $data;
}
public function getDataForBootstrapTable($request)
{
$builder = $this->select('id, name, price')->where('id_user', session('auth')['id']);
// search query
$builder->like('name', $request->getGet('search'));
// sort query
$builder->orderBy($request->getGet('sort'), $request->getGet('order'));
// paging query
$builder->limit($request->getGet('limit'), $request->getGet('offset'));
$total = $builder->countAllResults(false); // set false for not reset query
$products = $builder->get()->getResultArray();
$total_filter = count($products);
// load helper
helper('number');
helper('aeshash');
// build data
$data = [];
foreach ($products as $product) {
$data[] = array(
'hash' => aeshash('enc', $product['id'] , session('auth')['id'] ),
'name' => $product['name'],
'price' => number_to_currency($product['price'], "IDR", "id", 0),
);
}
return [
'total' => $total,
'totalNotFiltered' => $total_filter,
'rows' => $data
];
}
public function getCategories()
{
$builder = $this->select('category')->distinct()->findAll();
$category_list = [];
foreach ($builder as $key => $value) {
$category_list[] = $value['category'];
}
return $category_list;
}
}
Membuat Custom Pager
pager disini maksudnya style untuk pagination
karena kita ingin menggunakan style pager bawaan bootstrap maka kita harus membuatnya secara custom, berikut langkah-langkahnya
Langkah 1 (Membuat Pager)
buat folder _pager dan buat juga filenya dengan nama bootstrap.php
isi file '_pager/bootstrap.php' dengan kode dibawah ini
<?php $pager->setSurroundCount(1) ?>
<nav class="mt-5 d-flex justify-content-between align-items-center" aria-label="Page navigation">
<div class="d-none d-md-block">
Showing Page (<?= $pager->getCurrentPageNumber().' of '.$pager->getPageCount() ?>)
</div>
<ul id="pagination" class="pagination justify-content-end mb-0">
<?php if ($pager->hasPreviousPage()) : ?>
<li class="page-item">
<a class="page-link" href="<?= $pager->getPreviousPage() ?>" aria-label="<?= lang('Pager.previous') ?>">
<span aria-hidden="true"><?= lang('Pager.previous') ?></span>
</a>
</li>
<?php endif ?>
<?php foreach ($pager->links() as $link) : ?>
<li class="page-item <?= $link['active'] ? 'active"' : '' ?>">
<a class="page-link" href="<?= $link['uri'] ?>">
<?= $link['title'] ?>
</a>
</li>
<?php endforeach ?>
<?php if ($pager->hasNextPage()) : ?>
<li class="page-item">
<a class="page-link" href="<?= $pager->getNextPage() ?>" aria-label="<?= lang('Pager.next') ?>">
<span aria-hidden="true"><?= lang('Pager.next') ?></span>
</a>
</li>
<?php endif ?>
</ul>
</nav>
Langkah 2 (Mengatur Pager)
buka pengaturan pager yang terletak di 'app/Config/Pager.php'
tambahkan pager yang baru saja kita buat dibagian property $templates
'bootstrap' => 'App\Views\_pager\bootstrap',
sehingga nantinya property $templates
menjadi seperti
public $templates = [
'default_full' => 'CodeIgniter\Pager\Views\default_full',
'default_simple' => 'CodeIgniter\Pager\Views\default_simple',
'default_head' => 'CodeIgniter\Pager\Views\default_head',
'bootstrap' => 'App\Views\_pager\bootstrap',
];
Langkah 3 (Menggunakan Pager)
edit view homepage yang terletak di 'app/Views/homepage.php'
ganti isi kodenya dengan kode dibawah ini
<?= $this->extend('_layout') ?>
<?= $this->section('content') ?>
<main id="main-page" class="pt-3 pb-5 my-auto">
<div class="container-md">
<div id="main-product">
<?= view('homepage/product'); ?>
</div>
</div>
</main>
<?= $this->endSection() ?>
<?= $this->section('js') ?>
<script type='text/javascript'>
function productPlaceholder(){
let placeholder = '';
for (var i = 1; i <= 12 ; i++) {
placeholder += `
<div class="col">
<div class="card w-100" aria-hidden="true">
<svg class="bd-placeholder-img card-img-top" width="100%" height="150px" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#83b5ff"></rect></svg>
<div class="card-body">
<h5 class="card-title placeholder-glow">
<span class="placeholder col-12 bg-primary"></span>
</h5>
<p class="card-text placeholder-glow d-flex justify-content-between">
<span class="placeholder col-4 bg-primary"></span>
<span class="placeholder col-4 bg-primary"></span>
</p>
</div>
</div>
</div>`;
}
$("#row-product").html(placeholder);
}
function PaginationAjax() {
let click_status = false;
$('#pagination').on('click','a',function(e){
// reverse link action
e.preventDefault();
// get href data
var href = $(this).attr('href');
// disable click
if (click_status) {return false;}
click_status = true;
// disable pagination
$('#pagination li.page-item').addClass('disabled');
// go to top
$('html, body').animate({ scrollTop: 0 }, 0);
// placeholder
productPlaceholder();
$.ajax({
url: href,
type: 'POST',
dataType: 'JSON',
success: function(data) {
// change title
document.title = data.title;
// change url history
history.pushState({}, data.title, href);
// refresh data
$('#main-product').html(data.content);
// re-init because its refresh DOM
sortProduct();
categoryProduct();
PaginationAjax();
// enable click
click_status = false;
},error: function(xhr, statusText, errorThrown) {
alert(statusText);
// enable pagination
$('#pagination li.page-item').removeClass('disabled');
// enable click
click_status = false;
}
});
});
}
// init for first DOM
PaginationAjax();
// for search
function searchProduct(){
$("#search-product").on("submit", function(e){
e.preventDefault();
const form = $(this),
search = $("#search-product input[name=q]").val();
search_url = (search.length > 0) ? base_url + '?q=' + search : base_url;
$.ajax({
url: search_url,
type: 'POST',
dataType: 'JSON',
success: function(data) {
// change title
document.title = data.title;
// change url history
history.pushState({}, data.title, search_url);
// refresh data
$('#main-product').html(data.content);
// re-init because its refresh DOM
sortProduct();
categoryProduct();
PaginationAjax();
},error: function(xhr, statusText, errorThrown) {
alert(statusText);
}
});
});
// nav search key up
function debounce(callback, wait) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(function () { callback.apply(this, args); }, wait);
};
}
$("#search-product input[name=q]").on("keyup search", debounce(() => {
$('#search-product').submit();
},2000));
}
// init nav search
searchProduct();
function sortProduct(){
$('#sort-product').on('change', function() {
const sort = this.value;
const category = $('#category-product').val();
const search = $("#search-product input[name=q]").val();
// build url
let build_url;
if (search.length > 0) {
build_url = base_url + '?q=' + search +'&sort=' + sort;
}else if (category.length > 0) {
build_url = base_url + '?category=' + category +'&sort=' + sort;
}else{
build_url = base_url + '?sort=' + sort;
}
// placeholder
productPlaceholder();
$.ajax({
url: build_url,
type: 'POST',
dataType: 'JSON',
success: function(data) {
// change title
document.title = data.title;
// change url history
history.pushState({}, data.title, build_url);
// refresh data
$('#main-product').html(data.content);
// re-init because its refresh DOM
sortProduct();
categoryProduct();
PaginationAjax();
},error: function(xhr, statusText, errorThrown) {
alert(statusText);
}
});
});
}
// init sortProduct
sortProduct();
function categoryProduct(){
$('#category-product').on('change', function() {
const category = this.value;
const sort = $('#sort-product').val();
const build_url = (category.length > 0) ? base_url + '?category=' + category +'&sort=' + sort : base_url + '?sort=' + sort;
// placeholder
productPlaceholder();
$.ajax({
url: build_url,
type: 'POST',
dataType: 'JSON',
success: function(data) {
// change title
document.title = data.title;
// change url history
history.pushState({}, data.title, build_url);
// refresh data
$('#main-product').html(data.content);
// re-init because its refresh DOM
sortProduct();
categoryProduct();
PaginationAjax();
},error: function(xhr, statusText, errorThrown) {
alert(statusText);
}
});
});
}
// init categoryProduct
categoryProduct();
</script>
<?= $this->endSection() ?>
buat folder homepage didalam folder views dan buat file baru didalam folder homepage tersebut dengan nama product.php
nantinya akan seperti ini
.
└── app/
└── Views/
└── homepage/
└── product.php
buka file 'homepage/product.php' dan isi dengan kode dibawah ini
<?php if (!$products): ?>
<?php if ($search): ?>
<div class="text-center fs-3">
no product found with keyword : <?= $search ?>
</div>
<?php else: ?>
<div class="text-center fs-3">
There are no products to display yet
</div>
<?php endif ?>
<?php endif ?>
<?php if ($products): ?>
<div id="row-product-title" class="row align-items-center mb-3">
<div class="col-12 col-md">
<?php if (!$products): ?>
<?php if ($search): ?>
<h2 class="fs-4">
no product found with keyword : <?= $search ?>
</h2>
<?php else: ?>
<h2 class="fs-4">
There are no products to display yet
</h2>
<?php endif ?>
<?php else: ?>
<?php if ($search): ?>
<h2 class="fs-4 mb-3">
Search Product : <?= $search ?>
</h2>
<?php elseif($category): ?>
<h2 class="fs-4 mb-3">
Category : <?= $category ?>
</h2>
<?php else: ?>
<h2 class="fs-4">
Display All Product
</h2>
<?php endif ?>
<?php endif ?>
</div>
<div class="col-12 col-md-auto">
<div class="d-flex">
<select id="category-product" name="category" class="form-select me-2">
<option value="" selected="">
All Category
</option>
<?php foreach ($categories as $filter): ?>
<option value="<?= $filter ?>" <?= $category == $filter ? 'selected' : '' ?>>
<?= $filter ?>
</option>
<?php endforeach ?>
</select>
<select id="sort-product" name="sort" class="form-select">
<option value="latest" <?= $sort == 'latest' ? 'selected' : '' ?>>
Latest
</option>
<option value="oldest" <?= $sort == 'oldest' ? 'selected' : '' ?>>
Oldeset
</option>
<option value="high-price" <?= $sort == 'high-price' ? 'selected' : '' ?>>
High Price
</option>
<option value="low-price" <?= $sort == 'low-price' ? 'selected' : '' ?>>
Low Price
</option>
</select>
</div>
</div>
</div>
<?php endif ?>
<div id="row-product" class="row row-cols-2 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-4 g-3">
<?php foreach ($products as $product): ?>
<!-- make card same height with d-flex align-items-stretch -->
<div class="col d-flex align-items-stretch">
<div class="card w-100 shadow-sm">
<div style="position: relative;">
<img class="bd-placeholder-img card-img-top" src="<?= $product['photo'] ?>">
<?php if (!$category): ?>
<span class="badge bg-primary" style="position: absolute;right: 5px;top: 10px;">
<i class="bi bi-hash" style="font-size:10px"></i>
<?= $product['category'] ?>
</span>
<?php endif ?>
</div>
<!-- make card same height with d-flex flex-column -->
<div class="card-body d-flex flex-column">
<h5 class="card-title pb-2">
<a class="text-decoration-none" href="#">
<?= $product['name'] ?>
</a>
</h5>
<!-- make this element always on bottom when height is not same -->
<div class="d-flex justify-content-md-between flex-column flex-md-row align-items-start mb-1 mt-auto">
<small class="text-muted"><?= $product['price'] ?></small>
<div>
<i class="bi bi-person-circle"></i> <?= $product['owner'] ?>
</div>
</div>
</div>
</div>
</div>
<?php endforeach ?>
</div>
<div class="row">
<div class="col-12 text-center">
<?php if ($products): ?>
<?= $pager->links('product', 'bootstrap') ?>
<?php endif ?>
</div>
</div>
Uji Coba Pager
jalankan webserver menggunakan spark
php spark serve
buka browser dan kunjungi url http://localhost:8080
berikut hasilnya