Upgrading a PHP pagination function to run faster - php

I created a pagination for an website file last year, and I've never really had to use it again.
But now I'll need to use it in a bigger application, and I'm afraid it won't behave really good with a huge amount of mysql rows, I don't know.
I'll post an example bellow and I'll be greatful if someone could tell me if I really need to upgrade the code.
/* Connection file */
include( 'config.php' );
/* Get the amount of all rows */
$mqc = "SELECT COUNT(*) AS total FROM table";
$mqe = mysql_query( $mqc );
$mqn = mysql_fetch_array( $mqe );
$total = $mqn['total'];
/* Rows per page */
$perPage = 10;
/* Range for the pagination */
$range = 5;
/* Actual page */
$page = isset( $_GET['page'] ) &&
is_numeric( $_GET['page'] ) &&
!empty( $_GET['page'] ) ?
( (int)$_GET['page'] * $range ) : 0;
/* Query */
$mqc = "SELECT * FROM table
LIMIT " . $page . ", " . $perPage . " ";
$mqe = mysql_query( $mqc );
/* Show results */
while( $l = mysql_fetch_object( $mqe ) )
{
echo $l->id . ' - ' . $l->title . '<br />';
}
/* Function for pagination */
function pages( $total = false, $perPage = false, $page = false, $range = false )
{
/* Number of pages */
$totalPages = ( ceil( $total / $perPage ) ) - 1;
/* The range * 2 */
$range = $range;
/* The real param $_GET['page'] */
$realGetPage = !empty( $page ) ? ( $page / $range ) : 0;
/* Show the first page */
if($realGetPage >= 7)
{
echo ' 1 ... ';
}
/* Loop to create the range */
for($i = ( $realGetPage - $range ); $i < ( $range + $realGetPage ); $i++)
{
/* Check out if the value isn't outta range */
if( $i > 0 && $i < $totalPages )
{
/* Highlight the actual page */
if( ( $i - 1 ) == $realGetPage )
{
echo ' [ ' . $i . ' ] ';
}
else
{
echo ' ' . $i . ' ';
}
}
/* Store the number of the last page */
$lastPage = ( $i + 1 );
}
/* Hack to show 5678910 */
if( $lastPage < ( $range * 2 ) + 1 )
{
for( $j = $lastPage; $j <= ( $range * 2 ); $j++ )
{
if( ($j - 1) == $realGetPage )
{
echo ' [ ' . $j . ' ] ';
}
else
{
echo ' ' . $j . ' ';
}
}
}
/* Last page */
echo ' ... ' . $totalPages . ' ';
}
/* Show the pagination */
pages( $total, $perPage, $page, $range );
What do you think?

From the information you provided in your comment, the performance issues you might have will not come from your PHP script. If you do multiple INNER JOINS on huge tables, this is exactly what will affect your performance. Some important things to look for:
Tables indexes (if you use filters, for your joins if you don't join on primary keys)
Optimizing your table / queries to decrease the number of JOINs
If you expect a lot of traffic, consider using caching for your queries (memcached for instance)
As Phil mentionned, the ORDER BY must be used to render a predictable result and could slow down your queries if the tables are used. You could pre-render ordered table using a cron job
Consider using denormalization to reduce the number of JOINS

The main thing I see missing is an ORDER BY clause. Without this, your paged results are (potentially) unpredictable.
Add an ORDER BY clause and make sure you have appropriate database indexes in place on the sortable column(s).
I'd also recommend switching to a more up-to-date database connection library with prepared statements and bound parameters. Please see PDO.

Related

php pagination of an array with previous and next tab

I have this array which contain around 1000 records. I want to display 20 array records per page.
$list=array(
array([title]=>"sony", [description]=>"camera"),
array([title]=>"sony", [description]=>"mobiles"),
array([title]=>"lenovo", [description]=>"laptop"),
array([title]=>"lenovo", [description]=>"mobiles")
);
I have used the following code for pagination. It is giving me a long row for pagination. Can someone help me to include previous and next code to my existing code so that my pagination will look good.
$page = isset($_REQUEST['page']) && $_REQUEST['page'] > 0 ? $_REQUEST['page'] : 1;
function display($list, $page = 1)
{
$start = ($page - 1) * 2;
$list = array_slice($list, $start, 15);
foreach ($list as $key => $val) {
echo $val['title'] . '<br/>';
echo $val['description'] . '<br/>';
echo "<br>";
}} $len = count($list);
$pages = ceil($len / 2);
if ($page > $pages or $page < 1)
{
echo 'page not found';
}
else
{
display($list, $page);
for ($i = 1 ; $i <= $pages ; $i++)
{
$current = ($i == $page) ? TRUE : FALSE;
if ($current) {
echo '<b>' . $i . '</b>';
}
else
{
?>
<?php echo $i;?>
<?php
}
}
}
Here's an example with the data array from your question.
The example
The page size is assumed to be 2 (20 in your question).
The size of the data array does not matter.
The start parameter is provided (as in your example) thru a GET parameter http://localhost/flipkart-api/fkt_offer.php?…start=index_or_page. This parameter is available in the script as $_GET['start'].
The previous and next start indices are to be calculated ($start +/- $maxpage, etc.).
To keep this example simple, I took the start index, not the page number, as parameter. But you also could use a page number and calculate the index, of course.
For the reason of brevity I omitted error checking ("what if no more items", etc.).
Code:
<?php
// The data array
$list=array(
array('title'=>"sony", 'description'=>"camera"),
array('title'=>"sony", 'description'=>"mobiles"),
array('title'=>"lenovo", 'description'=>"laptop"),
array('title'=>"lenovo", 'description'=>"mobiles")
);
// Evaluate URL
$proto = ((isset($_SERVER["HTTPS"])) && (strtoupper($_SERVER["HTTPS"]) == 'ON')) ? "https://" : "http://";
$hname = getenv("SERVER_NAME");
$port = getenv("SERVER_PORT");
if ( (($port==80)&&($proto=='http://')) || (($port==443)&&($proto=='https://')) ) { $port = ''; }
$params = '';
foreach ($_GET as $key=>$value) {
if (strtolower($key)=='start') continue;
$params .= (empty($params)) ? "$key=$value" : "&$key=$value";
}
$url = $proto . $hname . $port. $_SERVER['SCRIPT_NAME'] . '?' . $params;
// Page contents
$last = count($list)-1;
$start = (isset($_GET['start'])) ? intval($_GET['start']) : 0;
if ($start<0) $start = 0; if ($start > $last) $start = $last;
$maxpage = 2;
echo "<p>Start index = $start</p>" . PHP_EOL;
$curpage = 0;
for($xi=$start; $xi<=$last; $xi++) {
if ($curpage >= $maxpage) break;
$curpage++;
echo 'Entry ' . $curpage .
': ' . $list[$xi]['title'] .
' - ' . $list[$xi]['description'] .
'<br />' . PHP_EOL;
}
// Navigation
$prev = $start - $maxpage; if ($prev<0) $prev = 0;
$next = ( ($start+$maxpage) > $last) ? $start : $start + $maxpage;
$prev = ( ($start-$maxpage) < 0) ? 0 : $start - $maxpage;
echo '<p>Previous ';
echo 'Next</p>';
?>
Result (e.g)
Start index = 2
Entry 1: lenovo - laptop
Entry 2: lenovo - mobiles
Previous Next

Pagination Prev - Next Increment

i create a php news system, but i have a problem:
<?php
include('config.php');
if( isset( $_GET["page"]) ) $PAGE=$_GET["page"]; else $PAGE=1;
$query1=mysql_query("select id, name, email , age from addd LIMIT ". (($PAGE * 5) - 5) .",5");
echo "<table><tr><td>Testo</td><td>Nome</td><td>Anni</td></tr>";
function truncate_string($str, $length) {
if (!(strlen($query2['name']) <= $length)) {
$query2['name'] = substr($query2['name'], 0, strpos($query2['name'], ' ', $length)) . '...';
}
return $query2['name'];
}
while($query2=mysql_fetch_array($query1))
{
$number= $query2['name'];
echo "<tr><td>".substr($query2['name'], 0, 500)."...</td>";
echo "<td>".$query2['email']."</td>";
echo "<td>".$query2['age']."</td>";
echo "<td>".str_word_count($number)."</td>";
echo "<td><a href='edit.php?id=".$query2['id']."'>Mod</a></td>";
echo "<td><a href='delete.php?id=".$query2['id']."' onclick=\"return confirm('Sei sicuro di volerlo eliminare?');\");'>Canc</a></td><tr>";
echo "<td><a href='singletwo.php?id=".$query2['id']."');'>vedi</a></td<tr>";
}
?>
The pages follow this numbering: ?page=1, ?page=2 ecc.
Each page contains 5 news.
How do I create an automatic pagination system?
With Prev-Next, automatically detect possible next or previous pages?
I don't know how to do it.
Start by having the max length and total number of rows in variables:
<?php
include('config.php');
$max = 5;
$total = mysql_query("select count(*) from addd");
$PAGE = isset($_GET["page"]) ? $_GET["page"] : 1;
$query1 = mysql_query("select id, name, email , age from addd LIMIT " . (($PAGE * $max) - $max) . "," . $max);
That way, you can calculate how many pages you'll need.
The following code will give you a page list (Page 1, Page 2, Page 3 etc.):
for($i = 0; $i < ceil($total / $max); $i ++)
{
$p = $i + 1;
echo 'Page ' . $p . '';
}
If you'd rather have Previous and Next links, try this:
if($PAGE > 1)
echo '<a href="?page=' . ($PAGE - 1) . '>Previous</a>';
if(ceil($total / $max) > $PAGE)
echo '<a href="?page=' . ($PAGE + 1) . '>Next</a>';
What you could do is:
$currentPage = isset($_GET['page']) ? $_GET['page'] : 1;
$limit = $currentPage*5;
$offset = $offset-5;
Now that you have these numbers, you can use them in your query:
$stmt = "SELECT
...
FROM news
LIMIT ".$offset.", ".$limit.";
This way you'll get the records you want. As far as the next and previous buttons go:
if ($currentPage > 1) {
// Show previous button
}
For the next button you'll need to do another query:
$stmt = "SELECT COUNT(*) as total FROM news";
$result = $pdo->fetch();
$totalRows = $result['total'];
if ($currentPage < round($totalRows/5)) {
// Show next button
}

PHP Pagination with 5 pages

if( isset($_GET['page'] ) )
{
$page = $_GET['page']-1;
$offset = $recLimit * $page;$page = $_GET['page']+1;
}
else
{
$page=2;
$offset = 0;
}
$phpself = $_SERVER['PHP_SELF'];
if( $totalPages <= $noofpages )
{
echo "Pages if less than or equal 5 : ";
for($i=1; $i <= $totalPages; $i++)
{
echo "$i |";
}
}
else
{
$i=1;
for($i; $i <= $totalPages; $i++)
{
echo "$i |";
}
}
?><table border="1" cellpadding="8"><tr> <th>ID</th><th>Name</th><th>Age</th><th>E-mail ID</th><th>Verified</th>
I'm goin to make a PHP pagination but i've some problem, in this i want to show 5 pages and 4 records per page. and if we click on next button then it shows the remaining page. And there are total 7 pages.
Since I couldn't really understand your code, here is something to get you started:
$items_per_page = 4;
/* Get the total number of records */
$result = mysqli_query("select count(*) as n from ...");
$row = mysqli_fetch_assoc($result);
$total_count = $row["n"];
/* Calculate the total number of pages */
$total_pages = ceil($total_count / $items_per_page);
/* Determine the current page */
$current_page = intval(#$_GET["page"]);
if(!$current_page || $current_page < 1) { $current_page = 1; }
if($current_page > $total_pages) { $current_page = $total_pages; }
/* Get the records for the current page */
$limit = $items_per_page;
$offset = ($current_page - 1) * $items_per_page;
$result = mysqli_query("select ... limit " . $offset . ", " . $limit);
To display the pagination links, use this:
for($i = 1; $i <= $total_pages; $i++)
{
if($i > 1) { print(" | "); }
if($current_page == $i) { print($i); }
else { print('' . $i . ''; }
}

Doctrine2 Paginator getting total results

Right away I would say, that I read this question Doctrine2 Paginator, but it doesn't give me enough information regarding the title of my question.
When I used Doctrine1 I was getting the result with such code for example:
$list = $this->query
->addSelect( 'SQL_CALC_FOUND_ROWS *' )
->offset( ( $this->currentPage - 1 ) * $this->perPage )
->limit( $this->perPage )
->execute( array(), \Doctrine_Core::HYDRATE_ARRAY_SHALLOW );
$totalRecords = SqlCalcFoundRowsEventListener::getFoundRowsCount();
$this->totalPages = ceil( $totalRecords / $this->perPage );
and it was great.
Now, when using Doctrine2 I'm confused on how should I get total amount of records with same query as limited result for current page.
Any help/advice would be greatly appreciated.
The paginator usage is as following:
$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($query);
$totalItems = count($paginator);
$pagesCount = ceil($totalItems / $pageSize);
// now get one page's items:
$paginator
->getQuery()
->setFirstResult($pageSize * ($currentPage-1)) // set the offset
->setMaxResults($pageSize); // set the limit
foreach ($paginator as $pageItem) {
echo "<li>" . $pageItem->getName() . "</li>";
}
Or some other loop:
for ($i=0; $i < $pagesCount; $i++) {
if ($currentPage == ($i+1)) {
$pagination1 .= '<li class="active">'.($i+1).'</li>';
}else{
$pagination1 .= '<li>'.($i+1).'</li>';
}
}

How to reliably find similar strings to that typed in

I have an interface where a user will enter the name of a company. It then compares what they typed to current entries in the database, and if something similar is found it presents them with options (in case they misspelled) or they can click a button which confirms what they typed is definitely new and unique.
The problem I am having is that it is not very accurate and often brings up dozens of "similar" matches that aren't that similar at all!
Here is what I have now, the first large function I didn't make and I am not clear on what exactly it does. Is there are much simpler way to acheive what I want?
// Compares strings and determines how similar they are based on a nth letter split comparison.
function cmp_by_optionNumber($b, $a) {
if ($a["score"] == $b["score"]) return 0;
if ($a["score"] > $b["score"]) return 1;
return -1;
}
function string_compare($str_a, $str_b)
{
$length = strlen($str_a);
$length_b = strlen($str_b);
$i = 0;
$segmentcount = 0;
$segmentsinfo = array();
$segment = '';
while ($i < $length)
{
$char = substr($str_a, $i, 1);
if (strpos($str_b, $char) !== FALSE)
{
$segment = $segment.$char;
if (strpos($str_b, $segment) !== FALSE)
{
$segmentpos_a = $i - strlen($segment) + 1;
$segmentpos_b = strpos($str_b, $segment);
$positiondiff = abs($segmentpos_a - $segmentpos_b);
$posfactor = ($length - $positiondiff) / $length_b; // <-- ?
$lengthfactor = strlen($segment)/$length;
$segmentsinfo[$segmentcount] = array( 'segment' => $segment, 'score' => ($posfactor * $lengthfactor));
}
else
{
$segment = '';
$i--;
$segmentcount++;
}
}
else
{
$segment = '';
$segmentcount++;
}
$i++;
}
// PHP 5.3 lambda in array_map
$totalscore = array_sum(array_map(function($v) { return $v['score']; }, $segmentsinfo));
return $totalscore;
}
$q = $_POST['stringA'] ;
$qLengthMin = strlen($q) - 5 ; // Part of search calibration. Smaller number = stricter.
$qLengthMax = strlen($q) + 2 ; // not in use.
$main = array() ;
include("pdoconnect.php") ;
$result = $dbh->query("SELECT id, name FROM entity_details WHERE
name LIKE '{$q[0]}%'
AND CHAR_LENGTH(name) >= '$qLengthMin'
#LIMIT 50") ; // The first letter MUST be correct. This assumption makes checker faster and reduces irrelivant results.
$x = 0 ;
while($row = $result->fetch(PDO::FETCH_ASSOC)) {
$percent = string_compare(strtolower($q), strtolower(rawurldecode($row['name']))) ;
if($percent == 1) {
//echo 1 ;// 1 signifies an exact match on a company already in our DB.
echo $row['id'] ;
exit() ;
}
elseif($percent >= 0.6) { // Part of search calibration. Higher deci number = stricter.
$x++ ;
$main[$x]['name'] = rawurldecode($row['name']) ;
$main[$x]['score'] = round($percent, 2) * 100;
//array_push($overs, urldecode($row['name']) . " ($percent)<br />") ;
}
}
usort($main, "cmp_by_optionNumber") ;
$z = 0 ;
echo '<div style="overflow-y:scroll;height:175px;width:460px;">' ;
foreach($main as $c) {
if($c['score'] > 100) $c['score'] = 100 ;
if(count($main) > 1) {
echo '<div id="anysuggested' . $z . '" class="hoverdiv" onclick="selectAuto(' . "'score$z'" . ');">' ;
}
else echo '<div id="anysuggested' . $z . '" class="hoverdiv" style="color:#009444;" onclick="selectAuto(' . "'score$z'" . ');">' ;
echo '<span id="autoscore' . $z . '">' . $c['name'] . '</span></div>' ;
$z++ ;
}
echo '</div>' ;
Comparing strings is a huge topic and there are many ways to do it. One very common algorithm is called the Levenshtein difference. This is a native implementation in PHP but none in MySQL. There is however an implementation here that you could use.
You need aproximate/fuzzy string matching.
Read more about
http://php.net/manual/en/function.levenshtein.php, http://www.slideshare.net/kyleburton/fuzzy-string-matching
The best way would be to use some index based search engine like SOLR http://lucene.apache.org/solr/.

Categories