Chapter24 課題の完成

課題内容 CMSに機能を拡張しよう。

作成したCMSに「ページング機能」と、「カテゴリ絞込機能」を追加して下さい。
時間があればCSSでCMSをもっと使いやすくデザインして下さい。

課題内容

作成したCMSに以下の機能を追加して下さい。

  • ページング機能 (
  • カテゴリ絞込機能 (
提出ファイル
「mini-cms」フォルダ内の全てのファイル
提出場所
別途指示

ページング機能完成例 新着記事一覧にページング。

基本的には、管理画面に実装したページング機能と同じ。

mini-cms/index.php
<?php
  // ファイルの読み込み
  require_once('inc/config.php');
  require_once('inc/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 posts';

    // SQLを実行
    $stmt = $dbh->query($sql);

    // 実行結果を連想配列として取得
    $count = $stmt->fetch(PDO::FETCH_ASSOC);

    // トータルページ数
    $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();
  }
?>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>新着記事一覧</title>
</head>
<body>
  <h1>新着情報</h1>
  <p>全<?php echo h($count['total']) ?>件 <small>(<?php echo h($page) ?> / <?php echo h($total); ?>ページ目)</small></p>

  <dl>
    <?php foreach($result as $row) : ?>
    <dt><time datetime="<?php echo h($row['created']); ?>"><?php echo h(date('Y年m月d日', strtotime($row['created']))); ?></time></dt>
    <dd>
      <a href="detail.php?id=<?php echo h($row['id']); ?>">
        <?php echo h($row['title']); ?>
      </a>
    </dd>
    <?php endforeach; ?>
  </dl>

  <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>

答えを見る

ブラウザでの表示例

RERUN

カテゴリ絞込機能の完成例 新着記事のカテゴリを絞込。

カテゴリのIDをGETパラメータに付加して、
WHERE句でカテゴリIDと一致するレコードのみ取得する
カテゴリの表示も、「categories」テーブルから取得し、
ループで表示すると効率が良い。

mini-cms/index.php
<?php
  // ファイルの読み込み
  require_once('inc/config.php');
  require_once('inc/functions.php');

  // 現在のページを取得
  $page = 1; // 初期値
  if ( isset($_GET['page']) && !empty($_GET['page']) ) {
    $page = $_GET['page'];
  }

  // カテゴリを取得
  $current_category = 0;
  if ( isset($_GET['category']) && !empty($_GET['category']) ) {
    $current_category = $_GET['category'];
  }

  // 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);

    // トータルページ数
    $total = ceil($total / $limit);

    // $page が存在しないページ番号にならない対策
    $page = max($page, 1);  // 1より小さくならない
    $page = min($page, $total);  // トータルページ数より大きくならない

    // 取得する投稿の開始位置
    $start = ($page - 1) * $limit;

    // SQL文の作成 カテゴリ
    $sql = 'SELECT * FROM categories';

    // SQLを実行
    $stmt = $dbh->query($sql);

    // 実行結果を連想配列として取得
    $categories = $stmt->fetchAll(PDO::FETCH_ASSOC);

    if ( $current_category === 0 ) {
      // 全てのカテゴリ表示時

      // SQL文の作成
      $sql = 'SELECT p.*, c.category_name FROM posts AS p JOIN categories AS c ON p.category_id = c.id 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);
    } else {
      // カテゴリが絞り込まれている時

      // SQL文の作成
      $sql = 'SELECT p.*, c.category_name FROM posts AS p JOIN categories AS c ON p.category_id = c.id WHERE p.category_id = ? ORDER BY created DESC LIMIT ?, ?';

      // ステートメント用意
      $stmt = $dbh->prepare($sql);

      // プレースホルダーに値をガッチャンコ
      $stmt->bindValue(1, (int)$current_category , PDO::PARAM_INT);
      $stmt->bindValue(2, (int)$start , PDO::PARAM_INT);
      $stmt->bindValue(3, (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>
  <p>全<?php echo h($count['total']) ?>件 <small>(<?php echo h($page) ?> / <?php echo h($total); ?>ページ目)</small></p>

  <ul>
    <li><a href="index.php?category=0">全て</a></li>
    <?php foreach( $categories as $category ) : ?>
    <li><a href="index.php?category=<?php echo $category['id']; ?>"><?php echo h($category['category_name']); ?></a></li>
    <?php endforeach; ?>
  </ul>

  <dl>
    <?php foreach($result as $row) : ?>
    <dt><time datetime="<?php echo h($row['created']); ?>"><?php echo h(date('Y年m月d日', strtotime($row['created']))); ?></time> 【<?php echo h($row['category_name']); ?>】</dt>
    <dd>
      <a href="detail.php?id=<?php echo h($row['id']); ?>">
        <?php echo h($row['title']); ?>
      </a>
    </dd>
    <?php endforeach; ?>
  </dl>

  <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>

答えを見る


ページング機能と連動

カテゴリが絞り込まれている時は、トータルの記事件数もWHERE句で絞らなければ件数がずれる。
またページジングのリンクには、現在のカテゴリIDのGETパラメータも追加する必要がある。

mini-cms/index.php
<?php
  // ファイルの読み込み
  require_once('inc/config.php');
  require_once('inc/functions.php');

  // 現在のページを取得
  $page = 1; // 初期値
  if ( isset($_GET['page']) && !empty($_GET['page']) ) {
    $page = $_GET['page'];
  }

  // カテゴリを取得
  $current_category = 0;
  if ( isset($_GET['category']) && !empty($_GET['category']) ) {
    $current_category = $_GET['category'];
  }

  // 1ページ辺りの表示件数
  $limit = 5;

  try {
    // データベースへ接続
    $dbh = new PDO(DSN, DB_USER, DB_PASSWORD);

    // エラー発生時に「PDOException」という例外を投げる設定に変更
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    if ( $current_category === 0 ) {
      // 全カテゴリ表示時

      // SQL文の作成 レコード数を抽出
      $sql = 'SELECT count(*) AS total FROM posts';

      // SQLを実行
      $stmt = $dbh->query($sql);

    } else {
      // 特定のカテゴリのみ表示時

      // SQL文の作成 レコード数を抽出
      $sql = 'SELECT count(*) AS total FROM posts WHERE category_id = ?';

      // ステートメント用意
      $stmt = $dbh->prepare($sql);

      // プレースホルダーに値をガッチャンコ
      $stmt->bindValue(1, (int)$current_category , PDO::PARAM_INT);

      // ステートメントを実行
      $stmt->execute();
    }

    // 実行結果を連想配列として取得
    $count = $stmt->fetch(PDO::FETCH_ASSOC);
    // print_r($count);

    // トータルが 0 にならない対策
    $total = max($count['total'], 1);

    // トータルページ数
    $total = ceil($total / $limit);

    // $page が存在しないページ番号にならない対策
    $page = max($page, 1);  // 1より小さくならない
    $page = min($page, $total);  // トータルページ数より大きくならない

    // 取得する投稿の開始位置
    $start = ($page - 1) * $limit;

    // SQL文の作成 カテゴリ
    $sql = 'SELECT * FROM categories';

    // SQLを実行
    $stmt = $dbh->query($sql);

    // 実行結果を連想配列として取得
    $categories = $stmt->fetchAll(PDO::FETCH_ASSOC);

    if ( $current_category === 0 ) {
      // 全てのカテゴリ表示時

      // SQL文の作成
      $sql = 'SELECT p.*, c.category_name FROM posts AS p JOIN categories AS c ON p.category_id = c.id 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);
    } else {
      // カテゴリが絞り込まれている時

      // SQL文の作成
      $sql = 'SELECT p.*, c.category_name FROM posts AS p JOIN categories AS c ON p.category_id = c.id WHERE p.category_id = ? ORDER BY created DESC LIMIT ?, ?';

      // ステートメント用意
      $stmt = $dbh->prepare($sql);

      // プレースホルダーに値をガッチャンコ
      $stmt->bindValue(1, (int)$current_category , PDO::PARAM_INT);
      $stmt->bindValue(2, (int)$start , PDO::PARAM_INT);
      $stmt->bindValue(3, (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>
  <p>全<?php echo h($count['total']) ?>件 <small>(<?php echo h($page) ?> / <?php echo h($total); ?>ページ目)</small></p>

  <ul>
    <li><a href="index.php?category=0">全て</a></li>
    <?php foreach( $categories as $category ) : ?>
    <li><a href="index.php?category=<?php echo $category['id']; ?>"><?php echo h($category['category_name']); ?></a></li>
    <?php endforeach; ?>
  </ul>

  <dl>
    <?php foreach($result as $row) : ?>
    <dt><time datetime="<?php echo h($row['created']); ?>"><?php echo h(date('Y年m月d日', strtotime($row['created']))); ?></time> 【<?php echo h($row['category_name']); ?>】</dt>
    <dd>
      <a href="detail.php?id=<?php echo h($row['id']); ?>">
        <?php echo h($row['title']); ?>
      </a>
    </dd>
    <?php endforeach; ?>
  </dl>

  <nav>
    <h2>ページナビゲーション</h2>
    <ul>
      <?php if ( $page > 1 ) : ?>
      <li><a href="index.php?page=<?php echo h( $page - 1 ); ?>&category=<?php echo h($current_category); ?>">前のページへ</a></li>
      <?php endif; ?>
      <?php if ( $page < $total ) : ?>
      <li><a href="index.php?page=<?php echo h( $page + 1 ); ?>&category=<?php echo h($current_category); ?>">次のページへ</a></li>
      <?php endif; ?>
    </ul>
  </nav>
</body>
</html>

答えを見る

ブラウザでの表示例

RERUN