ニュースセクション

前のセクションでは、静的ページを参照するクラスを作成することで、フレームワークの基本的な概念をいくつか説明しました。カスタムルーティングルールを追加することで、URIを整理しました。今度は動的なコンテンツを紹介し、データベースの使用を開始します。

使用するデータベースの作成

CodeIgniterのインストールでは、要件に記載されているように、適切なデータベースが設定されていることを前提としています。このチュートリアルでは、MySQLデータベースのSQLコードを提供し、データベースコマンド(mysql、MySQL Workbench、またはphpMyAdmin)を発行するための適切なクライアントがあることも前提としています。

このチュートリアルで使用できるデータベースci4tutorialを作成し、CodeIgniterで使用するように設定する必要があります。

データベースクライアントを使用して、データベースに接続し、以下のSQLコマンドを実行します(MySQL)

CREATE TABLE news (
    id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    title VARCHAR(128) NOT NULL,
    slug VARCHAR(128) NOT NULL,
    body TEXT NOT NULL,
    PRIMARY KEY (id),
    UNIQUE slug (slug)
);

また、いくつかのシードレコードを追加します。ここでは、テーブルを作成するために必要なSQL文だけを示しますが、CodeIgniterに慣れてきたら、プログラムによってこれを行うことができることに注意してください。マイグレーションシードについて読んで、後でより便利なデータベース設定を作成することができます。

補足として、ウェブパブリッシングの文脈における「slug」とは、リソースを識別して説明するためにURLで使用される、ユーザーとSEOフレンドリーな短いテキストです。

シードレコードは、次のようなものになります。

INSERT INTO news VALUES
(1,'Elvis sighted','elvis-sighted','Elvis was sighted at the Podunk internet cafe. It looked like he was writing a CodeIgniter app.'),
(2,'Say it isn\'t so!','say-it-isnt-so','Scientists conclude that some programmers have a sense of humor.'),
(3,'Caffeination, Yes!','caffeination-yes','World\'s largest coffee shop open onsite nested coffee shop for staff only.');

データベースへの接続

CodeIgniterのインストール時に作成したローカル設定ファイル** .env **には、使用するデータベースに対して適切に設定されたデータベースプロパティの設定がコメント解除され、適切に設定されている必要があります。データベース設定の説明に従って、データベースが正しく設定されていることを確認してください。

database.default.hostname = localhost
database.default.database = ci4tutorial
database.default.username = root
database.default.password = root
database.default.DBDriver = MySQLi

モデルの設定

コントローラーにデータベース操作を直接記述する代わりに、クエリはモデルに配置する必要があります。これにより、後で簡単に再利用できます。モデルは、データベースまたはその他のデータストア内の情報を取得、挿入、更新する場所です。それらはデータへのアクセスを提供します。CodeIgniterのモデルの使用で詳しく読むことができます。

NewsModelの作成

**app/Models**ディレクトリを開き、**NewsModel.php**という新しいファイルを作成し、以下のコードを追加します。

<?php

namespace App\Models;

use CodeIgniter\Model;

class NewsModel extends Model
{
    protected $table = 'news';
}

このコードは、以前使用したコントローラーコードに似ています。CodeIgniter\Modelを拡張して新しいモデルを作成し、データベースライブラリを読み込みます。これにより、$this->dbオブジェクトを介してデータベースクラスを使用できるようになります。

NewsModel::getNews()メソッドの追加

データベースとモデルが設定されたので、データベースからすべての投稿を取得するメソッドが必要です。これを行うために、CodeIgniterに含まれるデータベース抽象化レイヤーであるQueryBuilderCodeIgniter\Modelで使用されます。これにより、「クエリ」を一度記述して、サポートされているすべてのデータベースシステムで動作させることができます。Modelクラスを使用すると、QueryBuilderを簡単に操作でき、データの操作を簡素化する追加のツールも提供されます。モデルに以下のコードを追加します。

    public function getNews($slug = false)
    {
        if ($slug === false) {
            return $this->findAll();
        }

        return $this->where(['slug' => $slug])->first();
    }

このコードでは、2つの異なるクエリを実行できます。すべてのニュースレコードを取得することも、slugによるニュースアイテムを取得することもできます。$slug変数はクエリを実行する前にエスケープされていませんでしたが、QueryBuilderが自動的に実行します。

ここで使用されている2つのメソッド、findAll()first()は、CodeIgniter\Modelクラスによって提供されています。これらは、以前NewsModelクラスで設定した$tableプロパティに基づいて使用するテーブルを既に認識しています。これらは、QueryBuilderを使用して現在のテーブルでコマンドを実行し、選択した形式で結果の配列を返すヘルパーメソッドです。この例では、findAll()は配列の配列を返します。

ニュースの表示

クエリが記述されたので、モデルを、ニュースアイテムをユーザーに表示するビューに結び付ける必要があります。これは、以前作成したPagesコントローラーで行うこともできますが、明確にするために、新しいNewsコントローラーを定義します。

ルーティングルールの追加

**app/Config/Routes.php**ファイルを次のように変更します。

<?php

// ...

use App\Controllers\News; // Add this line
use App\Controllers\Pages;

$routes->get('news', [News::class, 'index']);           // Add this line
$routes->get('news/(:segment)', [News::class, 'show']); // Add this line

$routes->get('pages', [Pages::class, 'index']);
$routes->get('(:segment)', [Pages::class, 'view']);

これにより、リクエストがPagesコントローラーに直接行くのではなく、Newsコントローラーに到達することが保証されます。2番目の$routes->get()行は、slug付きのURIをNewsコントローラーのshow()メソッドにルーティングします。

ニュースコントローラーの作成

**app/Controllers/News.php**に新しいコントローラーを作成します。

<?php

namespace App\Controllers;

use App\Models\NewsModel;

class News extends BaseController
{
    public function index()
    {
        $model = model(NewsModel::class);

        $data['news'] = $model->getNews();
    }

    public function show($slug = null)
    {
        $model = model(NewsModel::class);

        $data['news'] = $model->getNews($slug);
    }
}

コードを見ると、以前に作成したファイルとの類似点があるかもしれません。まず、コアCodeIgniterクラスであるControllerを拡張するBaseControllerを継承しています。これにより、いくつかのヘルパーメソッドが提供され、現在のRequestオブジェクトとResponseオブジェクト、そしてディスクへの情報の保存に使用するLoggerクラスにアクセスできるようになります。

次に、すべてのニュースアイテムを表示するメソッドと、特定のニュースアイテムを表示するメソッドの2つのメソッドがあります。

次に、model()関数が使用され、NewsModelインスタンスが作成されます。これはヘルパー関数です。グローバル関数と定数で詳細を読むことができます。model()関数を使用しない場合は、$model = new NewsModel();と書くこともできます。

$slug変数が2番目のメソッドでモデルのメソッドに渡されていることがわかります。モデルはこのslugを使用して、返すニュースアイテムを識別しています。

完全なNews::index()メソッド

これで、データはモデルを介してコントローラによって取得されましたが、まだ何も表示されていません。次にやるべきことは、このデータをビューに渡すことです。index()メソッドを次のように変更します。

<?php

namespace App\Controllers;

use App\Models\NewsModel;

class News extends BaseController
{
    public function index()
    {
        $model = model(NewsModel::class);

        $data = [
            'news'  => $model->getNews(),
            'title' => 'News archive',
        ];

        return view('templates/header', $data)
            . view('news/index')
            . view('templates/footer');
    }

    // ...
}

上記のコードは、モデルからすべてのニュースレコードを取得し、それを変数に代入します。タイトルの値も$data['title']要素に代入され、すべてのデータがビューに渡されます。これで、ニュースアイテムをレンダリングするためのビューを作成する必要があります。

news/indexビューファイルの作成

**app/Views/news/index.php**を作成し、次のコードを追加します。

<h2><?= esc($title) ?></h2>

<?php if (! empty($news) && is_array($news)): ?>

    <?php foreach ($news as $news_item): ?>

        <h3><?= esc($news_item['title']) ?></h3>

        <div class="main">
            <?= esc($news_item['body']) ?>
        </div>
        <p><a href="/news/<?= esc($news_item['slug'], 'url') ?>">View article</a></p>

    <?php endforeach ?>

<?php else: ?>

    <h3>No News</h3>

    <p>Unable to find any news for you.</p>

<?php endif ?>

注記

再びesc()を使用してXSS攻撃を防いでいます。しかし今回は、第2のパラメータとして"url"も渡しています。これは、出力を使用するコンテキストによって攻撃パターンが異なるためです。

ここでは、各ニュースアイテムがループ処理され、ユーザーに表示されます。PHPとHTMLを混在させたテンプレートを作成していることがわかります。テンプレート言語を使用したい場合は、CodeIgniterのビューパーサーまたはサードパーティのパーサーを使用できます。

完全なNews::show()メソッド

ニュースの概要ページはこれで完了ですが、個々のニュースアイテムを表示するページはまだありません。先に作成したモデルは、この機能に簡単に使用できるように作成されています。コントローラにコードを追加し、新しいビューを作成するだけです。Newsコントローラに戻り、show()メソッドを次のように更新します。

<?php

namespace App\Controllers;

use App\Models\NewsModel;
use CodeIgniter\Exceptions\PageNotFoundException;

class News extends BaseController
{
    // ...

    public function show($slug = null)
    {
        $model = model(NewsModel::class);

        $data['news'] = $model->getNews($slug);

        if (empty($data['news'])) {
            throw new PageNotFoundException('Cannot find the news item: ' . $slug);
        }

        $data['title'] = $data['news']['title'];

        return view('templates/header', $data)
            . view('news/view')
            . view('templates/footer');
    }
}

PageNotFoundExceptionクラスをインポートするために、use CodeIgniter\Exceptions\PageNotFoundException;を追加することを忘れないでください。

パラメータなしでgetNews()メソッドを呼び出す代わりに、$slug変数が渡されるため、特定のニュースアイテムが返されます。

news/viewビューファイルの作成

残りの作業は、**app/Views/news/view.php**に対応するビューを作成することだけです。このファイルに次のコードを入力します。

<h2><?= esc($news['title']) ?></h2>
<p><?= esc($news['body']) ?></p>

ブラウザで「news」ページ(例:**localhost:8080/news**)にアクセスすると、ニュースアイテムの一覧が表示され、それぞれの記事を表示するリンクが付けられています。

../_images/tutorial2.png