Pagination in MongoDB database for PHP language - php

I have the following code for the pagination(PHP) in MongoDB database.
<?php
$mongodb = new Mongo("vvv");
$database = fff
$collection = gggg
$page = isset($_GET['page']) ? (int) $_GET['page'] : 1;
$limit = 12;
$skip = ($page - 1) * $limit;
$next = ($page + 1);
$prev = ($page - 1);
$sort = array('createdAt' => -1);
$cursor = $collection->find()->skip($skip)->limit($limit)->sort($sort);
foreach ($cursor as $r) {
--------
}
$total= $cursor->count();
if($page > 1){
echo 'Previous';
if($page * $limit < $total) {
echo ' Next';
}
} else {
if($page * $limit < $total) {
echo ' Next';
}
}
$mongodb->close();
?>
BUT my database size is 30GB+, each search provides the results of 20,000 which takes HUGE TIME to count() //$total= $cursor->count();
Can any one provide any PHP pagination code for MongoDB which does not count the total number of results but do the pagination?

cursor.skip() requires the server to walk from the beginning of the collection or index to get the offset or skip position before beginning to return results. It is not recommended to use it in pagination against 30GB+ data in your case.
If you cannot narrow a little bit your condition in find(), you can consider to add a sequence number in your docs for pagination purpose, assuming that you will rarely remove/update those documents:
{
createdAt: "2015-01-01",
...
seq_no: 1
},
{
createdAt: "2015-01-02",
...
seq_no: 2
}
Then you can implement the pagination query like this (remember to create the index on seq_no, of course):
$cursor = $collection->find({"seq_no": {"$gt":$skip, "$lte":($skip+$limit)}})

Related

How to correctly make pagination with PHP and MongoDB

I'm new to Mongodb and php.
I have a problem setting up pagination
This is the code that I'm using:
<?php
require 'vendor/autoload.php';
$mongodb = new MongoDB\Client;
$database = $mongodb->database_test;
$collection = $database->table_test2;
$searchterm = "4x";
$page = isset($_GET['page']) ? (int) $_GET['page'] : 1;
$limit = 20;
$skip = ($page - 1) * $limit;
$next = ($page + 1);
$prev = ($page - 1);
$sort = array('url' => -1);
$cursor = $collection->find(
['page_title' => new MongoDB\BSON\Regex($searchterm, 'i'),],
[
'limit' => $limit,
'skip' => $skip,
'sort' => $sort
]);
echo "<html dir='rtl'>";
foreach ($cursor as $r) {
echo sprintf('<p>Page Title is: %s.<br> page URL is: %s. </p><br>', $r['page_title'], $r['url']);
}
// second part
$total= $cursor->count();
if($page > 1){
echo 'Previous';
if($page * $limit < $total) {
echo ' Next';
}
} else {
if($page * $limit < $total) {
echo ' Next';
}
}
$mongodb->close();
?>
The first part of the code apparently works fine, I get the results corectly.
the second part of the code
$total= $cursor->count();
if($page > 1){
echo 'Previous';
if($page * $limit < $total) {
echo ' Next';
}
} else {
if($page * $limit < $total) {
echo ' Next';
}
}
$mongodb->close();
has a issue were I don't get the next/previous links but if I manually change the URL and add page=2 I can see results but no navigation links.
if I comment-out:
$total= $cursor->count();
I'm able to get the "Previous" navigation link but not the "next" navigation link.
I'll appreciate any help and advise to correct this code so both the "next" and the "previous" navigation links will appear.
and also if possible, how to show the number of the results found.

How to paginate the json data using PHP

I need to paginate some JSON result using PHP but as per my code its not working as expected. I am explaining my code below.
<?php
$arr='[{"sku":"Pizza001","position":0,"category_id":"67"},{"sku":"Birthday Cake","position":0,"category_id":"67"},{"sku":"Birthday Cake-1","position":0,"category_id":"67"},{"sku":"Fruit cake","position":0,"category_id":"67"},{"sku":"Birthday Cake for Gift","position":0,"category_id":"67"}]';
$raw_data = json_decode($arr, true);
paginate($raw_data,1);
function paginate($data, $page = 2, $perPage = 2) {
$x = ($page - 1) * $perPage;
$z = $page * $perPage;
$y = ($z > count($data)) ? count($data) : $z;
for(; $x < $y; $x++) {
print_r($data[$x]);
}
}
?>
Here I am getting always first 2 set o data from JSON object. I need as per $page and $perPage the data will be filtered. Suppose $page=1 and $perPage=2 then the first 2 set of record will be fetched and if $page=2 and $perPage=2 then next 2 set(i.e-from 2nd index) of data will be fetched and so on. Please help me to resolve this issue.
I have used a simple logic using array_slice as suggested by Wenchoeng Young Here you go:
$i=0;
$page=5;
$perPage=1;
$arr='[
{"sku":"Pizza001","position":0,"category_id":"67"},
{"sku":"Birthday Cake","position":0,"category_id":"67"},
{"sku":"Birthday Cake-1","position":0,"category_id":"67"},
{"sku":"Fruit cake","position":0,"category_id":"67"},
{"sku":"Birthday Cake for Gift","position":0,"category_id":"67"}
]';
$raw_data = json_decode($arr, true);
paginate($raw_data, $page, $perPage);
function paginate($data, $page, $perPage)
{ print_r(array_slice($data, $page-1, $perPage)); }
Use dynamic variable for the page number and limit your records per page. On click of page number it automatically changes the results.
<?php
$arr = '[{"sku":"Pizza001","position":0,"category_id":"67"},{"sku":"Birthday Cake","position":0,"category_id":"67"},{"sku":"Birthday Cake-1","position":0,"category_id":"67"},{"sku":"Fruit cake","position":0,"category_id":"67"},{"sku":"Birthday Cake for Gift","position":0,"category_id":"67"}]';
$raw_data = json_decode($arr, true);
paginate($raw_data, 1);
function paginate($data, $page = $pageNumber, $perPage = $limit)
{
$x = ($page - 1) * $perPage;
$z = $page * $perPage;
$y = ($z > count($data)) ? count($data) : $z;
for (; $x < $y; ++$x) {
print_r($data[$x]);
}
}

PDO pagination query not executed correctly?

I'm currently trying to make some pagination work, but without any success.
The first query I'm executing is to grab all the data needed: so first result should be on the first page, second result on the second and so on... But I'm always getting the first result. Although I copied the query into phpmyadmin to manualy set the query and there it showed me the right results.
Here's the code where everything is happening. I'm stuck why it won't work.
$page = isset($_GET['page']) ? $_GET['page'] : 1;
$limit = 1;
$start = 0;
$query = $db->prepare("SELECT * FROM `users` LIMIT $limit OFFSET $start");
$query->execute();
$result = $query->fetchAll(PDO::FETCH_OBJ);
$count = count($result);
$query2 = $db->prepare("SELECT * FROM `users`");
$query2->execute();
$result2 = $query2->fetchALL(PDO::FETCH_OBJ);
$count2 = count($result2);
// Pagination
$total = ceil($count2 / $limit);
if ($page > 1) {
$start = ($page - 1) * $limit;
}
if ($page != $total) {
$next_page = '<li>»</li>';
} else {
$next_page = '<li class="disabled">»</li>';
}
if ($page > 1) {
$previous_page = '<li>«</li>';
} else {
$previous_page = '<li class="disabled">«</li>';
}
What am I doing wrong?
You are doing nothing with $page. $start is always 0 in the query.
You're going to need to define how many records you want per page, then multiply that by $page -1, e.g.
$start = ($page - 1) * $records_per_page;

Pagination for foreach loop

I currently have a method inside my "car class" that display the car:
static function getCars(){
$autos = DB::query("SELECT * FROM automoviles");
$retorno = array();
foreach($autos as $a){
$automovil = automovil::fromDB($a->marca, $a->modelo, $a->version, $a->year, $a->usuario_id, $a->kilometraje, $a->info,
$a->hits, $a->cilindrada, $a->estado, $a->color, $a->categoria, $a->precio, $a->idAutomovil);
array_push($retorno, $automovil);
}
return $retorno;
}
In my index.php I call that function
foreach(car::getCars() as $a){
That allows me to display the info this way ( of course inside the foreach I have a huge code with the details I'll display.
Is there a way to implement a pagination to that thing so I can handle 8 cars per page, instead of showing all of them at the same page?
You can add a $limit and $page parameter on your function so that it will only return a maximum of $limit number of items starting from $limit * $page(or will call it the $offset). You also need to add a function to get the total number of rows you have for automoviles table.
static function getCars($page = 0, $limit = 8){
$offset = $limit * max(0, $page - 1);
//replace this with prepared statement
$autos = DB::query("SELECT * FROM automoviles LIMIT $offset, $limit");
$retorno = array();
foreach($autos as $a){
$automovil = automovil::fromDB($a->marca, $a->modelo, $a->version, $a->year, $a->usuario_id, $a->kilometraje, $a->info,
$a->hits, $a->cilindrada, $a->estado, $a->color, $a->categoria, $a->precio, $a->idAutomovil);
array_push($retorno, $automovil);
}
return $retorno;
}
static function getTotal()
{
//query to get total number of rows in automoviles table
}
In your index.php do this:
foreach(car::getCars((isset($_GET['page']) ? $_GET['page'] : 1)) as $a){
...
}
and add the pagination links.
$total = car::getTotal();
if($total > 8) {
for($i = 1; $i <= intval(ceil(1.0 * $total / $limit)); $i++) {
echo '' . $i . ';
}
}

XML pagination with PHP [duplicate]

This question already has answers here:
How to Paginate lines in a foreach loop with PHP
(2 answers)
Closed 9 years ago.
Below is code I'm using to parse XML file, however file has many records and I want to paginate it, and display 20 records per page.
I also want the pagination links at bottom of page so users can go to other pages as well. It should be something like, if no value is give then it will start from 0 to 20 else if value is 2 start from 40 and stop at 60, test.php?page=2.
$xml = new SimpleXMLElement('xmlfile.xml', 0, true);
foreach ($xml->product as $key => $value) {
echo "$value->name";
echo "<br>";
}
Something like this should work:
<?php
$startPage = $_GET['page'];
$perPage = 10;
$currentRecord = 0;
$xml = new SimpleXMLElement('xmlfile.xml', 0, true);
foreach($xml->product as $key => $value)
{
$currentRecord += 1;
if($currentRecord > ($startPage * $perPage) && $currentRecord < ($startPage * $perPage + $perPage)){
echo "$value->name";
//echo $value->name;
echo "<br>";
}
}
//and the pagination:
for ($i = 1; $i <= ($currentRecord / $perPage); $i++) {
echo("<a href='thispage.php?page=".$i."'>".$i."</a>");
} ?>
You could use php's array_slice function (Documentation: http://www.php.net/manual/en/function.array-slice.php)
Start would be $page * $itemsPerPage, end would be $page * $itemsPerPage + $itemsPerPage and the number of pages would be ceil(count($xml->product) / $itemsPerPage).
Example:
$allItems = array(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20);
$itemsPerPage = 5;
$page = isset($_GET['page']) ? intval($_GET['page']) : 0;
foreach (array_slice($allItems, $page * $itemsPerPage, $page * $itemsPerPage + $itemsPerPage) as $item) {
echo "item $item";
}
It even works :) see: http://codepad.org/JiOiWcD1
As SimpleXMLElement is a Traversable, you can do the pagination with a LimitItertor which ships with PHP.
To get the total number of product elements you can use the SimpleXMLElement::count() function.
Pagination works like outlined in the hundreds of other questions, I preferable use the LimitPagination type for it.
It takes the current page, the total amount of elements and elements per page as arguments (see as well: PHP 5.2 and Pagination). It also has a helper function to provide the LimitIterator.
Example:
$products = $xml->product;
// pagination
$pagination = new LimitPagination($_GET['page'], $products->count(), 20);
foreach ($pagination->getLimitIterator($products) as $product) {
...
}
If you want to output a pager that allows to navigate between the pages, the LimitPagination has more to offer to make that a bit easier, e.g. for just all pages highlighting the current page (here exemplary with brackets):
foreach ($pagination->getPageRange() as $page)
{
if ($page === $pagination->getPage()) {
// current page
printf("[p%d] ", $page);
} else {
printf("p%d ", $page);
}
}
foreach ($pagination->getPageRange() as $page)
{
if ($page === $pagination->getPage()) {
// current page
printf("[p%d] ", $page);
} else {
printf("p%d ", $page);
}
}
Interactive online demo: http://codepad.viper-7.com/OjvNcO
Less interactive online demo: http://eval.in/14176

Categories