著者は、 Apache Software Foundation を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

ページ付けは、レコードセットで返される行の数を個別の整然としたページに制限して、それらの間を簡単に移動できるようにするという概念です。したがって、大きなデータセットがある場合は、各ページで特定の数の行のみを返すようにページ付けを構成できます。 たとえば、ページに表示されるアイテムの数を減らすことで、Webストアに数千の製品が含まれている場合に、ユーザーがすべての製品を表示する必要がなくなる可能性が高いため、ページネーションはユーザーを圧倒するのを防ぐのに役立ちます。 もう1つの例は、モバイルデバイスでレコードを表示するアプリケーションです。 このような場合にページ付けを有効にすると、レコードが複数のページに分割され、画面にうまく収まります。

エンドユーザーにとっての視覚的な利点に加えて、ページネーションは、一度に返されるレコードの数を減らすため、アプリケーションを高速化します。 これにより、クライアントとサーバー間で送信する必要のあるデータが制限され、RAMなどのサーバーリソースを保護できます。

このチュートリアルでは、 PHP スクリプトを作成してデータベースに接続し、 MySQL LIMIT句を使用してスクリプトへのページ付けを実装します。

前提条件

始める前に、次のものが必要になります。

  • Ubuntu 18.04を使用した初期サーバーセットアップに従ってセットアップされた1つのUbuntu18.04サーバー(sudo非rootユーザーを含む)。
  • システムにインストールされているApache、MySQL、およびPHP。 Linux、Apache、MySQL、PHP(LAMP)スタックをUbuntu18.04にインストールする方法に関するガイドに従うことができます。

ステップ1—データベースユーザーとテストデータベースの作成

このチュートリアルでは、MySQLデータベースに接続し、レコードをフェッチして、テーブル内のHTMLページに表示するPHPスクリプトを作成します。 PHPスクリプトは、Webブラウザから2つの異なる方法でテストします。 まず、ページネーションコードなしでスクリプトを作成して、レコードがどのように表示されるかを確認します。 次に、ページナビゲーションコードをPHPファイルに追加して、ページ付けが実際にどのように機能するかを理解します。

PHPコードには、認証のためのMySQLユーザーと、接続するためのサンプルデータベースが必要です。 このステップでは、MySQLデータベース、サンプルデータベース、およびPHPスクリプトをテストするためのテーブルの非rootユーザーを作成します。

サーバーへのログインを開始します。 次に、次のコマンドを使用してMySQLサーバーにログインします。

  1. sudo mysql -u root -p

MySQLサーバーのrootパスワードを入力し、ENTERを押して続行します。 次に、MySQLプロンプトが表示されます。 このチュートリアルでtest_dbと呼ぶサンプルデータベースを作成するには、次のコマンドを実行します。

  1. Create database test_db;

次の出力が表示されます。

Output
Query OK, 1 row affected (0.00 sec)

次に、test_userを作成し、test_dbに対するすべての権限をユーザーに付与します。 PASSWORDを強力な値に置き換えます。

  1. GRANT ALL PRIVILEGES ON test_db.* TO 'test_user'@'localhost' IDENTIFIED BY 'PASSWORD';
Output
Query OK, 1 row affected (0.00 sec)

次のコマンドでMySQL権限をリロードします。

  1. FLUSH PRIVILEGES;
Output
Query OK, 1 row affected (0.00 sec)

次に、test_dbデータベースに切り替えて、test_dbデータベースで直接作業を開始します。

  1. Use test_db;
Output
Database changed

次に、productsテーブルを作成します。 この表にはサンプル製品が含まれています。このチュートリアルでは、データに必要な列は2つだけです。 product_id列は、各レコードを一意に識別するための主キーとして機能します。 この列はAUTO_INCREMENTに設定され、挿入されたアイテムごとに新しいproduct_idを生成します。 product_nameフィールドを使用して、各アイテムを名前で区別します。

  1. Create table products (product_id BIGINT PRIMARY KEY AUTO_INCREMENT, product_name VARCHAR(50) NOT NULL ) Engine = InnoDB;
Output
Query OK, 0 rows affected (0.02 sec)

productsテーブルに10個のテスト製品を追加するには、次のSQLステートメントを実行します。

  1. Insert into products(product_name) values ('WIRELESS MOUSE');
  2. Insert into products(product_name) values ('BLUETOOTH SPEAKER');
  3. Insert into products(product_name) values ('GAMING KEYBOARD');
  4. Insert into products(product_name) values ('320GB FAST SSD');
  5. Insert into products(product_name) values ('17 INCHES TFT');
  6. Insert into products(product_name) values ('SPECIAL HEADPHONES');
  7. Insert into products(product_name) values ('HD GRAPHIC CARD');
  8. Insert into products(product_name) values ('80MM THERMAL PRINTER');
  9. Insert into products(product_name) values ('HDMI TO VGA CONVERTER');
  10. Insert into products(product_name) values ('FINGERPRINT SCANNER');

次の出力が表示されます。

Output
Query OK, 1 row affected (0.02 sec)

次のコマンドを実行して、製品がテーブルに挿入されたことを確認します。

  1. select * from products;

2つの列内の出力に製品が表示されます。

Output
+------------+-----------------------+ | product_id | product_name | +------------+-----------------------+ | 1 | WIRELESS MOUSE | | 2 | BLUETOOTH SPEAKER | | 3 | GAMING KEYBOARD | | 4 | 320GB FAST SSD | | 5 | 17 INCHES TFT | | 6 | SPECIAL HEADPHONES | | 7 | HD GRAPHIC CARD | | 8 | 80MM THERMAL PRINTER | | 9 | HDMI TO VGA CONVERTER | | 10 | FINGERPRINT SCANNER | +------------+-----------------------+ 10 rows in set (0.00 sec)

MySQLを終了します:

  1. quit;

サンプルデータベース、テーブル、およびテストデータが配置されたら、Webページにデータを表示するためのPHPスクリプトを作成できるようになりました。

ステップ2—ページネーションなしでMySQLレコードを表示する

次に、前の手順で作成したMySQLデータベースに接続するPHPスクリプトを作成し、Webブラウザーに製品を一覧表示します。 このステップでは、PHPコードはページ付けなしで実行され、分割されていないレコードが1つのページにどのように表示されるかを示します。 このチュートリアルでは、テスト目的で10個のレコードしかありませんが、ページ付けなしでレコードを表示すると、データをセグメント化することで最終的にユーザーエクスペリエンスが向上し、サーバーへの負担が軽減される理由がわかります。

次のコマンドを使用して、WebサイトのドキュメントルートにPHPスクリプトファイルを作成します。

  1. sudo nano /var/www/html/pagination_test.php

次に、次のコンテンツをファイルに追加します。 PASSWORDを、前の手順でtest_userに割り当てたパスワードの正しい値に置き換えることを忘れないでください。

/var/www/html/pagination_test.php
<?php

try {
  $pdo = new PDO("mysql:host=localhost;dbname=test_db", "test_user", "PASSWORD");
  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);

  $sql = "select * from products";

  $stmt = $pdo->prepare($sql);

  $stmt -> execute();

  echo "<table border='1' align='center'>";

  while (($row = $stmt -> fetch(PDO::FETCH_ASSOC)) !== false) {
    echo "<tr>";
    echo "<td>".$row['product_id']."</td>";
    echo "<td>".$row['product_name']."</td>";
    echo "</tr>";
  }

  echo "</table>";

} catch(PDOException $e) {
  echo $e->getMessage();
}

?>

CTRL+XYENTERを押してファイルを保存します。

このスクリプトでは、ステップ1で作成したデータベース資格情報を使用して PDO(PHPデータオブジェクト)ライブラリを使用してMySQLデータベースに接続しています。

PDOは、データベースに接続するための軽量のインターフェースです。 データアクセス層はより移植性が高く、わずかなコードの書き直しでさまざまなデータベースで機能します。 PDOは、プリペアドステートメントをサポートしているため、セキュリティが強化されています。これは、クエリを安全な方法で高速化するための機能です。

次に、select * from productsステートメントを実行し、ページ付けなしでHTMLテーブルに製品を一覧表示するようにPDOAPIに指示します。 行$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);は、データ型がデータベースに表示されるとおりに返されることを保証します。 これは、PDOがproduct_idを整数として返し、product_nameを文字列として返すことを意味します。 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);は、エラーが発生した場合に例外をスローするようにPDOに指示します。 デバッグを容易にするために、PHPtry{}...catch{}ブロック内のエラーをキャッチしています。

作成した/var/www/html/pagination_test.phpPHPスクリプトファイルを実行するには、次のURLにアクセスして、your-server-IPをサーバーのパブリックIPアドレスに置き換えます。

http://your-server-IP/pagination_test.php

製品の表が記載されたページが表示されます。

MySQL Records Displayed with a PHP script - No Pagination

PHPスクリプトは期待どおりに機能しています。 1ページにすべての製品を一覧表示します。 何千もの製品がある場合、製品がデータベースからフェッチされてPHPページにレンダリングされるため、ループが長くなります。

この制限を克服するには、PHPスクリプトを変更し、MySQL LIMIT句といくつかのナビゲーションリンクを表の下部に含めて、ページ付け機能を追加します。

ステップ3—PHPを使用したページ付けの実装

このステップの目標は、テストデータを複数の管理しやすいページに分割することです。 これにより、読みやすさが向上するだけでなく、サーバーのリソースをより効率的に使用できます。 前の手順で作成したPHPスクリプトを変更して、ページネーションに対応します。

これを行うには、MySQLLIMIT句を実装します。 これをスクリプトに追加する前に、MySQLLIMIT構文の例を見てみましょう。

  1. Select [column1, column2, column n...] from [table name] LIMIT offset, records;

LIMIT句は、このステートメントの最後に示されているように、2つの引数を取ります。 offset値は、最初の行の前にスキップするレコードの数です。 recordsは、ページごとに表示するレコードの最大数を設定します。

ページネーションをテストするには、ページごとに3つのレコードを表示します。 合計ページ数を取得するには、テーブルの合計レコードを、ページごとに表示する行で除算する必要があります。 次に、次のPHPコードスニペットの例に示すように、PHP Ceil関数を使用して、結果の値を最も近い整数に丸めます。

$total_pages=ceil($total_records/$per_page);

以下は、完全なページネーションコードを含むPHPスクリプトの修正バージョンです。 ページネーションとナビゲーションコードを含めるには、/var/www/html/pagination_test.phpファイルを開きます。

  1. sudo nano /var/www/html/pagination_test.php

次に、次の強調表示されたコードをファイルに追加します。

/var/www/html/pagination_test.php
<?php

try {
  $pdo = new PDO("mysql:host=localhost;dbname=test_db", "test_user", "PASSWORD");
  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);

  /* Begin Paging Info */

  $page = 1;

  if (isset($_GET['page'])) {
    $page = filter_var($_GET['page'], FILTER_SANITIZE_NUMBER_INT);
  }

  $per_page = 3;

  $sqlcount = "select count(*) as total_records from products";
  $stmt = $pdo->prepare($sqlcount);
  $stmt->execute();
  $row = $stmt->fetch();
  $total_records = $row['total_records'];

  $total_pages = ceil($total_records / $per_page);

  $offset = ($page-1) * $per_page;

  /* End Paging Info */

  $sql = "select * from products limit :offset, :per_page";
  $stmt = $pdo->prepare($sql);
  $stmt->execute(['offset'=>$offset, 'per_page'=>$per_page]);

  echo "<table border='1' align='center'>";

  while ( ($row = $stmt->fetch(PDO::FETCH_ASSOC) ) !== false) {
    echo "<tr>";
    echo "<td>".$row['product_id']."</td>";
    echo "<td>".$row['product_name']."</td>";
    echo "</tr>";
  }

  echo "</table>";

  /* Begin Navigation */

  echo "<table border='1' align='center'>";

  echo "<tr>";

  if ($page-1 >= 1) {
    echo "<td><a href=".$_SERVER['PHP_SELF']."?page=".($page - 1).">Previous</a></td>";
  }

  if ($page+1 <= $total_pages) {
    echo "<td><a href=".$_SERVER['PHP_SELF']."?page=".($page + 1).">Next</a></td>";
  }

  echo "</tr>";

  echo "</table>";

  /* End Navigation */

} catch(PDOException $e) {
  echo $e->getMessage();
}

?>

ファイルで、ページングを実行するために追加のパラメーターを使用しました。

  • $page:この変数は、スクリプトの現在のページを保持します。 ページ間を移動するとき、スクリプトは$_GET['page']変数を使用してpageという名前のURLパラメーターを取得します。
  • $per_page:この変数は、ページごとに表示する最大レコードを保持します。 あなたの場合、各ページに3つの製品をリストします。
  • $total_records:製品を一覧表示する前に、SQLステートメントを実行して、ターゲットテーブル内のレコードの総数を取得し、それを$total_records変数に割り当てます。
  • $offset:この変数は、最初の行の前にスキップするレコードの合計を表します。 この値は、式$offset=($page-1)*$per_pageを使用してPHPスクリプトによって動的に計算されます。 この式をPHPページネーションプロジェクトに適合させることができます。 必要に応じて$per_page変数を変更できることを忘れないでください。 たとえば、50の値に変更して、モバイルデバイス用のWebサイトまたは別の金額を実行している場合は、ページごとに50個のアイテムを表示できます。

ここでも、ブラウザでIPアドレスにアクセスし、your_server_ipをサーバーのパブリックIPアドレスに置き換えます。

http://your_server_ip/pagination_test.php

ページの下部にいくつかのナビゲーションボタンが表示されます。 最初のページには、前へボタンは表示されません。 次へページボタンが表示されない最後のページでも同じことが起こります。 また、各ページにアクセスすると、pageURLパラメーターがどのように変化するかに注意してください。

MySQL Records Displayed with a PHP script with Pagination - Page 1

MySQL Records Displayed with a PHP script with Pagination - Page 2

Final page of MySQL Records Displayed with a PHP script with Pagination - Page 4

ページの下部にあるナビゲーションリンクは、ファイルからの次のPHPコードスニペットを使用して実現されます。

/var/www/html/pagination_test.php
. . .
if( $page-1>=1) {
  echo "<td><a href=".$_SERVER['PHP_SELF']."?page=".($page-1).">Previous</a></td>";
}

if( $page+1<=$total_pages) {
  echo "<td><a href=".$_SERVER['PHP_SELF']."?page=".($page+1).">Next</a></td>";
}
. . .

ここで、$page変数は現在のページ番号を表します。 次に、前のページを取得するために、コードは変数から1を差し引きます。 したがって、2ページ目にいる場合、数式(2-1)1の結果を示し、これがリンクに表示される前のページになります。 ただし、1以上の結果が得られた場合にのみ、前のページが表示されることに注意してください。

同様に、次のページに進むには、$page変数に1つ追加し、pageURLパラメーターに追加する$pageの結果が次のようになることも確認する必要があります。 PHPコードで計算した合計ページ数を超えないようにしてください。

この時点で、PHPスクリプトはページ付けで機能しており、レコードナビゲーションを改善するためにMySQLLIMIT句を実装できます。

結論

このチュートリアルでは、Ubuntu18.04サーバー上のPHPを使用してMySQLでページングを実装しました。 PHPスクリプトを使用して、より大きなレコードセットでこれらの手順を使用して、ページネーションを含めることができます。 Webサイトまたはアプリケーションでページ付けを使用することにより、サーバー上でより優れたユーザーナビゲーションと最適なリソース使用率を作成できます。

ここから、次のチュートリアルを使用して、データベースおよびその他のデータベースタスクのさらなる最適化を検討できます。