Laravel LengthAwarePagination - php

I am working with laravel's LengthAwarePaginator Class. My problem is that I cannot use
$users = DB::table('users')->paginate(15);
or in other words,larvel's querybuilder or eloquent results. The reason is I have provided user with a frontend where they can create queries dynamically that can as simple as a select query and can be as complicated as queries with multiple joins etc. So I am left with only option of using LengthAwarePaginator. Here is what I have done do far
private function queryWithPagination($queryString,$path,$fieldName = '',$sortOrder = '',$perPage = 100){
$queryString = str_replace(''',"'",$queryString);
$queryString = str_replace('**',"",$queryString);
if(!empty($fieldName) && !empty($sortOrder)){
$queryString .= " ORDER BY '{$fieldName}' {$sortOrder}";
}
$currentPage = LengthAwarePaginator::resolveCurrentPage();
$limit = $perPage +1; // to have pagination links clickable i.e next,previous buttons
$queryString.= " LIMIT {$limit}";
if($currentPage == 1){
$queryString.= " OFFSET 0";
}
else{
$offset = ($currentPage-1)*$perPage;
$queryString.= " OFFSET {$offset}";
}
$path = preg_replace('/&page(=[^&]*)?|^page(=[^&]*)?&?/','', $path);
$result = DB::connection('database')->select($queryString);
$collection = new Collection($result);
//$currentPageSearchResults = $collection->slice(($currentPage - 1) * $perPage, $perPage)->all();
$entries = new LengthAwarePaginator($collection, count($collection), $perPage);
$entries->setPath($path);
//dd($queryString,$result);
dd($entries);
return $entries;
}
As you can see I append LIMIT and OFFSET in the query otherwise, for complex queries the load time was getting greater resulting in bad user experience. The problem with current set up is that the last page is always set to 2 and when I get to second page I cannot browse more results i.e cases where records returned were more than 500 I can still only browse upto page 2. How do I fix this issue where by using limit offset I can still keep browsing all the results until I get to the last page and then disable the pagination links?

made the second parameter to LengthAwarePaginator dynamic as follows in the given code,it solved my problem
$entries = new LengthAwarePaginator($collection, (count($collection)>=101)?$currentPage*$limit:count($collection), $perPage);

Related

Apply pagination to MOODLE website

We are trying to apply pagination concept on Course Category list page in our MOODLE website and we have got success in it. But while displaying it shows the same course on every page. We are able to set how much number of topics/categories should be shown on each page, and same number of categories are shown. But each page shows the same topic. Please help if somebody has applied pagination into their MOODLE website.
There is no generic way to add pagination to different pages in Moodle - there is a general '$OUTPUT->paging_bar' function, which will generate the output for a paging bar, but it is then up to your own code to decide what to do with the 'page' parameter that is then passed on to the PHP script.
Usually the code looks something like this:
$page = optional_param('page', 0, PARAM_INT);
$perpage = optional_param('perpage', 30, PARAM_INT);
...
$count = $DB->count_records_sql('Some SQL query to count the number of results');
$start = $page * $perpage;
if ($start > $count) {
$page = 0;
$start = 0;
}
$results = $DB->get_records_sql('Some SQL query to get the results', array(parameters for query), $start, $perpage); // Start at result '$start' and return '$perpage' results.
Alternatively, if this is not possible, you can get all the results and then use array_slice:
$page = optional_param('page', 0, PARAM_INT);
$perpage = optional_param('perpage', 30, PARAM_INT);
...
$results = $DB->get_records_sql('Some SQL query to get the results', array(params for query));
$start = $page * $perpage;
if ($start > count($results)) {
$page = 0;
$start = 0;
}
$results = array_slice($results, $start, $perpage, true);
The list of courses within a category are already paginated.
So I'm guessing you mean the list of categories? You can use a flexible table with pagination
http://docs.moodle.org/dev/lib/tablelib.php
You can also display one section of a course by going to course -> edit settings -> Course format -> Course Layout -> show one section per page.
But if you want to display some of the sections - not just one - then you probably need to have a look at designing a course format
http://docs.moodle.org/dev/Course_formats

How do I get the first and last results from a query?

I am creating a pagination script and I need to get the first and last results in the database query so that I can determine what results appear when the user clicks a page to go to. This is the code that I have at the minute:
// my database connection is opened
// this gets all of the entries in the database
$q = mysql_query("SELECT * FROM my_table ORDER BY id ASC");
$count = mysql_num_rows($q);
// this is how many results I want to display
$max = 2;
// this determines how many pages there will be
$pages = round($count/$max,0);
// this is where I think my script goes wrong
// I want to get the last result of the first page
// or the first result of the previous page
// so the query can start where the last query left off
// I've tried a few different things to get this script to work
// but I think that I need to get the first or last result of the previous page
// but I don't know how to.
$get = $_GET['p'];
$pn = $_GET['pn'];
$pq = mysql_query("SELECT * FROM my_table ORDER BY id ASC LIMIT $max OFFSET $get");
// my query results appear
if(!$pn) {
$pn = 1;
}
echo "</table><br />
Page $pn of $pages<br />";
for($p = 1;$p<=$pages;$p++) {
echo "<a href='javascript:void(0);' onclick='nextPage($max, $p);' title='Page $p'>Page $p</a> ";
}
I think you have few problems there, but I try to tackle them for you. First, as comments say above, you are using code that it vulnerable to SQL injection. Take care of that - you might want to use PDO, which is as easy use as MySQL extension, and will save you from many trouble (like injection).
But to your code, lets go through it:
You should ask DB to get count of the rows, not using mysql function, it's far more effective, so use SELECT count(*) FROM mytable.
For $pages use ceil() as you want all rows to be printed, if you have $max 5 and have 11 rows, round will make $pages 2, where you actually want 3 (last page just contains that last 11th row)
in LIMIT you want to LIMIT row_count OFFSET offset. You can calculate offset from page number, so: $max = row_count but $offset = ($max * $page) - $max. In your code if $get is directly the page, it means you get $get'th row (Not sure though what happens in your JS nextpage. Bare in mind that not all use JavaScript.)
I have prepared simple example here which uses PDO, maybe that gives you idea how simple it's use PDO.
The selecting rows shows example how to put parameters in SQL, it would be perfectly safe in this case state, 'SELECT * FROM pseudorows LIMIT '.$start.','.$max by I wanted to make an example how easy it is (and then safe):
// DB config
$DB_NAME = 'test';
$DB_USER = 'test';
$DB_PASSWD = 'test';
// make connection
try {
$DB_CONN = new PDO("mysql:host=localhost;dbname=".$DB_NAME, $DB_USER, $DB_PASSWD);
$DB_CONN->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die($e);
}
// lets say user param 'p' is page, we cast it int, just to be safe
$page = (int) (isset($_GET['p'])?$_GET['p']:1);
// max rows in page
$max = 20;
// first select count of all rows in the table
$stmt = $DB_CONN->prepare('SELECT count(*) FROM pseudorows');
$stmt->execute();
if($value = $stmt->fetch()) {
// now we know how many pages we must print in pagination
// it's $value/$max = pages
$pages = ceil($value[0]/$max);
// now let's print this page results, we are on $page page
// we start from position max_rows_in_page * page_we_are_in - max_rows_in_page
// (as first page is 1 not 0, and rows in DB start from 0 when LIMITing)
$start = ($page * $max) - $max;
$stmt = $DB_CONN->prepare('SELECT * FROM pseudorows LIMIT :start,:max');
$stmt->bindParam(':start',$start,PDO::PARAM_INT);
$stmt->bindParam(':max', $max,PDO::PARAM_INT);
$stmt->execute();
// simply just print rows
echo '<table>';
while($row = $stmt->fetch()) {
echo '<tr><td>#'.$row['id'].'</td><td>'.$row['title'].'</td></tr>';
}
echo '</table>';
// let's show pagination
for($i=1;$i<=$pages;$i++) {
echo '[ '.$i.' ]';
}
}
mysql_fetch_array returns an associative array
Which means you can use reset and end to get the first and last results:
$pqa = mysql_fetch_array($pq);
$first = reset($pqa);
$last = end($pqa);
I don't see how you plan to use the actual results, just page numbers should be sufficient for pagination.
Still, hope it helps. And yes, upgrade to mysqli, so your code doesn't get obsolete.

Modifing Pagination-Sorting Method to Change Rows Per Page

Hi i have a PHP based Pagination and Sorting Method which works fine.
I am able to sort and paginate according to any categories, but having problems when it comes to rows per page.
i have used GET Method to fetch information for pagination and sorting as
if (isset($_GET['rpp'])&& is_numeric($_GET['rpp'])) {
$rowsperpage = mysql_real_escape_string($_GET['rpp']);
}else{
$rowsperpage='5';
}
if (isset($_GET['page']) && is_numeric($_GET['page'])) {
$currentpage = (int) mysql_real_escape_string($_GET['page']);
} else {
$currentpage = 1;
}
if (isset($_GET['order']) && in_array($_GET['order'], $order)) {
$orderby = mysql_real_escape_string($_GET['order']);
}else{
$orderby='id';
}
$sortby = '';
if (isset($_GET['sort'])) {
$sortby = mysql_real_escape_string($_GET['sort']);
}else{
$sortby='desc';
}
for pagination i have used following type of url string
echo " <li><a href='$pagename?order=$orderby&sort=$sortby&rpp=$rowsperpage&page=$nextpage'>Next»»</a>
and my mysql query is
$sql2 = "SELECT * FROM internet_security ORDER BY $orderby $sortby LIMIT $rowsperpage OFFSET $offset";
and in html i have used links to sort categories like
id-desc:
id-asc:
title-desc:
title-asc:
**limit rows by**
5
10
20
My question is
every thing is working fine except when i limit row per page via limit rows by - rows get limited to whatever limit(5,10,20) is selected but then if i click pagination every thing goes to its default set value i.e by id and in DESC order with LIMIT of 5.
and if i do it like
id-desc:
id-desc:
id-desc:
then it works but then i must have at least 12 links for full functionality.which is not a standard approach.
what i want is once a limit is set either by default or by sorting options, i shall be able to sort by id title etc.. and paginatin should works along with it.
i hope i made it clear.
please see what i am doing wrong and suggest any possible solution to my approach.
(Upgrading to an answer)
Store $rpp in a $_SESSION variable, updating only if it's present in $_GET:
if ( isset($_GET[ 'rpp'])) $_SESSION['rpp'] = intval($_GET['rpp']);
elseif (!isset($_SESSION['rpp'])) $_SESSION['rpp'] = 5;
Then use $_SESSION['rpp'] in your query:
$sql2 = "
SELECT *
FROM internet_security
ORDER BY $orderby $sortby
LIMIT $_SESSION[rpp] OFFSET $offset
";

MySQL PHP Pagination

Is it possible to create pagination without getting all elements of table?
But with pages in GET like /1 /666…
It usually involves issuing two queries: one to get your "slice" of the result set, and one to get the total number of records. From there, you can work out how many pages you have and build pagination accordingly.
A simply example:
<?php
$where = ""; // your WHERE clause would go in here
$batch = 10; // how many results to show at any one time
$page = (intval($_GET['page']) > 0) ? intval($_GET['page']) : 1;
$start = $page-1/$batch;
$pages = ceil($total/$batch);
$sql = "SELECT COUNT(*) AS total FROM tbl $where";
$res = mysql_query($sql);
$row = mysql_fetch_assoc($res);
$total = $row['total'];
// start pagination
$paging = '<p class="paging">Pages:';
for ($i=1; $i <= $pages; $i++) {
if ($i==$page) {
$paging.= sprintf(' <span class="current">%d</a>', $i);
} else {
$paging.= sprintf(' %1$d', $i);
}
}
$paging.= sprintf' (%d total; showing %d to %d)', $total, $start+1, min($total, $start+$batch));
And then to see your pagination links:
...
// loop over result set here
// render pagination links
echo $paging;
I hope this helps.
Yes, using mySQL's LIMIT clause. Most pagination tutorials make good examples of how to use it.
See these questions for further links and information:
How do you implement pagination in PHP?
Searching for advanced php/mysql pagination script
more results
You can use LIMIT to paginate over your result set.
SELECT * FROM comments WHERE post_id = 1 LIMIT 5, 10
where LIMIT 5 means 5 comments and 10 is the offset. You can also use the longer syntax:
... LIMIT 5 OFFSET 10

Paginator Class

everyone. I am working on a site with smarty templates using php and a mysql database.
This is a more specific question than my first one which asked how to pass methods to a class. I thought it would be easier to repackage the question than edit the old one.
I have written a paginator script for my image gallery which displays images on the page. If a user has selected a category then only images in a particular category are shown and the results are always paginated.
The script is shown below.
$page_num = (isset($_GET['page_num']))?$_GET['page_num']:1; //if $_GET['page_num'] is set then assign to var $page_num. Otherwise set $page_num = 1 for first page
$category = (isset($_GET['req1']))?$_GET['req1']:'null'; //if $_GET['req1'] is set assign to $category other set $category to null
$items_pp = 5;
$total = $db->num_images_gallery($category); //returns the number of records in total('null') or in a particular category('category_name')
$pages_required = ceil($total/$items_pp); //total records / records to display per page rounded up
if($page_num > $pages_required){//in case the current page number is greater that the pages required then set it to the amount of pages required
$page_num = $pages_required;
}
if($page_num < 1){//in case the current page num is set to less that one set it back to 1
$page_num = 1;
}
$limit = "LIMIT " .($page_num - 1)*$items_pp . "," . $items_pp . ""; //if 5 results pre page and we on page 3 then LIMIT 10,5 that is record 10,11,12,13 and 14
$result = $db->get_images_gallery($category,$limit);
$i = 0;
while($row = $result->fetch_assoc()){
$images[$i]['file'] =$row['file'];
$images[$i]['file_thumb'] = str_replace('.','_thumbnail.',$row['file']);//show the thumbnail version of the image on the page
$images[$i]['title'] = $row['title'];
$images[$i]['description'] = $row['description'];
$i++;
}
if(!empty($images)){
$smarty->assign('images',$images);}else{
$smarty->assign('message',"There are no images to display in the ".ucwords(str_replace('_',' ',$category))." category");}
if($total > 0 && $pages_required >= 1){//only display this navigation if there are images to display and more than one page
$smarty->assign('page_scroll',$page_num . ' of ' . $pages_required);
$page_scroll_first = "<a href='".$_SERVER['REDIRECT_URL'] . "?page_num=1"."' >FIRST</a> <a href='".$_SERVER['REDIRECT_URL'] . "?page_num=" . ($page_num-1)."' ><<PREVIOUS</a>";
$page_scroll_last = " <a href='".$_SERVER['REDIRECT_URL'] . "?page_num=". ($page_num+1) . "'>NEXT>></a> <a href='" . $_SERVER['REDIRECT_URL'] . "?page_num=".$pages_required."'>LAST</a>";
if($page_num == 1){$page_scroll_first = "FIRST <<PREVIOUS";}
if($page_num == $pages_required){$page_scroll_last = "NEXT>> LAST";}
$smarty->assign('page_scroll_first',$page_scroll_first);//just use if statements to set the values for page scroll first and page scroll last and then assign them here
$smarty->assign('page_scroll_last',$page_scroll_last);
$smarty->assign('page_num',$page_num);
}
The script calls on two methods from my database class:
$db->num_images_gallery which looks like this:
function num_images_gallery($cat='null'){
$query = ($cat == 'null')?
"SELECT COUNT(*) AS images FROM images
LEFT JOIN image_categories ON (images.image_categories_id = image_categories.id)
WHERE images.gallery='1' AND image_categories.gallery = '1'"//no images should be shown in a category which is not intended to be shown at all
:
"SELECT COUNT(*) AS images FROM images
LEFT JOIN image_categories ON (images.image_categories_id = image_categories.id)
WHERE category = '{$cat}'
AND images.gallery='1' AND image_categories.gallery = '1'";
$result = $this->connection->query('SELECT COUNT(*) AS images FROM (?)',$x);
$row = $result->fetch_assoc();
$row_count = $row['images'];
echo $row_count;
return $row_count;
}
and the method $db::get_images_gallery() which looks like this:
function get_images_gallery($category,$limit){
$query = ($category=='null')?
"SELECT `file`,title,images.description,sizes,images.gallery,category FROM images
LEFT JOIN image_categories ON (images.image_categories_id = image_categories.id) WHERE images.gallery='1' AND image_categories.gallery = '1' {$limit}"
:
"SELECT `file`,title,images.description,sizes,images.gallery,category FROM images
LEFT JOIN image_categories ON (images.image_categories_id = image_categories.id)
WHERE category = '{$category}' AND images.gallery='1' AND image_categories.gallery = '1' {$limit}";
$result = $this->connection->query($query);
return $result;
}
I now want to create a class called paginate and put this script in it so i can display my site products paginated.
The main problem is that i need to use different functions to get the num of prodducts in my product table and then return the paginated results. How do i turn the script above into a class where i can change the functions which are used. I almost got an answer on my previous question, but the question was not specific enough.
Thanks
andrew
There's a Smarty Add-On for pagination.
You can find it here: http://www.phpinsider.com/php/code/SmartyPaginate/
For a quick example, extracted from the linked page:
index.php
session_start();
require('Smarty.class.php');
require('SmartyPaginate.class.php');
$smarty =& new Smarty;
// required connect
SmartyPaginate::connect();
// set items per page
SmartyPaginate::setLimit(25);
// assign your db results to the template
$smarty->assign('results', get_db_results());
// assign {$paginate} var
SmartyPaginate::assign($smarty);
// display results
$smarty->display('index.tpl');
function get_db_results() {
// normally you would have an SQL query here,
// for this example we fabricate a 100 item array
// (emulating a table with 100 records)
// and slice out our pagination range
// (emulating a LIMIT X,Y MySQL clause)
$_data = range(1,100);
SmartyPaginate::setTotal(count($_data));
return array_slice($_data, SmartyPaginate::getCurrentIndex(),
SmartyPaginate::getLimit());
}
index.tpl
{* display pagination header *}
Items {$paginate.first}-{$paginate.last} out of {$paginate.total} displayed.
{* display results *}
{section name=res loop=$results}
{$results[res]}
{/section}
{* display pagination info *}
{paginate_prev} {paginate_middle} {paginate_next}
Regarding your question about mixing the DB class and the Paginator class, it's all ok:
Your DB class will handle fetching data from DB
The SmartyPaginate class will handle the pagination
And your index.php just make the calls to each one where appropriate to set things out.
The idea is to keep responsibilities isolated.
Your DB class won't handle pagination, nor will your pagination class contain DB code.
From your other question, I think you were trying to do something too much convoluted for the problem at hand.
I'd suggest you to move all code that is DB-related inside your DB handling class and outside your index.php
This, for example:
$limit = "LIMIT " .($page_num - 1)*$items_pp . "," . $items_pp . ""; //if 5 results pre page and we on page 3 then LIMIT 10,5 that is record 10,11,12,13 and 14
This is DB logic, it generates (part of) an SQL string, so move it around.
It depends on 2 parameters, so find a way to get them available.
In this case, I'd suggest just passing both as parameters.
Instead of:
$result = $db->get_images_gallery($category,$limit);
Use:
$result = $db->get_images_gallery($category,$no_items, $page);
Also, your pager navigation rule should be inside your paginator class..
if($total > 0 && $pages_required >= 1){//only display this navigation if there are images to display and more than one page
$smarty->assign('page_scroll',$page_num . ' of ' . $pages_required);
$page_scroll_first = "<a href='".$_SERVER['REDIRECT_URL'] . "?page_num=1"."' >FIRST</a> <a href='".$_SERVER['REDIRECT_URL'] . "?page_num=" . ($page_num-1)."' ><<PREVIOUS</a>";
$page_scroll_last = " <a href='".$_SERVER['REDIRECT_URL'] . "?page_num=". ($page_num+1) . "'>NEXT>></a> <a href='" . $_SERVER['REDIRECT_URL'] . "?page_num=".$pages_required."'>LAST</a>";
if($page_num == 1){$page_scroll_first = "FIRST <<PREVIOUS";}
if($page_num == $pages_required){$page_scroll_last = "NEXT>> LAST";}
$smarty->assign('page_scroll_first',$page_scroll_first);//just use if statements to set the values for page scroll first and page scroll last and then assign them here
$smarty->assign('page_scroll_last',$page_scroll_last);
$smarty->assign('page_num',$page_num);
}
In this case, I hope the Add-On will handle it automatically for you.
You could then move this whole block, which does all your logic for fetching and preparing images data to a function (inside your ImageGalery class if you have one)
$total = $db->num_images_gallery($category); //returns the number of records in total('null') or in a particular category('category_name')
$pages_required = ceil($total/$items_pp); //total records / records to display per page rounded up
if($page_num > $pages_required){//in case the current page number is greater that the pages required then set it to the amount of pages required
$page_num = $pages_required;
}
if($page_num < 1){//in case the current page num is set to less that one set it back to 1
$page_num = 1;
}
$limit = "LIMIT " .($page_num - 1)*$items_pp . "," . $items_pp . ""; //if 5 results pre page and we on page 3 then LIMIT 10,5 that is record 10,11,12,13 and 14
$result = $db->get_images_gallery($category,$limit);
$i = 0;
while($row = $result->fetch_assoc()){
$images[$i]['file'] =$row['file'];
$images[$i]['file_thumb'] = str_replace('.','_thumbnail.',$row['file']);//show the thumbnail version of the image on the page
$images[$i]['title'] = $row['title'];
$images[$i]['description'] = $row['description'];
$i++;
}
Finally, on your index.php, all you have to do is:
Validate the parameters you received
Call your ImageGalery class to fetch the galery data (pass the parameters it needs)
Call your Pagination class to do the pagination (setting up navigation links, etc)
Set the Smarty template variables you need
And display it.
There is still lots of room for improvement, but I hope those few steps will help get your Image Galery code more clear.

Categories