2021/03/21

[実践CodeIgniter4]第1回:Viewクラスの拡張

[実践CodeIgniter4]第1回:Viewクラスの拡張 イメージ

こんにちは。エンジニアのマサオです。
今回は個人的に大好きなPHPフレームワーク「CodeIgniter4」について実践的な日本語の入門記事がまだ少ないようなので、せっかくの機会ですからご紹介したいと思います。

CodeIgniter(コードイグナイター)とは?

軽量・シンプルかつエレガントで、フル機能のwebアプリケーションを制作するためのフレームワークです。(公式サイトより。翻訳がイマイチな部分はお許しください。)

2021年の国内PHPフレームワークのトレンドでは圧倒的に多いのが「Laravel」。
次に多いのは「CakePHP」、その次が「Symfony」。
そしてそのさらに下が「FuelPHP」「CodeIgniter」との統計です。
海外だとCodeIgniter(以後CI)は3位あたりの人気のようです。

筆者が開発する際に心掛けていることは、『できる限り素早い応答速度』『保守性』この2点です。
CIは他のフレームワークに比べ機能が軽量・シンプルが売りなので、筆者の心掛けを2点とも十分に満たしてくれます。

これまではPHP5.3以上対応のCI3が主流でしたが、最近ではPHP7系が主流になってきているので、1年ちょっと前の2020年初頭にようやくCI4が正式リリースされました。
現在ではバージョンも4.1.1まで上がってきていますし、最近のレンタルサーバーはほとんどPHP7以上に対応してきているので、CI4を使わない手はありません。
CI3から大幅に構造が変わってしまいましたが、何より名前空間(namespace)が使えるようになったのでクラス名の管理が容易になりました。

本記事では公式ドキュメントや他のサイトで多く取り上げられている本当に初めてCIを触られる方向けの内容は割愛します。
※公式ドキュメントはこちら → CodeIgniter4 User Guide - CodeIgniter4.1.1 documentation(英語)
※翻訳版ドキュメントはこちら → CodeIgniter4 ユーザーガイド - CodeIgniter4 4.0.0-beta.2 ドキュメント

CIを拡張する

CI3ではアプリケーションのCoreディレクトリ内に『MY_コアクラス名.php』と言うファイルを置いておくだけで勝手に拡張したクラスを読込してくれていました。
CI4ではアプリケーションディレクトリ内に設置したファイルを利用するためにはもうひと手間必要です。

公式ドキュメント『Extending CodeIgniter - Creating Core System Classes』にとても簡単な説明があります。
本記事では『CodeIgniter\View\View.php』を拡張するものとして解説します。

拡張Viewクラスを作成する

公式ドキュメントでは『App/Libraries』ディレクトリに追加していますが、CI3の頃の慣習、構造の分かりやすさを踏まえアプリケーションディレクトリ直下に『Core/View』ディレクトリを作成し、その中にファイル名を『View.php』として新しいクラスファイルを作成してください。

<?php namespace App\Core\View;

use Psr\Log\LoggerInterface;

class View extends \CodeIgniter\View\View
{
    public function __construct($config, string $viewPath = null, $loader = null, bool $debug = null, LoggerInterface $logger = null)
    {
        parent::__construct($config, $viewPath, $loader, $debug, $logger);
    }
}

まずはこれが基本形となります。
元のViewクラスを継承した新しいViewクラスを作成し、コンストラクタが呼び出された際に親クラスのコンストラクタも呼び出します。

ではこのViewクラスで試しにHTML出力の際のhead内のmeta要素用にいくつかプロパティを追加してみましょう。

<?php namespace App\Core\View;

use Psr\Log\LoggerInterface;

class View extends \CodeIgniter\View\View
{
    public $page_title = "タイトルのテストです";
    public $page_description = "デスクリプションのテストです";
    public $page_robots = "NOINDEX,NOFOLLOW";

    public function __construct($config, string $viewPath = null, $loader = null, bool $debug = null, LoggerInterface $logger = null)
    {
        parent::__construct($config, $viewPath, $loader, $debug, $logger);
    }
}

Config/Services.php へ登録する

先ほど作成した新しいViewクラスを呼び出させるためには『App/Config/Services.php』への登録が必要です。
元々Viewクラスを呼び出ししているのは『CodeIgniter/Config/Services.php』内の rendererメソッド なのでまず内容を確認します。

<?php
namespace CodeIgniter\Config;

...(省略)

class Services extends BaseService
{
	/**
	 * The Renderer class is the class that actually displays a file to the user.
	 * The default View class within CodeIgniter is intentionally simple, but this
	 * service could easily be replaced by a template engine if the user needed to.
	 *
	 * @param string  $viewPath
	 * @param mixed   $config
	 * @param boolean $getShared
	 *
	 * @return \CodeIgniter\View\View
	 */
	public static function renderer(string $viewPath = null, $config = null, bool $getShared = true)
	{
		if ($getShared)
		{
			return static::getSharedInstance('renderer', $viewPath, $config);
		}

		if (is_null($config))
		{
			$config = new \Config\View();
		}

		if (is_null($viewPath))
		{
			$paths = config('Paths');

			$viewPath = $paths->viewDirectory;
		}

		return new \CodeIgniter\View\View($config, $viewPath, static::locator(), CI_DEBUG, static::logger());
	}
}

これをオーバーライドした内容のrendererメソッドを定義しないと先ほど作った新しいViewクラスは永久に呼び出されませんので、『App/Config/Services.php』に以下のように追記します。

<?php
namespace Config;

use CodeIgniter\Config\Services as CoreServices;
use Psr\Log\LoggerInterface;

class Services extends CoreService
{
	public static function renderer(string $viewPath = null, $config = null, bool $getShared = true)
	{
		if ($getShared)
		{
			return static::getSharedInstance('renderer', $viewPath, $config);
		}

		if (is_null($config))
		{
			$config = new \Config\View();
		}

		if (is_null($viewPath))
		{
			$paths = config('Paths');

			$viewPath = $paths->viewDirectory;
		}

		return new \App\Core\View\View($config, $viewPath, static::locator(), CI_DEBUG, static::logger());
	}
}

どこが変わったかお分かりでしょうか?
returnの際にインスタンス化するViewクラスを『\CodeIgniter\View\View』から『\App\Core\View\View』に変更しました。
これで『layouts/default/layout.php』などのViewsディレクトリのファイルで新しいViewクラスに追加したプロパティが呼び出しできるようになりました。
新しいViewクラスに追加したプロパティをビュー内で呼び出すには擬似変数『$this』を使用します。

<html>
<head>
<title><?php echo $this->page_title; ?></title>
<meta name="description" content="<?php echo $this->page_description; ?>">
<meta name="robots" content="<?php echo $this->page_robots; ?>">

...

どうでしょう?うまくいきましたか?
毎回コントローラーから変数を渡してもいいのですが、筆者的にはこの方がタイトル等の値の渡し忘れがなく、最悪渡し忘れていたとしてもデフォルト値が表示されますのでこのような拡張を行ってみました。

おわりに

今回は公式ドキュメントや他のサイト様ではあまり解説していない部分について解説しました。
本記事を通じてCodeIgniter拡張について少しご理解頂けたのではないでしょうか?
筆者のようにフレームワークのソースコードを隅から隅まで読みあさる気力のある方には不要な解説だったかもしれませんが、CI初心者の方のお役にたてて利用者が増えればいいなとの思いから執筆しました。
CIはシンプルが故に、完全に拡張せずに使うケースは少ないことかと思います。
メンテナンス性の悪いオレオレフレームワークになりすぎない程度に皆さんもどんどん拡張していって頂ければと思います。

CodeIgniterを使用したプロジェクトで行き詰まったり、軽快なフルスクラッチCMS・業務システム制作などご検討でしたらお気軽にお問い合わせください。