概要と目標
ページング機能を実装し、
1ページあたりの表示件数を制限しよう。
一覧ページは、記事数が多くなるとスクロール量が多くなるため、
1ページあたりの表示件数を制限するページング機能を実装しましょう。
一覧ページは、記事数が多くなるとスクロール量が多くなるため、
1ページあたりの表示件数を制限するページング機能を実装しましょう。
SELECT文に、LIMITを付ける。
ページング機能は、SELECT文のLIMITで表示件数を制限する。
今回は、1ページあたりの表示件数を「5」件にしたい。
そのため、1ページ目は「1件目」から「5件」表示し、
2ページ目は「6件目」から「5件」表示・・・としたい。
この記事を取得する開始位置を計算する必要がある。
LIMIT句の開始位置は、「0」から始まる。
従って、1件目は「0」、6件目は「5」となる。
この、ページごとの開始位置を計算式で求めたい。
(現在のページ - 1) × 1ページあたりの表示件数
ページングのページ番号はGETパラメータで管理する。
<?php
// セッションの開始
session_start();
// ファイルの読み込み
require_once('../inc/config.php');
require_once('../inc/functions.php');
// CSRF対策 ・・・ トークンの生成
set_token();
// 現在のページを取得
$page = 1; // 初期値
if ( isset($_GET['page']) && !empty($_GET['page'])) {
$page = $_GET['page'];
}
// 1ページ辺りの表示件数
$limit = 5;
try {
// データベースへ接続
$dbh = new PDO(DSN, DB_USER, DB_PASSWORD);
// エラー発生時に「PDOException」という例外を投げる設定に変更
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL文の作成
$sql = 'SELECT * FROM posts';
// SQLを実行
$stmt = $dbh->query($sql);
// 実行結果を連想配列として取得
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
// print_r($result);
// データベースとの接続を終了
$dbh = null;
} catch (PDOException $e) {
// 例外発生時の処理
echo 'エラー' . h($e->getMessage());
exit();
}
?>
LIMITで制限する。
LIMITの開始位置を計算するために必要なものが揃ったので、。
開始位置を計算し、SELECT文に LIMITを付ける
<?php
// セッションの開始
session_start();
// ファイルの読み込み
require_once('../inc/config.php');
require_once('../inc/functions.php');
// CSRF対策 ・・・ トークンの生成
set_token();
// 現在のページを取得
$page = 1; // 初期値
if ( isset($_GET['page']) && !empty($_GET['page']) ) {
$page = $_GET['page'];
}
// 1ページ辺りの表示件数
$limit = 5;
try {
// データベースへ接続
$dbh = new PDO(DSN, DB_USER, DB_PASSWORD);
// エラー発生時に「PDOException」という例外を投げる設定に変更
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 取得する投稿の開始位置
$start = ($page - 1) * $limit;
// SQL文の作成
$sql = 'SELECT * FROM posts';
// SQLを実行
$stmt = $dbh->query($sql);
// 実行結果を連想配列として取得
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
// print_r($result);
// データベースとの接続を終了
$dbh = null;
} catch (PDOException $e) {
// 例外発生時の処理
echo 'エラー' . h($e->getMessage());
exit();
}
?>
LIMIT句を使って、SELECT文をLIMIT句で制限する<?php
// セッションの開始
session_start();
// ファイルの読み込み
require_once('../inc/config.php');
require_once('../inc/functions.php');
// CSRF対策 ・・・ トークンの生成
set_token();
// 現在のページを取得
$page = 1; // 初期値
if ( isset($_GET['page']) && !empty($_GET['page']) ) {
$page = $_GET['page'];
}
// 1ページ辺りの表示件数
$limit = 5;
try {
// データベースへ接続
$dbh = new PDO(DSN, DB_USER, DB_PASSWORD);
// エラー発生時に「PDOException」という例外を投げる設定に変更
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 取得する投稿の開始位置
$start = ($page - 1) * $limit;
// SQL文の作成
$sql = 'SELECT * FROM posts ORDER BY created DESC LIMIT ?, ?';
// ステートメント用意
$stmt = $dbh->prepare($sql);
// プレースホルダーに値をガッチャンコ
$stmt->bindValue(1, (int)$start , PDO::PARAM_INT);
$stmt->bindValue(2, (int)$limit , PDO::PARAM_INT);
// ステートメントを実行
$stmt->execute();
// 実行結果を連想配列として取得
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
// print_r($result);
// データベースとの接続を終了
$dbh = null;
} catch (PDOException $e) {
// 例外発生時の処理
echo 'エラー' . h($e->getMessage());
exit();
}
?>
?page=2」など)し、
ただし、GETパラメータに存在しないページ番号を入力しても移動してしまう。$page 変数が存在するページ以外にならない対策が必要。
全レコード数 ÷ 1ページあたりの表示件数
(余りは繰り上げ)
最大ページ数を計算するには、記事が全部で何件あるかを取得し、
それを、1ページあたりの表示件数で割る必要がある。
従ってまず、データベースから集約関数を使って、全レコード数を取得する。
SELECT文で、「posts」テーブルの全記事数を取得する
<?php
// セッションの開始
session_start();
// ファイルの読み込み
require_once('../inc/config.php');
require_once('../inc/functions.php');
// CSRF対策 ・・・ トークンの生成
set_token();
// 現在のページを取得
$page = 1; // 初期値
if ( isset($_GET['page']) && !empty($_GET['page']) ) {
$page = $_GET['page'];
}
// 1ページ辺りの表示件数
$limit = 5;
try {
// データベースへ接続
$dbh = new PDO(DSN, DB_USER, DB_PASSWORD);
// エラー発生時に「PDOException」という例外を投げる設定に変更
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL文の作成 レコード数を抽出
$sql = 'SELECT count(*) AS total FROM posts';
// SQLを実行
$stmt = $dbh->query($sql);
// 実行結果を連想配列として取得
$count = $stmt->fetch(PDO::FETCH_ASSOC);
print_r($count);
// 取得する投稿の開始位置
$start = ($page - 1) * $limit;
// SQL文の作成
$sql = 'SELECT * FROM posts ORDER BY created DESC LIMIT ?, ?';
// ステートメント用意
$stmt = $dbh->prepare($sql);
// プレースホルダーに値をガッチャンコ
$stmt->bindValue(1, (int)$start , PDO::PARAM_INT);
$stmt->bindValue(2, (int)$limit , PDO::PARAM_INT);
// ステートメントを実行
$stmt->execute();
// 実行結果を連想配列として取得
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
// print_r($result);
// データベースとの接続を終了
$dbh = null;
} catch (PDOException $e) {
// 例外発生時の処理
echo 'エラー' . h($e->getMessage());
exit();
}
?>
ページングに必要なページ数を割り出すには、
トータル記事数 ÷ 1ページあたりの表示数の端数を繰り上げする。
ceil関数 ・・・ 繰り上げをする関数ceil(繰り上げする数値)
詳細はPHPマニュアルを参照
print_r($count);をコメントにし、<?php
// セッションの開始
session_start();
// ファイルの読み込み
require_once('../inc/config.php');
require_once('../inc/functions.php');
// CSRF対策 ・・・ トークンの生成
set_token();
// 現在のページを取得
$page = 1; // 初期値
if ( isset($_GET['page']) && !empty($_GET['page']) ) {
$page = $_GET['page'];
}
// 1ページ辺りの表示件数
$limit = 5;
try {
// データベースへ接続
$dbh = new PDO(DSN, DB_USER, DB_PASSWORD);
// エラー発生時に「PDOException」という例外を投げる設定に変更
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL文の作成 レコード数を抽出
$sql = 'SELECT count(*) AS total FROM posts';
// SQLを実行
$stmt = $dbh->query($sql);
// 実行結果を連想配列として取得
$count = $stmt->fetch(PDO::FETCH_ASSOC);
// print_r($count);
// トータルページ数
$total = ceil($count['total'] / $limit);
// 取得する投稿の開始位置
$start = ($page - 1) * $limit;
// SQL文の作成
$sql = 'SELECT * FROM posts ORDER BY created DESC LIMIT ?, ?';
// ステートメント用意
$stmt = $dbh->prepare($sql);
// プレースホルダーに値をガッチャンコ
$stmt->bindValue(1, (int)$start , PDO::PARAM_INT);
$stmt->bindValue(2, (int)$limit , PDO::PARAM_INT);
// ステートメントを実行
$stmt->execute();
// 実行結果を連想配列として取得
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
// print_r($result);
// データベースとの接続を終了
$dbh = null;
} catch (PDOException $e) {
// 例外発生時の処理
echo 'エラー' . h($e->getMessage());
exit();
}
?>
URLのGETパラメータに、存在しなページ番号が直接入力された場合、
「1」 ~ トータルページ数の範囲内に修正する。
max関数 ・・・ 指定された数値の中から最大値を取得する関数max(数値1, 数値2)
引数: 引数は「,(半角カンマ)」で複数指定できる
詳細はPHPマニュアルを参照
min関数 ・・・ 指定された数値の中から最小値を取得する関数min(数値1, 数値2)
引数: 引数は「,(半角カンマ)」で複数指定できる
詳細はPHPマニュアルを参照
max関数を使い、ページ番号が「1」より小さくならない対策を行い、min関数を使い、ページ番号がトータルページ数より大きくならない対策を行う
<?php
// セッションの開始
session_start();
// ファイルの読み込み
require_once('../inc/config.php');
require_once('../inc/functions.php');
// CSRF対策 ・・・ トークンの生成
set_token();
// 現在のページを取得
$page = 1; // 初期値
if ( isset($_GET['page']) && !empty($_GET['page']) ) {
$page = $_GET['page'];
}
// 1ページ辺りの表示件数
$limit = 5;
try {
// データベースへ接続
$dbh = new PDO(DSN, DB_USER, DB_PASSWORD);
// エラー発生時に「PDOException」という例外を投げる設定に変更
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL文の作成 レコード数を抽出
$sql = 'SELECT count(*) AS total FROM posts';
// SQLを実行
$stmt = $dbh->query($sql);
// 実行結果を連想配列として取得
$count = $stmt->fetch(PDO::FETCH_ASSOC);
// print_r($count);
// トータルページ数
$total = ceil($count['total'] / $limit);
// $page が存在しないページ番号にならない対策
$page = max($page, 1); // 1より小さくならない
$page = min($page, $total); // トータルページ数より大きくならない
// 取得する投稿の開始位置
$start = ($page - 1) * $limit;
// SQL文の作成
$sql = 'SELECT * FROM posts ORDER BY created DESC LIMIT ?, ?';
// ステートメント用意
$stmt = $dbh->prepare($sql);
// プレースホルダーに値をガッチャンコ
$stmt->bindValue(1, (int)$start , PDO::PARAM_INT);
$stmt->bindValue(2, (int)$limit , PDO::PARAM_INT);
// ステートメントを実行
$stmt->execute();
// 実行結果を連想配列として取得
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
// print_r($result);
// データベースとの接続を終了
$dbh = null;
} catch (PDOException $e) {
// 例外発生時の処理
echo 'エラー' . h($e->getMessage());
exit();
}
?>
?page=0」など)し、
ページングのリンクは、リンク先にページ数のGETパラメータを付加して、
次のページ、または前のページを指定する。
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>タイトル</th>
<th>公開日</th>
<th>更新日</th>
<th>編集</th>
<th>削除</th>
</tr>
</thead>
<tbody>
<?php foreach($result as $row) : ?>
<tr>
<td><?php echo h($row['id']); ?></td>
<td><?php echo h($row['title']); ?></td>
<td><time datetime="<?php echo h($row['created']); ?>"><?php echo h(date('Y年m月d日', strtotime($row['created']))); ?></time></td>
<td><time datetime="<?php echo h($row['modified']); ?>"><?php echo h(date('Y年m月d日', strtotime($row['modified']))); ?></time></td>
<td><a href="edit.php?id=<?php echo h($row['id']); ?>">編集</a></td>
<td>
<form action="delete.php" method="post">
<input type="hidden" name="id" value="<?php echo h($row['id']); ?>">
<input type="hidden" name="token" value="<?php echo h($_SESSION['token']); ?>">
<input type="submit" value="削除">
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<nav>
<h2>ページナビゲーション</h2>
<ul>
<?php if ( $page > 1 ) : ?>
<li><a href="index.php?page=<?php echo h( $page - 1 ); ?>">前のページへ</a></li>
<?php endif; ?>
<?php if ( $page < $total ) : ?>
<li><a href="index.php?page=<?php echo h( $page + 1 ); ?>">次のページへ</a></li>
<?php endif; ?>
</ul>
</nav>
完成例を参考に「tranig」フォルダ内にある「index.php」を使って、
カテゴリの管理画面にページング機能を作成して下さい。
<?php
// データベースの定数
define('DB_NAME', 'mini_cms_app');
define('DB_USER', 'root');
define('DB_PASSWORD', ''); // パスワード (MAMPは「root」)
define('DSN', 'mysql:host=localhost;dbname='. DB_NAME . ';charset=utf8');
<?php
// XSS 対策
function h($s) {
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}
<?php
// ファイルの読み込み
require_once('config.php');
require_once('functions.php');
// 現在のページを取得
$page = 1; // 初期値
if ( isset($_GET['page']) && !empty($_GET['page']) ) {
$page = $_GET['page'];
}
// 1ページ辺りの表示件数
$limit = 5;
try {
// データベースへ接続
$dbh = new PDO(DSN, DB_USER, DB_PASSWORD);
// エラー発生時に「PDOException」という例外を投げる設定に変更
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// SQL文の作成 レコード数を抽出
$sql = 'SELECT count(*) AS total FROM categories';
// SQLを実行
$stmt = $dbh->query($sql);
// 実行結果を連想配列として取得
$count = $stmt->fetch(PDO::FETCH_ASSOC);
// print_r($count);
// トータルページ数
$total = ceil($count['total'] / $limit);
// $page が存在しないページ番号にならない対策
$page = max($page, 1); // 1より小さくならない
$page = min($page, $total); // トータルページ数より大きくならない
// 取得する投稿の開始位置
$start = ($page - 1) * $limit;
// SQL文の作成
$sql = 'SELECT * FROM categories LIMIT ?, ?';
// ステートメント用意
$stmt = $dbh->prepare($sql);
// プレースホルダーに値をガッチャンコ
$stmt->bindValue(1, (int)$start , PDO::PARAM_INT);
$stmt->bindValue(2, (int)$limit , PDO::PARAM_INT);
// ステートメントを実行
$stmt->execute();
// 実行結果を連想配列として取得
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
// print_r($result);
// データベースとの接続を終了
$dbh = null;
} catch (PDOException $e) {
// 例外発生時の処理
echo 'エラー' . h($e->getMessage());
exit();
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>カテゴリの管理</title>
</head>
<body>
<h1>カテゴリの管理</h1>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>カテゴリ名</th>
</tr>
</thead>
<tbody>
<?php foreach($result as $row) : ?>
<tr>
<td><?php echo h($row['id']); ?></td>
<td><?php echo h($row['category_name']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<nav>
<h2>ページナビゲーション</h2>
<ul>
<?php if ( $page > 1 ) : ?>
<li><a href="index.php?page=<?php echo h( $page - 1 ); ?>">前のページへ</a></li>
<?php endif; ?>
<?php if ( $page < $total ) : ?>
<li><a href="index.php?page=<?php echo h( $page + 1 ); ?>">次のページへ</a></li>
<?php endif; ?>
</ul>
</nav>
</body>
</html>
解答例は全問題のチェックボックスが on になるとご覧いただけます。
記事数が多くなると記事一覧ページが、縦にとても長くなる。
ページングを活用することで、複数ページにわけることが出来る。