コンポーネントとは
CakePHPには「コンポーネント」と呼ばれる機構があります。CakePHPにおけるコンポーネントとは何かという定義は難しいのですが、CakePHP プログラマーズ リファレンスガイド の定義が短く的確なので引用させていただきます。
コンポーネントというのは、( コントローラ間で)共有できる小さな“ コントローラレット” だ。
9章 コンポーネント より
たとえば1つのコントローラ内のコードで重複する箇所が発生するとき、コントローラ内のメソッドとして定義して再利用することがあります。この再利用の範囲である「コントローラ内」を「別々のコントローラ間」まで広げてくれるのが、コンポーネントです。
コンポーネントを使うべきか否かはプログラマの裁量次第です。あるコードをコンポーネントに移すことで全体の開発効率が良くなると感じれば、やってみてください。
本記事ではCakePHP1.1系でのコンポーネントの作り方を解説します。
コンポーネントファイルの仕様
設置場所
コンポーネントは通常、1コンポーネント1ファイルで記述します。ファイルは app/controllers/components/ディレクトリ内に設置します。componentsディレクトリがなければ、自分で作成してかまいません。app/controllers/components/ ディレクトリ内にはコンポーネントファイルのみが格納されます。
クラス名・ファイル名の命名規則
コンポーネントは1つのクラスとして定義します。クラス名は「コンポーネント名のCamelCase(単語の頭文字が大文字)+Component」です。ファイル名はコンポーネント名を小文字で、単語をアンダースコアで区切り、拡張子をphpとします。
たとえば「GihyoCommon」コンポーネントは、以下のようになります。
GihyoCommonComponentクラス
app/controllers/components/gihyo_common.php
クラス定義
GihyoCommonコンポーネントのクラス定義は以下のようになります。CakePHPコアで定義されたObjectクラスを継承します。
<?php
class GihyoCommonComponent extends Object {
}
このクラスに自由にプロパティやメソッドを作成してコントローラから呼び出すことができます。
コンポーネントを作ってみよう
では、GihyoCommonコンポーネントとして、gihyo.jp のRSSを取得する機能を実装してみます。
app/controllers/components/gihyo_common.php
<?php
class GihyoCommonComponent extends Object {
var $_index_rss_url = 'http://rss.rssad.jp/rss/gihyo/feed/rss2';
function getIndexRSS() {
return file_get_contents($this->_index_rss_url);
}
}
注: このサンプルはphpのsafe_modeが有効だとエラーになります
このコンポーネントをコントローラから使用する例が以下になります。
<?php
class ExampleController extends AppController {
var $uses = array();
var $autoRender = false;
var $components = array('GihyoCommon');
function index() {
echo $this->GihyoCommon->getIndexRss();
}
}
コンポーネントの使用において直接的に関係する箇所は以下の2ヵ所です。
var $components = array('GihyoCommon');
GihyoCommonの使用を定義しています。
使用したいコンポーネントがあるとき、コントローラの$componentsプロパティにコンポーネント名の文字列を配列で定義します。
echo $this->GihyoCommon->getIndexRss();
GihyoCommonコンポーネントのgetIndexRssメソッドを呼び出しています。
コンポーネントはコントローラのインスタンス内に、コンポーネント名と同じ名前のプロパティとして定義されるので、$thisから参照してメソッドなどを呼び出すことができます。
コンポーネントからモデルを使用したいときは
コンポーネントからはコントローラで定義したモデルなどはそのままでは使用することができません。コンポーネント内での $this は、コンポーネントのインスタンスへの参照です。コントローラ内に定義されるモデルのプロパティは存在しません。
コンポーネント内でモデルを使用するには、モデルのインスタンスを新たに作成するか、コンポーネントの初期化時にコントローラの参照を受け取ってコントローラ経由で使用する方法の2通りがあります。
最も簡単な方法は、モデルのインスタンスを新たに作成する方法です。
<?php
class GihyoCommonComponent extends Object {
function getUsers() {
$user =& new User();
return $user->findAll();
}
}
この例では、$user はUserモデルのインスタンスです。
ちなみに、コントローラ側でUserモデルを利用できる状態でないときにコンポーネント内で new User()を行うと、「 Fatal error: Class 'User' not found」というFatal Errorが発生してしまいます。使用したいモデルのファイルが読み込まれておらず、クラスが定義されていないためです。
ということで、モデルはそもそもコントローラ側で定義されていることが前提となります。そこで、モデルの状態のことを考慮する必要はありますが、コントローラを経由して参照する方法を紹介いたします。
事前準備として、コンポーネントの初期化時に呼び出されるstartupメソッドによってコントローラのインスタンスへの参照を受け取って保持します。そして保持したコントローラを経由してモデルを参照する簡単な例が、以下になります。
<?php
class GihyoCommonComponent extends Object {
var $_controller;
function startup(& $controller) {
$this->_controller = $controller;
}
function getUsers() {
return $this->_controller->User->findAll();
}
}
モデル以外にもコントローラのプロパティに値をセットしたりもできるので、他にも活用方法があります。ただしコンポーネントからコントローラの状態を変更したりするのは、アプリケーションの見通しに悪影響を及ぼす可能性が高いです。個人的には、よっぽどの効率向上が期待される場合以外は、コントローラの状態変更は控えておいたほうが良いと思います。
なお、これらのコンポーネント例は解説用にシンプルさが優先されているため、コンポーネント化するほどではありません。この程度であれば、getUsersメソッドの機能はUserモデルに定義すべきです。コンポーネントからモデルを使用するのは、もっと複雑な要件があるときにしたほうが良いでしょう。
コンポーネントをうまく使えると、ある程度規模の大きいアプリケーションの見通しがかなり良くなりますので、ぜひ、使用してみてください。