เนื้อหานี้เราจะมาดูการใช้งาน Pagination
หรือการแบ่งหน้ารายการข้อมูลใน CI4 ซึ่งมีวิธี
และรุปแบบการใช้งานที่ค่อนข้างง่ายและสะดวก
นอกจากนั้น ยังสามารถปรับแแต่งค่าต่างๆ ได้
ง่ายและยืดหยุ่นอีกด้วย เนื้อหานี้จำเป็นต้องมีความรู้
เกี่ยวกับส่วนต่างๆของ CodeIgniter จากบทความผ่านๆ มา
โดยเฉพาะส่วนของการใช้งาน model ทบทวนได้ที่
รู้จักกับ Model และวิธีการใช้งาน Model ใน CodeIgniter 4 http://niik.in/1004
https://www.ninenik.com/content.php?arti_id=1004 via @ninenik
การแบ่งหหน้ารายการ หรือ Pagination
ใน CI4 เราสามารถกำหนดการแบ่งหน้ารายกาาข้อมูลได้ง่ายๆ ทั้งโดยใช้งานผ่าน คำสั่งของ Model หรือจะเรียก
ใช้งานผ่าน Controller โดยใช้ Pagination Library โดยตรง ก็สามารถทำได้เหมือนกัน ในที่นี้เราจะเริ่มต้นที่การใช้
งานผ่านคำสั่งของ model โดยจะใช้ข้อมูลตารางจังหวัดในประเทศทไทยตาราง 'tbl_provinces' จากลิ้งค์โครงสร้าง
ฐานข้อมูลนี้ประกอบ http://niik.in/que_2398_6277
เตรียม Model ข้อมูล
ให้เราสร้างไฟล์ model อย่างง่ายสำหรับใช้งานกับตาราง tbl_provinces ในที่นี้ใช้ชื่อเป้น ProvincesModel.php
app/Models/ProvincesModel.php
<?php namespace App\Models; // กำหนด namespace use CodeIgniter\Model; // เรียกใช้งาน Model class class ProfincesModel extends Model // สร้าง Model class โดยสืบทอดจาก Model { protected $table = 'tbl_provinces'; protected $primaryKey = 'province_id'; protected $returnType = 'array'; // array or object or custom class }
การใช้งานการแบ่งหน้าผ่าน Model
เราจะใช้งาน ProvincesModel ผ่าน Controller ตัวอย่างที่ ไฟล์ Helloworld.php โดยจะไม่สร้าง view เพื่อ
ลดขั้นตอนโดยจะใช้แท็ก html แสดงใน Controller โดยตรง ซึ่งสามารถปรับประะยุกต์ใช้กับการกำหนด view ได้
app/Controllers/Holloworold.php
<?php namespace App\Controllers; use App\Models\ProvincesModel; // ใช้งาน model use CodeIgniter\Controller; // เรียกใช้งาน Controller class class Helloworld extends Controller { public function index() { $pvModel = model('ProvincesModel'); $data = [ 'provinces' => $pvModel->paginate(10), 'pager' => $pvModel->pager ]; $result = $data['provinces']; // ข้อมูลหน้าปัจจุบัน echo $data['pager']->links(); // ลิ้งค์แบ่งหน้า แบบตัวเลข echo "<ul>"; if($result){ // วนลูปแสดงข้อมูล foreach($result as $row){ echo "<li>{$row['province_name']}</li>"; } } echo "</ul>"; echo $data['pager']->simpleLinks(); //ลิ้งค์แบบข้อความ Older และ Newer } }
เราสร้าง model ไว้ในตัวแปรชื่อ $pvModel จากนั้นเรียกใช้คำสั่งต่างของ Model ในการใช้งาน Pagination
มาดูคำสั่งที่ถูกใช้งานในโค้ดข้างต้น
$pvModel->paginate();
เป็นคำสั่งให้ข้อมูลในตาราง tbl_provinces ที่กำหนดใน ProvincesModel โดยให้แสดงหน้าละ 10 รายการ โดยกำหนดตัวเลข
จำนวนรายการที่ต้องการแสดงเป็น parameter แรก คำสั่งนี้เปรียบการคิวรี่เพื่อแสดงข้อมูลโดยกำหนด limit ที่ต้องการแสดง ค่า
ที่คืนกลับมาก็จะเปฺ็น Result Object หรือรายการข้อมูลถ้ามี เรากำหนดค่าที่คืนกลับมาในตัวแปร $data['provinces'] ค่านี้จะถูกส่ง
ไปยัง view หากเราใช้งาน view แต่ในที่นี้เราแสดงโดยตรง ไม่ต้องส่งค่าเข้าไป
คำสั่งทำงานร่วมกับ Pager Library ให้เราโดยอัตโนมัติ ดังนั้นเราสามารถนำมาใช้งานใน Controller ได้เลย โดยเปรียบเสมือน
การเรียกผ่าน url ที่มี query string เป็น "page=x" เมื่อ x คือหน้าข้อมูลปัจจุบัน
$pvModel->pager
เป็นคำสั่งที่ใช้สำหรับสร้าง Pager instance สำหรับใช้คำส่งต่างๆ ของ Pager Library ของ Model ในตัวอย่างเรากำหนดไว้ที่
ตัวแปร $data['pager'] ค่านี้จะถูกส่งไปใช้งานใน view เหมือนกัน แต่ในที่นี้เราจะเรียกผ่าน Controller โดยตรงแทน
$data['pager']->links();
เป็นคำสั่งของ pager library สำหรับแสดงการแบ่งหน้าตัวเลข และข้อความ ตามรูปแบบดังนี้
First Previous 1 2 3 Next Last
เราสามารถแสดงคำสั่งนี้ตำแหน่งไหนก็ได้ที่ต้องการแสดงการแบ่งหน้า โดยตัวคำสั่งจะจัดการรูปแบบการแสดงให้อัตโนมัติ เช่น
หากอยู่หน้าแรก ข้อความว่า First และ Previous ก็จะไม่แสดง รวมถึงการกำหนดจำนวนลิ้งค์หน้าที่จะแสดงด้วย โดยไม่ได้แสดงทั้งหมด
เสียทีเดียว นอกจากนั้นยังสร้างลิ้งค์ให้อัตโนมัติ ในรูปแบบดังนี้
https://www.mysslweb.com/helloworld?page=8
หน้าสุดท้ายของรายการจะเป็นหน้าที่ 8 จาก 77 รายการ แสดงทีละ 10
เนื่องจากในตัวอย่างเราแสดงผลใน Controller เลย ตัว Pager Instance จะอ้างอิงผ่านตัวแปร $data['pager'] แต่ถ้าเราส่งค่าไปใน
views ก็จะเรียกใช้านผ่านตัวแปร $pager ก็จะเป็น $pager->links() เป็นต้น
ตัวอย่างโครงสร้าง HTML ของการแบ่งหน้าที่ CI สร้างให้เมื่อยู่หน้าแรก
<nav aria-label="Page navigation"> <ul class="pagination"> <li class="active"> <a href="https://www.mysslweb.com/helloworld?page=1"> 1 </a> </li> <li> <a href="https://www.mysslweb.com/helloworld?page=2"> 2 </a> </li> <li> <a href="https://www.mysslweb.com/helloworld?page=3"> 3 </a> </li> <li> <a href="https://www.mysslweb.com/helloworld?page=4" aria-label="Next"> <span aria-hidden="true">Next</span> </a> </li> <li> <a href="https://www.mysslweb.com/helloworld?page=8" aria-label="Last"> <span aria-hidden="true">Last</span> </a> </li> </ul> </nav>
*ข้อสังเกต: สำหรับลิ้งค์ของ previous และ next จะไม่ได้หมายถึงหน้าก่อนหน้า หรือหน้าถัดไป นับจากหน้าปัจจุบัน
แต่จะหมายถึง หน้าก่อนหน้า และหน้าลำดับถัดไปจากลำดับการแบ่งหน้า ที่แสดง อย่างข้างต้น หน้าปัจจุบันเป็นหน้า 1 และเรา
มีหน้า 2 และ 3 แสดงอยู่แล้ว หน้าถัดไปจึงเป็นลิ้งค์ของหน้าที่ 4 แทน จะไม่ใช่หน้าที่ 2 แบบนี้เป็นต้น
$data['pager']->simpleLinks();
เป็นคำสั่งของ pager library สำหรับแสดงการแบ่งหน้าโดยแสดงเป็นข้อความ Newer สำหรับรายการใหม่ หรือปัจจุบัน และ
ข้อความ Older สำหรับบทความกอนหน้า หรือบทความเก่า รูปแบบจะเป็นดังนี้
Newer Older
รูปแบบโครงสร้าง HTML
<nav> <ul class="pager"> <li class="disabled"> <a href="#" aria-label="Previous"> <span aria-hidden="true">Newer</span> </a> </li> <li> <a href="https://www.mysslweb.com/helloworld?page=2" aria-label="Next"> <span aria-hidden="true">Older</span> </a> </li> </ul> </nav>
มาดูตัวอย่างผลลัพธ์การแบ่งหน้าข้อมูลจากตัวอย่างโค้ดใน Helloworld จะได้เป็นดังนี้
เนื่องจากเราไม่ได้กำหนด css style ให้กับโค้ดตัวอย่าง จึงเห็นเพียงโครงสร้างข้อมูลในรูปแบบการใช้งาน ul แท็กเท่านั้น เราสามารถ
สร้าง css class จัดรูปแบบการแสดงการแบ่งหน้าได้เอง หรือจะใช้ css framework อย่าง bootstrap ที่มีส่วนจัดการของ Pagination
Component ให้อยู่แล้วก็ได้ จะได้แนะนำวิธีเพิ่มเติมในหัวข้อในลำดับต่อๆ ไป
การแบ่งหน้ามากกว่า 1 รายการข้อมูล
ในบางกรณีเราอาจำเป็นต้องการแสดงข้อมูลมากกว่าหนึ่งรายการข้อมูลในหน้าเดียวกัน พร้อมทั้งให้รายการข้อมูลนั้นๆ สามารถแบ่ง
หน้าข้อมูลได้ด้วย เช่น ในหน้านั้นแสดงรายการจังหวัดพร้อมทั้งแบ่งหน้า และแสดงรายการข่าวพร้อมทั้งแบ่งหน้า อยู่ในหน้าเดียวกัน
เราก็สามารถจัดการได้ง่ายโดยใช้งาน Pager Library ในที่นี้ เราจะสมมติใช้ model ข้อมูลเดียวกัน แทนรายการข้อมูล 2 รายการ
โดยรายการข้อมูลแรก จะแสดงจังหวัดหน้าละ 5 รายการ และรายการข้อมูลที่สองแสดงจังหวัดหน้าละ 2 รายการ สามารถทำได้ดังนี้
app/Controllers/Holloworold.php
<?php namespace App\Controllers; use App\Models\ProvincesModel; // ใช้งาน model use CodeIgniter\Controller; // เรียกใช้งาน Controller class class Helloworld extends Controller { public function index() { $pvModel = model('ProvincesModel'); $pvModel2 = model('ProvincesModel', false); // สร้างเป็น instance ใหม่ $data = [ 'provinces1' => $pvModel->paginate(5, 'group1'), 'provinces2' => $pvModel2->paginate(2, 'group2'), 'pager1' => $pvModel->pager, 'pager2' => $pvModel2->pager ]; $result1 = $data['provinces1']; echo "<ul>"; if($result1){ foreach($result1 as $row){ echo "<li>{$row['province_name']}</li>"; } } echo "</ul>"; echo $data['pager1']->links('group1'); echo "<hr>"; $result2 = $data['provinces2']; echo "<ul>"; if($result2){ foreach($result2 as $row){ echo "<li>{$row['province_name']}</li>"; } } echo "</ul>"; echo $data['pager2']->links('group2'); } }
จากตัวอย่าง เราสร้าง instance model ของจังหวัดใหม่ขึ้นมาอีกอัน และกำหนดข้อมูลจังหวัดเป็น 2 ชุดผ่านตัวแปร
$data['province1'] และ $data['province2'] เป็นข้อมูลที่ใช้งานคำสั่ง paginate() คนละรูปแบบ คือกำหนดจำนวนที่จะแสดง
แตกต่างกัน พร้อมทั้งกำหนด parameter ตัวที่สอง เป็น group ของ ตัวแปร page ที่ใช้ในการแบ่งหน้า เช่น ถ้าเรากำหนด
group1 ตัวแปร page=x ที่ใช้แบ่งหน้าก็เปลี่ยนเป็น page_group1=x และจะมีผลกับเฉพาะการแบ่งหน้าของแต่ละรายการข้อมูล
เช่นกันกับตัวแปร $data['pager1'] และ $data['pager2'] ที่เรากำหนดแยกให้กับ Pager Instance ของแต่ละข้อมูลแยกกันด้วย
เพื่อใช้เป็นตัวสร้างลิ้งค์แบ่งหน้าแต่ละรายการข้อมูล
มาดูตัวอย่าง url ที่เกิดขึ้น
// เมื่อเราเปิดไปหน้าที่สองของรายการข้อมูลแรก https://www.mysslweb.com/helloworld?page_group1=2 // ขณะที่เราอยู่หน้าที่สองของรายการข้อมูลแรก เราเปิดไปหน้าสุดท้ายของรายการข้อมูลที่สอง จะได้เป็น https://www.mysslweb.com/helloworld?page_group1=2&page_group2=39
จะเห็นว่าขณะที่รายการข้อมูลแรกอยู่หน้าที่ 2 ผ่านตัวแปร page_group1 และ รายการข้อมูลที่สองก็จะแสดงหน้าที่ 39 ผ่านตัวแปร
page_group2 เป้นการทำงานแยกกันชัดเจน ทำให้เราสามารถแสดงรายการข้อมูลแบบแบ่งหน้าได้พร้อมกันในหน้าเดียว
ดูตัวอย่างผลลัพธ์ จะได้เป็น
การแบ่งหน้าแบบกำหนด Page เอง
ในการแบ่งหน้าโดยใช้ Pager Library ข้างต้น ปกติแล้วค่าที่ใช้เป็นตัวกำหนดหรือส่งค่าสำหรับแบ่งหน้า จะทำผ่านตัวแปร $_GET
เช่น $_GET[page'] หรือ $_GET['page_x'] หากใช้หลายรายการในหนึ่งหน้า อย่างไรก็ตาม เราสามารถ ใช้ค่าจากตัวแปรอื่นแบบ
กำหนดเองได้ โดยในขั้นตอนการใช้คำสั่ง paginate() ให้เรากำหนดตัวแปร หรือเลขหน้าที่ต้องการใน parameter ที่ 3 ดังนี้ได้
$pvModel->paginate(5,'',$page),
app/Controllers/Holloworold.php
<?php namespace App\Controllers; use App\Models\ProvincesModel; // ใช้งาน model use CodeIgniter\Controller; // เรียกใช้งาน Controller class class Helloworld extends Controller { public function index() { $pvModel = model('ProvincesModel'); // $page = 1; // กรณีกำหนดค่าตายตัว // http://niik.in/998 $page = $this->request->getGet('p'); // กรณีใช้ผ่านตัวแปร $_GET['p'] $data = [ 'provinces' => $pvModel->paginate(5,'',$page), 'pager' => $pvModel->pager, ]; $result = $data['provinces']; echo "<ul>"; if($result){ foreach($result as $row){ echo "<li>{$row['province_name']}</li>"; } } echo "</ul>"; echo $page; } }
กรณีเราใช้รูปแบบที่กำหนดเอง สามารถเลือกสร้างรูปแบบการแบ่งหน้าเองได้ตามต้องการ เช่น อาจจะทำเป็น select option
เมื่อเลือกก็ส่งค่าของหน้าที่ต้องการแสดงมาใช้งาน ดังนั้น ในกรณีกำหนดเอง เราไม่ต้องสร้างรูปแบบการแบ่งหน้าโดยใช้คำสั่ง
$data['pager']->links()
การกำหนด Page โดยใช้ URI Segment
เนื้อหาเกี่ยวกับ URI Segment ได้อธิบายเบื้องต้นเล็กน้อยไปแล้วในบทความเกี่ยวกับการใช้งาน URL และ URI Routing
ทบทวนได้ที่ http://niik.in/996 และเกี่ยวกับการใช้งาน URI Segment เพิ่มเติม จะได้อธิบายในเนื้อหาตอนหน้าอีกที
การใช้ค่าของ URI Segment เพื่อให้เป็นค่าที่กำหนดว่ากำลังแสดงข้อมูล page ใดอยู่ เราสามารถระบุตำแหน่งของ Segment
ที่จะใช้ ร่วมกับการประยุกต์ URI Routing ได้ดังนี้
ให้เรากำหนดรุปแบบ URI หรือ URL ให้กับการแบ่งหน้าใน Routes ที่ต้องการกำหนด ในที่นี้เราจะใช้เป็น
https://www.mysslweb.com/helloworld/1
นั่นคือใช้ Controller class ชื่อ Helloworld และใช้ Method ชื่อ index ใช้ ID เป็นตัวเลขกำหนดหน้า page
ซึ่งปกติถ้าเรียกแบบเต็มๆ ก่อนกำหนด Routes ก็จะเป็น
https://www.mysslweb.com/helloworld/index/16
ดังนั้นเพื่อให้เป็นตามรูปแบบที่ต้องการเราจะกำหนด Routes สำหรับการใช้งานนี้เป็นดังนี้
app/Config/Routes.php
$routes->get('/helloworld/(:num)', 'Helloworld::index/$1');
จาก Routes ที่เรากำหนด จะเห็นค่า segment ของเลข page ที่เราต้องการ จะอยู่ในตำแหน่งที่ 2
ให้กำหนดการใช้งานใน Controller ไฟล์เป็นดังนี้
app/Controllers/Helloworld.php
<?php namespace App\Controllers; use App\Models\ProvincesModel; // ใช้งาน model use CodeIgniter\Controller; // เรียกใช้งาน Controller class class Helloworld extends Controller { public function index($page = 1) { $pvModel = model('ProvincesModel'); $data = [ 'provinces' => $pvModel->paginate(5,'',$page, 2), 'pager' => $pvModel->pager, ]; $result = $data['provinces']; echo "<ul>"; if($result){ foreach($result as $row){ echo "<li>{$row['province_name']}</li>"; } } echo "</ul>"; echo $data['pager']->links(''); } }
ในการใช้งาน paginate() เรากำหนด ตำแหน่ง segment ที่จะใช้เป็นค่า page ตำแหน่งปัจจุบัน ไว้ใน parameter ตำแหน่งที่ 4
ค่านี้จะถูส่งไปใช้ในการสร้างลิ้งค์การแบ่งหน้า และเราใช้ค่าตัวแปร $page ที่ส่งผ่าน ที่เป็น parameter ของ index() method
ในการกำหนดหน้าแสดงข้อมูลปัจจุบัน
ดูตัวอย่างผลลัพธ์เมื่อกดไปหน้าสุดท้าย จะเป็นดังนี้
การใช้งานการแบ่งหน้าผ่าน Pager Library โดยตรง
นอกจากเราจะสามารถใช้งาน pager instance ผ่านการเรียกใช้คำสั่งของ model แล้ว เรายังสามารถเรียกใช้งาน Pager Library
โดยกำหนดการเรียกใช้โดยตรง หรือจะใช้ผ่านคำสั่ง service ก็ได้ ด้วยคำสั่งดังนี้
// $pager = \Config\Services::pager(); // แบบใช้โดยตรง // แบบใช้ service() ฟังก์ชั่น $pager = service('pager');
โดยในการใช้งาน Pager Library โดยตรง เราจะต้องสร้างลิ้งค์การแบ่งหน้าด้วยคำสั่ง ประมาณนี้
$pager->makeLinks($page, $perPage, $total)
นั่นคือเราต้องกำหนดค่าหน้าที่แสดง จำนวนรายการที่จะแสดงในแต่ละหน้า และจำนวนรายการทั้งหมด ผ่านคำสั่ง makeLinks()
โดยรูปแบบการแสดงที่ได้ จะเป็น template default ของ CI เหมือนตัวอย่างด้านบน ซึ่งถ้าเราต้องการกำหนดเพิ่มเติม ก็สามารถ
เพิมเติมใน parameter ตัวที่ 4 ค่า default มีสองค่าคือ 'default_full' และ 'default_simple' รูปแบบที่ได้ก็เหมือนกับการใช้งาน
คำสั่ง links() และ simpleLinks() แต่ถ้าเราใช้ในการกำหนดเองก็จะเป็น
$pager->makeLinks($page, $perPage, $total, 'default_full') // default_full || default_simple
มาดูตัวอย่างการกำหนดแบ่งหน้าแบบกำหนดเอง แบบทั่วไป จะได้เป็นดังนี้
app/Controllers/Helloworld.php
<?php namespace App\Controllers; use App\Models\ProvincesModel; // ใช้งาน model use CodeIgniter\Controller; // เรียกใช้งาน Controller class class Helloworld extends Controller { public function index() { $pvModel = model('ProvincesModel'); $pager = service('pager'); // รับหน้า page จากตัวแปร $_GET['page'] $page = (!empty($this->request->getGet('page')))?$this->request->getGet('page'):1; $perPage = 10; // จำนวนแสดงต่อหน้า $total = $pvModel->countAllResults(); // จำนวนรายการทั้งหมด // http://niik.in/1003 $offset = ($page-1)*$perPage; // คำนวณหา offset $result = $pvModel->findAll($perPage,$offset); // http://niik.in/1004 echo "<ul>"; if($result){ foreach($result as $row){ echo "<li>{$row['province_name']}</li>"; } } echo "</ul>"; echo $pager->makeLinks($page, $perPage, $total); } }
กรณีแสดงหลายรายการแบ่งหน้าข้อมูลในหน้าเดียว เราจะต้องกำหนด path ให้กับ query string ด้วยคำสั่ง
$pager->setPath('helloworld', 'group1');
การกำหนดข้างต้น จะหมายถึงให้ตัวแปรที่ส่งหน้า page ผ่านตัวแปร $_GET['page_group1'] คล้ายตัวอย่างก่อนหน้าที่ใช้งาน
ผ่าน model สิ่งที่ต้องปรับเพิ่มคือ
$page = (!empty($this->request->getGet('page_group')))?$this->request->getGet('page_group'):1;
และการกำหนด setPath() ที่ต้องกำหนดทุกๆ การสร้างการแบ่งหน้าที่ใช้งานดังนี้
$pager->setPath('helloworld', 'group1'); echo $pager->makeLinks($page, $perPage, $total,'default_full', 0,'group1');
จะได้ URL สมมติหน้ารายการสุดท้ายก็จะเป็น
https://www.mysslweb.com/helloworld?page_group1=8
ต่อด้วยตัวอย่างการใช้งานแบบ URI Segment
เงื่อนไขนี้เราจะต้องกำหนดตำแหน่ง segment ที่จะใช้เป็นตัวกำหนดค่า page ปัจจุบันที่จะแสดง เป็น parameter ตำแหน่งที่ 5
ของการใช้งานคำสั่ง makeLinks() ดังนี้
$pager->makeLinks($page, $perPage, $total,'default_full', 2);
จะได้โค้ดตัวอย่างการใช้งานแบบ Segment ดังนี้
app/Controllers/Helloworld.php
<?php namespace App\Controllers; use App\Models\ProvincesModel; // ใช้งาน model use CodeIgniter\Controller; // เรียกใช้งาน Controller class class Helloworld extends Controller { public function index($page =1) { $pvModel = model('ProvincesModel'); $pager = service('pager'); // รับหน้า page จาก Segment ตำแหน่งที่ 2 ผ่านตัวแปร $page $perPage = 10; // จำนวนแสดงต่อหน้า $total = $pvModel->countAllResults(); // จำนวนรายการทั้งหมด // http://niik.in/1003 $offset = ($page-1)*$perPage; // คำนวณหา offset $result = $pvModel->findAll($perPage,$offset); // http://niik.in/1004 echo "<ul>"; if($result){ foreach($result as $row){ echo "<li>{$row['province_name']}</li>"; } } echo "</ul>"; echo $pager->makeLinks($page, $perPage, $total,'default_full', 2); } }
กรณีแสดงหลายรายการแบ่งหน้าข้อมูลในหน้าเดียว ก็สามารถทำได้เหมือนวิธีที่ใช้แบบ query string คือกำหนด setPath()
ให้กับทุกๆ การกำหนดการแบ่งหน้า และอาจจะต้องปรับรูปแบบของ Routes เพิ่มเติมถ้าจำเป็น
การจำกัดเฉพาะ Query String ที่ต้องการ
โดยปกติ ถ้าเราใช้รูปแบบการแบ่งหน้าข้อมูล โดยส่งลิ้งค์ไปกับ Query String หรือ URL แบบ $_GET ยกตัวอย่างเช่น สมมติ
การแสดงจังหวัด เรามีการเพิ่มให้สามารถค้นหาชื่อได้ผ่านตัวแปร $_GET['search'] และเลือกภาคได้ผ่านตัวแปร $_GET['geo']
หากค้นข้อมูล แล้วมีมากกว่า 1 หน้า และหน้าที่ 2 แสดงผ่าน url ประมาณนี้
https://www.mysslweb.com/helloworld?search=data&geo=1&page=2
แต่ถ้าเราต้องการจำกัด ไม่ให้ url แสดง ตัว parameter ที่อยู่ใน query string ทั้งหมด ให้แสดงเฉพาะที่ต้องการ เช่น ไม่ต้อง
แสดงค่า $_GET['geo'] เราก็สามารถกำหนดโดยใช้คำสั่ง only() แล้วระบุ array ชือ query ที่ต้องการ ก็จะได้เป็นดังนี้
$pager->only(['search'])->makeLinks($page, $perPage, $total);
ถึงแม้ url ที่ส่งเข้ามาครั้งแรกจะมี parameter $_GET['geo'] มาด้วย แต่การกำหนด คำสั่ง only() ให้ใช้เฉพราะ serach ค่าเดียว
ก็จะทำให้ URL ในลิ้งค์ที่ใช้แบ่งหน้า เหลือเพียงเฉพาะ query ที่เราต้องการ รวมกับ $_GET['page'] เท่านั้น
https://www.mysslweb.com/helloworld?search=data&page=3
คำสั่งนี้ ใช้ได้กับการกำหนดโดยใช้ model ด้วยคำสั่ง $pager->only(['search'])->links();
กำหนด Template การแบ่งหน้า
ในการใช้งานโครงสร้าง HTML สำหรับการแบ่งหน้า หากเราใช้ค่าที่กำหนดโดย CI แล้ว ก็ไม่จำเป็นต้องปรับอะไร เพียงแค่กำหนด
css class ให้สอดคล้องกับการใช้งาน หรืออาจเรียกใช้ผ่านชื่อ template เริ่มต้น 'default_full' สำหรับรูปแบบเต็ม และ 'default_simple'
สำหรับรูปแบบอย่างง่าย อย่างไรก็ตาม เรายังสามารถกำหนดรูปแบบโครงสร้าง HTML ของการแบ่งหน้าได้เอง โดยการกำหนดการตั้งค่า
template ในไฟล์ Pager.php
เราสามารถกำหนดชื่อ template ตามต้องการ โดยระบุ path ของ Views ที่เราจะใช้เป็นโครงสร้าง HTML ของการแบ่งหน้า
ซึ่งค่าเริ่มต้นจะใช้จาก 'CodeIgniter\Pager\Views\' แต่ถ้าเรากำหนดเอง เราสามารถสร้างไว้ในโฟลเดอร์ app/Views ได้
app/Config/Pager.php
public $templates = [ 'default_full' => 'CodeIgniter\Pager\Views\default_full', 'default_simple' => 'CodeIgniter\Pager\Views\default_simple', 'default_head' => 'CodeIgniter\Pager\Views\default_head', ];
ในการสร้าง template เราอาจจะสร้างชื่อใหม่ หรือจะใช้ชื่อเดิม หากไม่ต้องการไปเปลี่ยนชื่อเวลาใช้งาน ก็สามารถใช้ชื่อ
'default_full' เดิมได้ แล้วเปลี่ยนแค่ path ของไฟล์ Views สมมติเช่น เราจะใช้รูปแบบการแบ่งหน้าด้วย Bootstrap framework
ก็อาจจะต้องชื่อเป็น bootstrap_full ก็ได้ จะได้เป็น
'default_full' => 'App\Views\Pagers\bootstrap_full',
เราไม่จำเป็นต้องกำหนด namespace สำหรับโฟลเดอร์มาตรฐานที่ไว้เก็บไฟล์ Views ที่ app/Views โดยกำหนด
แค่เพียงส่วนเพิ่มเติมแทนได้ดังนี้
'default_full' => 'Pagers\bootstrap_full',
แต่กรณีถ้าเรากำหนดเป็นชื่อ template ใหม่เพิ่มขึ้นมา และต้องการเรียกใช้งาน เช่น สมมติใช้ bulma framework
public $templates = [ 'default_full' => 'Pagers\bootstrap_full', 'default_simple' => 'CodeIgniter\Pager\Views\default_simple', 'default_head' => 'CodeIgniter\Pager\Views\default_head', 'bulma_full' => 'Pagers\bulma_full', ];
เวลาใช้งาน เราจะต้องไปกำหนดชื่อในขั้นตอนการเรียกใช้งานด้วย โดยคำสั่ง links() และ simpleLinks() กำหนดชื่อ template
ไว้ใน parameter ตัวที่ 2 และคำสั่ง makeLinks() กำหนดไว้ใน parameter ตัวที่ 4
<?= $pager->links('', 'bulma_full') ?> <?= $pager->simpleLinks('group2', 'bulma_full') ?> <?= $pager->makeLinks($page, $perPage, $total, 'bulma_full') ?>
การสร้าง Views สำหรับ Pagination
หลังจากเรากำหนดชื่อ template และ path สำหรับ Views เรียบร้อยแล้ว ขั้นตอนต่อไปก็คือการสร้าง Views สำหรับใช้เป็น
โครงสร้าง HTML ของ Pagination หรือการแบ่งหน้ารายการข้อมูล ซึ่งในที่นี้จะใช้ bootstrap ให้เราสร้างไฟล์ bootstrap_full.php
ตามโครงสร้างตัวอย่างดังนี้
app/Views/Pagers/bootstrap_full.php
<?php $pager->setSurroundCount(2) ?> <nav aria-label="Page navigation"> <ul class="pagination"> <?php if ($pager->hasPrevious()) : ?> <li> <a href="<?= $pager->getFirst() ?>" aria-label="<?= lang('Pager.first') ?>"> <span aria-hidden="true"><?= lang('Pager.first') ?></span> </a> </li> <li> <a href="<?= $pager->getPrevious() ?>" aria-label="<?= lang('Pager.previous') ?>"> <span aria-hidden="true"><?= lang('Pager.previous') ?></span> </a> </li> <?php endif ?> <?php foreach ($pager->links() as $link) : ?> <li <?= $link['active'] ? 'class="active"' : '' ?>> <a href="<?= $link['uri'] ?>"> <?= $link['title'] ?> </a> </li> <?php endforeach ?> <?php if ($pager->hasNext()) : ?> <li> <a href="<?= $pager->getNext() ?>" aria-label="<?= lang('Pager.next') ?>"> <span aria-hidden="true"><?= lang('Pager.next') ?></span> </a> </li> <li> <a href="<?= $pager->getLast() ?>" aria-label="<?= lang('Pager.last') ?>"> <span aria-hidden="true"><?= lang('Pager.last') ?></span> </a> </li> <?php endif ?> </ul> </nav>
โค้ดข้างต้น เราใช้รูปแบบเริ่มต้นจาก default_full หรือ template หลักมาก่อน แล้วค่อยปรับแก้ css class ใหม่ตามต้องกับ โดย
ใช้รูปแบบของ bootstrap framework
แต่ก่อนอื่นๆ มาทำความเข้าใจ คำสั่งต่างๆ ของ pager ว่ามีค่าอะไรบ้าง และเราจะต้องกำหนดอย่างไร
setSurroundCount()
เป็นการกำหนดจำนวนลิ้งค์ที่ต้องการให้แสดงข้างลิ้งค์หน้าปัจจุบัน โดยกำหนดค่าเป็นตัวเลขตามต้องการ ข้างต้นกำหนดเป็น 2
หมายความว่า ข้างๆ ลิ้งค์หน้าปัจจุบัน สมมติอยู่หน้าที่ 1 จะมีลิ้งค์เลข 2 และ 3 นับได้ เท่ากับ 2 แต่สมมติเรากำหนดเป็น 4 ก็จะมี
ลิ้งค์เลข 2 3 4 และ 5 นับเป็น 4 รายการอยู่ข้างๆ ประมาณนี้เป็นต้น
hasPrevious() และ hasNext()
เป็นคำสั่งเงื่อนไขใช้ตรวจสอบว่า ณ หนัาปัจจุบันที่แสดง มีหน้าอื่นอยู่ก่อนหน้า และมีหน้าอื่นอยู่ถัดไป อีกหรือไม่ ตามลำดับ
ยกตัวอย่าง สมมติมีจำนวนหน้าทั้งหมด 20 หน้า และเรากำหนดให้แสดงลิ้งค์รอบข้างไว้ที่ 2 จากคำสั่ง setSurroundCount(2)
ถ้าตอนนี้เราอยู่ที่หน้า 3 นั่นแสดงว่าตัวเลขหน้าจะแสดงเป็น 1 2 3 4 และ 5 และจากการที่ลิ้งค์แรกเป็นเลข 1 คำสั่ง hasPrevious()
จึงคืนค่าเป็น false เพราะไม่มีหน้าก่อนนี้แล้ว ส่วนในด้านคำสั่ง hasNext() จะคืนค่าเป็น true เพราะถัดจากหน้า 5 ก็ยังมีอีก 15
หน้าที่เหลือนั่นเอง
getPrevious() และ getNext()
เป็นคำสั่งสำหรับแสดง url ของหน้าก่อนหน้า และหน้าถัดไปในลิ้งค์แบ่งหน้า
getFirst() และ getLast()
เป็นคำสั่งสำหรับแสดง url ของหน้าแรก และหน้าสุดท้ายในลิ้งค์แบ่งหน้า
links()
เป็น array ข้อมูลลิ้งค์เลขหน้าที่แสดง สามารถทำการวนลูปแสดงค่าต่างๆ ซี่งประกอบด้วยค่าดังนี้
$link = [ 'active' => false, 'uri' => 'https://www.mysslweb.com/helloworld?page=2', 'title' => 1 ];
ค่า key 'active' คือค่าที่บอกว่าหน้าที่กำลังแสดงอยู่เป็นหน้าปัจจุบันหรือไม่
ค่า key 'url' ใช้สำหรับกำหนด url ของลิ้งค์เลขหน้าที่แสดง
ค่า key 'title' เป็นค่าตัวเลข ระบุเลขหน้าของการแบ่งหน้าข้อมูล
ในตอนต้นของบทความ เราได้ให้ข้อสังเกต เกี่ยวกับค่า previous และ next ไปแล้วว่า จะไม่ใช่ค่าของล้ิงค์ก่อนหน้า
และลิ้งค์ถัดไปของหน้าปัจจุจบัน แต่จะเป็นลิ้งค์ก่อนหน้าของเลขลำดับแรก และเป็นลิ้งค์ถัดไปของเลชลำดับสุดท้ายที่แสดง
แต่ถ้าเราต้องการให้ข้อความ previous และ next เป็นการลิ้งค์ไปยังรายการก่อน และหลังของรายการหน้าปัจจุบัน เราก็
สามารถปรับแต่งในส่วนนี้ได้ โดยเปลี่ยนมาใช้ค่าเหล่านี้แทน
hasPreviousPage() และ hasNextPage()
getPreviousPage() และ getNextPage()
จะได้เป็นดังนี้
<nav aria-label="<?= lang('Pager.pageNavigation') ?>"> <ul class="pagination"> <?php if ($pager->hasPreviousPage()) : ?> <li> <a href="<?= $pager->getFirst() ?>" aria-label="<?= lang('Pager.first') ?>"> <span aria-hidden="true"><?= lang('Pager.first') ?></span> </a> </li> <li> <a 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 <?= $link['active'] ? 'class="active"' : '' ?>> <a href="<?= $link['uri'] ?>"> <?= $link['title'] ?> </a> </li> <?php endforeach ?> <?php if ($pager->hasNextPage()) : ?> <li> <a href="<?= $pager->getNextPage() ?>" aria-label="<?= lang('Pager.next') ?>"> <span aria-hidden="true"><?= lang('Pager.next') ?></span> </a> </li> <li> <a href="<?= $pager->getLast() ?>" aria-label="<?= lang('Pager.last') ?>"> <span aria-hidden="true"><?= lang('Pager.last') ?></span> </a> </li> <?php endif ?> </ul> </nav>
hasPreviousPage() และ hasNextPage()
คำสั่งข้างต้น จะให้ความหมายเงื่อนไขที่แตกต่างไป คือ ถ้าหน้าปัจจุบันไม่ใช้หน้าที่ 1 แสดงว่า hasPreviousPage() ก็จะคืนค่า
เป็น true เพราะยังมีหน้าที่ 1 หรือมีหน้าก่อนหน้าอยู่ ในขณะที่คำสั่ง hasNextPage() คือ ถ้าหน้าปัจจุบันไม่ใช้หน้าสุดท้าย คำสั่ง
hasNextPage() ก็จะคืนค่าเป็น true เพราะยังมีหน้าสุดท้ายอยู่ นั่นเอง
getPreviousPage() และ getNextPage()
จะคืนค่าเป็น url ของหน้าก่อน หน้าปัจจุบันถ้ามี หรือถัดจากหน้าปัจจุบันถ้ามี
ลองกำหนด css class ของ bootstrap ตามลิ้งค์นี้ https://getbootstrap.com/docs/4.5/components/pagination/
จะได้เป็น
app/Views/Pagers/bootstrap_full.php
<?php $pager->setSurroundCount(2) ?> <nav aria-label="<?= lang('Pager.pageNavigation') ?>"> <ul class="pagination"> <?php if ($pager->hasPreviousPage()) : ?> <li class="page-item"> <a class="page-link" href="<?= $pager->getFirst() ?>" aria-label="<?= lang('Pager.first') ?>"> <span aria-hidden="true"><?= lang('Pager.first') ?></span> </a> </li> <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 <?= $link['active'] ? 'class="page-item 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> <li class="page-item"> <a class="page-link" href="<?= $pager->getLast() ?>" aria-label="<?= lang('Pager.last') ?>"> <span aria-hidden="true"><?= lang('Pager.last') ?></span> </a> </li> <?php endif ?> </ul> </nav>
ทดสอบเรียกใช้งานโดยเรียก bootstrap css stylesheet มาใช้งาน จะได้ผลลัพธ์ประมาณนี้
จะเห็นการใช้งาน Pager Library หรือการแบ่งหน้ารายการข้อมูลใน CI4 ค่อนข้างจัดการง่ายและสะดวกขึ้น
หวังว่าเนื้อหานี้จะเป็นประโยชน์เป็นแนวทางนำไปปรับใช้งานต่อไปได้