PHPにおけるページネーションの方法

初投稿です。
PHPのページネーションに関して、以前実装したものの知識が抜けていたので、アウトプットがてら復習したいと思います。何かのお役に立てれば幸いです。

要件
・各ページで5つずつ表示する
・ページの前後には「前へ」「次へ」を表示。複数ページある場合はリンクになり、それぞれのボタンを押すと前後のページへジャンプする
・ページ番号は5つまで表示。基本的に現在表示されているページの前後2つを表示させる
・ページ数が表示されてる部分にあわせて「○件中○〜○件目の商品」と記載する

なお、今回はLaravelなどのフレームワークは使わずに、生のPHPで実装します。

イメージとしてはこんな感じです。

![スクリーンショット 2020-07-27 22.36.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655473/b7d129a6-2117-3ab4-3049-261665efe8c6.png)

DB関連

```php
function get_db_connect(){
  $dsn = 'mysql:dbname='. DB_NAME .';host='. DB_HOST .';charset='.DB_CHARSET;
 
  try {
    $dbh = new PDO($dsn, DB_USER, DB_PASS, array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4'));
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
  } catch (PDOException $e) {
    exit('接続できませんでした。');
  }
  return $dbh;
}


function fetch_all_query($db, $sql, $params = array()){
  try{
    $statement = $db->prepare($sql);
    $statement->execute($params);
    return $statement->fetchAll();
  }catch(PDOException $e){
    エラーメッセージの表示;
  }
  return false;
}

```

<h1>①modelで必要な要素をselectで抽出する</h1>

$db = get_db_connect();

```php

function get_items_list($db, $limit = 5, $offset = 0){
  $sql = '
    SELECT
       --selectする要素を記載
    limit ?
    offset ?';

  return fetch_all_query($db, $sql, array($limit, $offset));
}
```
このとき、$limit=5で表示させる件数を予め限定させておく。

また、全体の件数も知っておきたいので、countを使って実装。

```php

function get_items_count($db) {
  $sql = '
    SELECT
        count(*)
    FROM
        テーブル名'
        
    $result = fetch_all_query($db, $sql);

    return $result[0]['count(*)'];
}
```

<h1>②view画面の実装</h1>

```php

<?php 

foreach($items as $value) {

--ここにget_items_list関数から表示させたいアイテムを表示

} 

echo $items_count. '件中'.$page_ini. "〜" .$page_fin. "件目の商品";

// リンクをつけるかの判定
if($now > 1){ 
    print '<a href=\'../html/〜.php?page_id='.($now - 1).'\')>前へ</a>'. '　';
} else {
    print '前へ'. '　';
}

for($i = 1; $i <= $max_page; $i++){
    if($i >= $now - $range && $i <= $now + $range) {
        if ($i == $now) {
            print '<a class="current_page" href=\'〜.php?page_id='.$now.'\')>'. $now. '</a>'. '　';
        } else {
            print '<a href=\'../html/〜.php?page_id='.$i. '\')>'. $i. '</a>'. '　';
        }
    }
}
// リンクをつけるかの判定
if($now < $max_page){ 
    print '<a href=\'../html/〜.php?page_id='.($now + 1).'\')>次へ</a>'. '　';
} else {
    print '次へ';
}
?>
```
まず、〜件中○〜○件目の商品の部分から。

$items_countに関しては、modelで記述したget_items_count関数を使って表示させます。

page_iniとpage_fin（つまり、表示させる商品ページの最初と最後の部分）に関しては、getで取得するページidによって、offsetの値が定義され、それにより表示されるページが変わるといった感じ。

「前へ」「次へ」に関しては、クリックするとgetしたページid+1がgetで渡される。

表示されるページの数に関しては、for文にて表示。

$rangeに関しては、ページidが1、もしくは最後のページのときは4、id=2、最後のページから1つ前のときは3、それ以外は2となるように設定しています。

```php

//ページネーションの表示数
if((int)$now === 1 || (int)$now === (int)$max_page) {
    $range = 4;
} else if((int)$now === 2 || (int)$now === ((int)$max_page -1)) {
    $range = 3;
} else  {
    $range = 2;
}
```
<h1>③controllerの記載</h1>

viewで指定した変数をそれぞれ定義する。

```php

if(get_get('page_id')) {
  $now = $_GET['page_id'];
} else {
  $now = 1;
}

$offset = ($now - 1) * 5;

$items = get_items_list($db, $limit = 5, $offset);

$items_count = get_items_count();

$max_page = ceil($items_count / 5);

$page_ini = ($offset + 1);

if(count($items) === 5) {
  $page_fin = ($offset + 5);
} else {
  $page_fin = $items_count;
}

if((int)$now === 1 || (int)$now === (int)$max_page) {
    $range = 4;
} else if((int)$now === 2 || (int)$now === ((int)$max_page -1)) {
    $range = 3;
} else  {
    $range = 2;
}
```
まずGETで受け取ったページidを定義する（get_getはユーザー定義関数）

```php

//viewからのidをgetしたとき
if(get_get('page_id')) {
  $now = $_GET['page_id'];
} else {
  //設定されてない場合は1ページ目にする
  $now = 1;
}
```
定義したら、その変数（$now)を使ってoffsetを定義する

```php

//ここでoffsetの定義
$offset = ($now - 1) * 5;
```
modelで実装したselect文もここで定義しておく。

```php

$items = get_items_list($db, $limit = 5, $offset);
$items_count = get_items_count();
```

ceil関数を使って、小数点になる場合は繰り上げを行う。

```php

$max_page = ceil($items_count / 5);
```

前述の通り、page_iniとpage_fin（つまり、表示させる商品ページの最初と最後の部分）に関しては、offsetの値により表示されるページが変わる。

```php

//最初
$page_ini = ($offset + 1);

//最後（1ページに表示されるアイテム数が5つの場合とそうでない場合で条件分岐）
if(count($items) === 5) {
  $page_fin = ($offset + 5);
} else {
  $page_fin = $items_count;
}
```

あとはLaravelでもページネーションを実装してみたいな。