Membuat Pagination, Search, Filter, Sort dengan Ajax di CodeIgniter 4

irfan-photo irfan · 2 bulan lalu

Membuat Pagination, Search, Filter, Sort dengan Ajax di CodeIgniter 4

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 alamt http://localhost:8080

berikut hasilnya

Download Source Code

Terakhir diupdate 19 Jun 2022 06:45 WIB
Daftar Konten Belajar CodeIgniter 4