Automatic Pagination Help Needed

Viewed 440

I have this script:

<?php
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles")
{
    include("Articles.php");
}
?>

Which allows me to setup a custom URL for a page. Now, the problem occurs comes when I need URLs like these:

/?articles | Page that contains newest articles which loads when user clicks "< Previous" button
/?articles&1 | Page that contains older articles which loads when user clicks "Next >" button
/?articles&2 | Page that contains older than older articles
/?articles&3 | Page that contains old articles
/?articles&4 | Page that contains old articles
/?articles&5 | Page that contains oldest articles
/?articles&6 | Page that contains oldest articles, also last page in the pagination

And so on. Namely, the code in the script above would be like this:

if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&1")
{
    include("Articles-500.php");
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&2")
{
    include("Articles-499.php");
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&3")
{
    include("Articles-498.php");
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&4")
{
    include("Articles-497.php");
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&5")
{
    include("Articles-496.php");
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&6")
{
    include("Articles-495.php");
}

But each time the page with the newest articles gets filled (contains 10 articles per page) I have to update the script like this:

if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&1")
{
    include("Articles-505.php");
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&1")
{
    include("Articles-504.php");
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&1")
{
    include("Articles-503.php");
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&1")
{
    include("Articles-501.php");
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&1")
{
    include("Articles-500.php");
}

I also need you to tell me how to use it practically in my navigation, id est: <A href="?">Next</A> and <A href="?">Previous</A>

5 Answers

What you can do, in order to have this created dynamically is to use the function glob that can list you all the files in a directory.

So given that you would have the files:

  • Articles-1.php
  • Articles-2.php
  • Articles-3.php
  • ...

Now, if your articles are indeed named as above (Articles-1.php and not Articles-001.php) you will face the problem that a simple alphabetical sort won't work.
Indeed, Articles-99.php will always show up earlier than Articles-505.php in a simple alphabetical sort.

But, hopefully, PHP offers us the really neat sorting which is natsort().

This function implements a sort algorithm that orders alphanumeric strings in the way a human being would while maintaining key/value associations. This is described as a "natural ordering". An example of the difference between this algorithm and the regular computer string sorting algorithms (used in sort()) can be seen in the example below.

Sorting via natsort() is nicer than my original usort idea, all credit for this idea goes to dılo sürücü's answer.

Still, we need the sorting to be in a reverse order, and this can be provided using the SORT_NATURAL flag on rsort().

SORT_NATURAL - compare items as strings using "natural ordering" like natsort()

Source: https://www.php.net/manual/en/function.sort.php
Credit for the usage of this flag goes to Seh Horng's answer

// Listing all the articles pages
$articles = glob("Articles-*.php");
// Applying reverse natural sorting, with SORT_NATURAL flag on rsort
rsort($articles, SORT_NATURAL);

/**
 * Extracting the selected page out of the REQUEST_URI, 
 * and transform it an integer
 */
$selectedPage = intval(
  str_replace("/?articles&", "", urldecode($_SERVER['REQUEST_URI']))
);

/**
 * This is a sanity check; 
 * just in case someone tries to access a page that does not exists,
 * like the page 99999999999
 * It also tackles the case when intval fails and returns 0
 * causing the index to be 0 - 1 = -1
 *
 * By the way, doing this check also covers your default behaviour
 * on the URL /?articles
 */
if(array_key_exists($selectedPage - 1, $articles)){

  /** 
   * We need to remove one from the selected page, 
   * remembering an array start at 0 in PHP
   */
  include $articles[$selectedPage - 1];
} else {
  include "Articles.php";
}

This should give you the right include dynamically.

Assuming that there will always be only 6 articles. You can try the following script:

<?php

//get array of php files in the current folder matching Articles-*.php
$phpfiles = glob("Articles\-*.php");

//sort by descending order
$sorted = rsort($phpfiles, SORT_NATURAL);

//use only the first 6 php files in the sorted array list
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&1")
{
    include($sorted[0]);
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&2")
{
    include($sorted[1]);
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&3")
{
    include($sorted[2]);
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&4")
{
    include($sorted[3]);
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&5")
{
    include($sorted[4]);
}
if(urldecode($_SERVER['REQUEST_URI']) == "/?articles&6")
{
    include($sorted[5]);
}

?>
$articles=glob('Article-*.php');
$sortedArticles=natsort($articles); 

$articleIndex=$_GET['articles'] ?? null;

$article=$sortedArticles[$articleIndex] ?? "Article.php";

include_once (string)$article;
<?php 

/*Supposing this our url :  path/?articles&3   
  and we have in serveur this articles 
  articles-498.php
  articles-500.php
  articles-499.php
  articles-496.php
  articles-497.php
  articles-495.php
*/
$current_url = parse_url($_SERVER['REQUEST_URI']); 
$path        = $current_url["path"]; //return articles&3

preg_match_all('!\d+!', $current_url["query"], $matches); //get the number of article
$index = current($matches)[0]; // now we have the index is 3
 
$articles = glob("articles-*.php"); // for get all the article 
rsort($articles, SORT_NATURAL);  // sort the article to get the new article 500-499-498 ...


$pages=array();
for($i=0;$i<count($articles);$i++){
     $x = $i+1;
     $pages["articles&".$x]=$articles[$i];
}
 // This array will return like this print_r($pages);
 /*
Array
(
    [articles&1] => articles-500.php
    [articles&2] => articles-499.php
    [articles&3] => articles-498.php
    [articles&4] => articles-497.php
    [articles&5] => articles-496.php
    [articles&6] => articles-495.php 
 
 */

/* here we check if the artile is exist in our serveur*/
if(file_exists($pages["articles&".$index])){
    //if exists
$current  = array_search($pages["articles&".$index], $pages);  // get the current article  articles&3
$Next     = array_search($pages["articles&".intval($index+1)], $pages); // this is next article  3+1  [articles&4] => articles-497.php
$Previous = array_search($pages["articles&".intval($index-1)], $pages); // this is Previous article  3-1  [articles&2] => articles-499.php

//include the current article 
include($pages["articles&".$index]);
  
  // and then show your tag a like this 
  echo "<br><a href='$path?".$Previous."'>Previous</a> Current Page : 
    ".$current." <a href='$path?".$Next."'>Next</a>";

}elseif($current_url["query"] == "articles"){
  include("articles.php");
  echo "<br><a href='$path?articles&1'>Next</a>";
 }else{
  echo "Page Not Found <a href='$path?articles'>Back to articles</a> !!";
 }


// final reuslt is 
/*

content of articles&3 

<a href='path/?articles&2'>Previous</a> Current Page : articles&3 <a href='path/?articles&4'>Next</a>

*/

die;

?>

Version Online DEMO CHECK HERE

Why not use $_GET global? If you already use parameters in URL then php should put them inside $_GET, you just have to assign values to them.

With URL like this:

/?articles=1

You will have seted $_GET['articles'] which you can check if exist and if it does just include Article.

if(isset($_GET['articles'])) {
  include("Articles.php");
}

And this method will also help you with pagination because you will have actual value in $_GET['articles'] so you could just do something like this:

<a href="?articles=<?= $_GET['articles'] + 1 ?>">Next</a> 

and

<a href="?articles=<?= $_GET['articles'] - 1 ?>">Previous</a>

and you will have to create if for checking when to hide previous or next button accordingly based on value of $_GET['articles'] (if it's equal to 1 then hide previous if equal to 6 hide next)

Or if you want to separate pagination and page action then you could just use two parameters:

/?page=articles&pagination=1

For your problem with including articles I would use DirectoryIterator which will allow you to iterate over all files in given folder and choose 6 newest files by using filectime() which return timestamp of when file was created and getting first 6 biggest values.

For example (created with this answer):

$dir = new DirectoryIterator(__DIR__);

  $articles = [];

  foreach ($dir as $file) {
    if ($file->isDot()) continue; // here we check if it's not a hidden file

    $articles[] = array(
      "path"  => $file->getPathname(),
      "cTime" => filectime($file->getPathname())
    );
  }

  $columns = array_column($articles, 'cTime');

  array_multisort($columns, SORT_DESC, $articles);

  print_r($articles);

Which will return all files in the directory in which script is placed, sorted by time they were created by newest (Descending).

This method is especially good because you don't depend on names and enumeration but on time the files were created. You can change it to time when they were modified so the updated posts will be newest.

NOTES

I'm pretty sure you know this but to have to sanitize data if you are giving user access and possibility to change it. I'm taking about $_GET from which I recommend getting values. Here is good answer on how to.

I also recommend to check if value is in proper format, for example pagination- it must be always an positive integer and lower or equal then 6:

if (isset($_GET['pagination']) && filter_var($_GET['pagination'], FILTER_VALIDATE_INT) !== false && $_GET['pagination'] > 0 &&  $_GET['pagination'] <= 6) {
   // code here
}
Related