CakePHP 便利な組み込みビヘイビア『ContainableBehavior』の使い方

cakePHPは、複数テーブルの結合を簡単に実現できます。
が、何も指定せずに『find』でデータを取り出すと、結合しているテーブルのデータも大量に取り出すことになります。

「このテーブルは、1つのフィールドからデータを取り出せればいいのに・・・」って時に活躍してくれるのが『ContainableBehavior』です。

取り出したいフィールドや、条件に一致する場合のみデータを取得なんてことが簡単にできるようになります。
そんな便利なビヘイビア『ContainableBehavior』の使い方を、簡単なブログ(?)みたいなシステムを例にして紹介したいと思います。

(モデルでの使用を例にしますが、コントローラーから使用する方法も基本的に同じです。)


▼簡単なブログシステムの構造

このブログシステムには以下の3つのテーブルがあります。

  • 【Post】
    ブログに投稿された記事を管理

  • 【User】
    ブログ利用者を管理

  • 【Comment】
    ブログに投稿された記事に対するコメントを管理

各テーブルの構造は、以下の通りです。

▼Postテーブル

`id` int(11) NOT NULL auto_increment,
`user_id` int(11) default NULL,
`mail` varchar(255) default NULL,
`title` varchar(50) default NULL,
`text` text,
`created` datetime default NULL,
`modified` datetime default NULL,
PRIMARY KEY  (`id`)

▼Userテーブル

`id` int(11) NOT NULL auto_increment,
`alias` varchar(50) default NULL,
`username` varchar(50) default NULL,
`password` varchar(70) default NULL,
`created` date default NULL,
`modified` date NOT NULL,
PRIMARY KEY  (`id`)

▼Commentテーブル

`id` int(11) NOT NULL auto_increment,
`post_id` int(11) default NULL,
`user_id` int(11) default NULL,
`mail` varchar(255) default NULL,
`title` varchar(50) default NULL,
`text` text,
`created` datetime default NULL,
`modified` datetime default NULL,
PRIMARY KEY  (`id`)

記事に対してコメントが連なる感じです。

あくまで『ContainableBehavior』の使い方を紹介するためだけに用意したシステムなので、細かい突っ込みなどは無しでよろしくお願いします・・・


1・準備

『ContainableBehavior』を使用するためには、モデルのメンバ変数『$actsAs』に『Containable』を追加します。

class Post extends AppModel {
    public $name   = 'Post';
    public $actsAs = array('Containable');
}

2・使い方

何も指定せず『find』などでデータを取得すると、以下のように大量のデータを取得してしまいます。

▼何も指定せず『find』した場合

Array (
    [0] => Array (
        [Post] => Array (
            [id] => 1
            [user_id] => 1
            [mail] => info@yasigani-ni.com
            [title] => ブログはじめました
            [text] => 初めてのブログ投稿です。
            [created] => 2011-10-8 18:00:00
            [modified] => 2011-10-08 18:00:00
        )

        [User] => Array (
            [id] => 1
            [alias] => ユーザー01
            [username] => test
            [password] => **********************
            [created] => 2011-10-8 18:00:00
            [modified] => 2011-10-08 18:00:00
        )

        [Comment] => Array (
            [0] => Array (
                [id] => 1
                [post_id] => 1
                [user_id] => 2
                [mail] => test01@yasigani-ni.com
                [title] => コメント
                [text] => 初めてのコメント投稿です。
		[created] => 2011-10-8 18:10:00
		[modified] => 2011-10-08 18:10:00
            )

            [1] => Array (
                [id] => 2
                [post_id] => 1
                [user_id] => 3
                [mail] => test02@yasigani-ni.com
                [title] => コメント
                [text] => 2つめのコメント投稿です。
		[created] => 2011-10-8 18:10:00
		[modified] => 2011-10-08 18:10:00
            )
        )
    )

    [1] => Array (
        [Post] => Array
	    ・・・

これを『Userテーブルからは、aliasだけを取得する』場合、以下のようにします。

▼Postモデルで指定する場合

public function find($type, $options = array()) {
    $data = parent::find($type, array_merge($options, array('contain' => array('User.alias'))));
    return $data;
}

データを取得する際に、オプションに『contain』に取得したいフィールド名を指定するだけです。
これでデータを取得すると、以下のようなデータになります。

▼結果

Array (
    [0] => Array (
        [Post] => Array (
            [id] => 1
            [user_id] => 1
            [mail] => info@yasigani-ni.com
            [title] => ブログはじめました
            [text] => 初めてのブログ投稿です。
            [created] => 2011-10-8 18:00:00
            [modified] => 2011-10-08 18:00:00
        )

        [User] => Array (
            [alias] => ユーザー01
        )

        [Comment] => Array (
            [0] => Array (
                [id] => 1
                [post_id] => 1
                [user_id] => 2
                [mail] => test01@yasigani-ni.com
                [title] => コメント
                [text] => 初めてのコメント投稿です。
		[created] => 2011-10-8 18:10:00
		[modified] => 2011-10-08 18:10:00
            )

            [1] => Array (
                [id] => 2
                [post_id] => 1
                [user_id] => 3
                [mail] => test02@yasigani-ni.com
                [title] => コメント
                [text] => 2つめのコメント投稿です。
		[created] => 2011-10-8 18:10:00
		[modified] => 2011-10-08 18:10:00
            )
        )
    )

    [1] => Array (
        [Post] => Array
	    ・・・

『User』からは、『alias』だけを取得しているのが分かると思います。

「これなら

『$this->Post->find('all', array('fields' => array('User.alias')));』

でいいんじゃない?」
と、お思いの方。

『ContainableBehavior』の実力は、こんなものじゃありません。

連結しているモデルの条件を指定したり、連結が複雑なモデルの場合などで力を発揮します。
単純に『recursive』の指定をしないで済むだけでも、とても楽になると思います。

簡単な条件を指定するには、以下のようにします。

▼簡単な条件指定

public function find($type, $options = array()) {
    $data = parent::find($type, array_merge($options, array('contain' => array('Comment.id = 1'))));
    return $data;
}

これで、idが1のCommentだけ取得することができます。
連結が複雑なモデルの場合でも、取得するフィールドを指定するだけならとても簡単です。
Commentが連結しているUserテーブルのaliasを取得する場合は、以下のようにします。

▼連結が複雑なモデルで取得するフィールドを指定

public function find($type, $options = array()) {
    $data = parent::find($type, array_merge($options, array('contain' => array('Comment.User.alias'))));
    return $data;
}

Commentには、idが1のCommentだけを取得。Userからはaliasを取得したい場合は以下のようにします。

▼条件指定と取得するフィールドの指定

parent::find('all', array_merge($options, array('contain' => array(
                'Comment' => array(
                    'conditions' => array('Comment.id = ?' => 1),
                ),

                'User' => array(
                    'fields' => array('User.alias'),
                ),
            )
        )
    )
);

通常のfindを使う感覚で、簡単に条件指定することができます。
連結が複雑なモデルの場合のは以下のようになります。
例えば、『Commentはtitleと、連結しているUserからaliasを取得する』『Postと連結しているUserからはaliasを取得』したい時は、

parent::find('all', array_merge($options, array('contain' => array(
                'Comment' => array(
                    'fields' => array('Comment.title'),
                    'User' => array(
                        'fields' => array('User.alias')
                    ),
                ),

                'User' => array(
                    'fields' => array('User.alias'),
                ),
            )
        )
    )
);

といった感じになります。


最後に

連結が複雑なモデルでも、取得するフィールドの指定だけならとても簡単です。
複雑な指定や連結が複雑なモデルだと配列だらけで読みにくくなってしまいますが、連結を意識したインデントなどで対処できると思います。

『ContainableBehavior』をまだ使用したことがなく、私の説明を読んだら余計に混乱した方や、難しそうと思った方。
私の文章力が悪いだけなので、だまされたと思って一度使用してみてください。
すごい楽に開発できるようになると思います。

マニュアルはこちらから

6.2 コンテイナブル

CakePHP まとめて『hidden』要素を出力する簡単なヘルパーを作ってみる

コンポーネントとビヘイビアの作成をしたので、ヘルパーの作り方も紹介しちゃいます。

ヘルパーは、呼んで字のごとく「ビューのコーディングの手助けをする」なので、「フォームから渡されたデータを『hidden』要素にする」ヘルパーを作りたいと思います。
ひとつずつ『hidden』を書くより一気に出力できたほうが楽ですからね^^

ヘルパーは、『AppHelper』クラスを継承して作成します。

▼『AppHelper』クラスを継承

class FormArrHelper extends AppHelper {
    public $name = 'FormArr';
}

あとはいつも通り、CakePHPの命名規則に従い、『/app/views/helpers』に保存します。
これでヘルパーとして使用できるようになります。

準備ができたところで、メソッドの実装をします。
ヘルパー内から他のヘルパーを使用することができるので、『hidden』要素の生成自体は『Formヘルパー』のメソッドを使用しています。
注意点は、ヘルパー内で他のヘルパーを使用するときは、『$this->ヘルパー名』となるぐらいです。

▼作成したヘルパーにメソッドを実装する

class FormArrHelper extends AppHelper {
    public $name    = 'FormArr';
    public $helpers = array('Form');

    /**
     *    渡されたフォームデータの全てをhiddenにした文字列を生成する
     *
     *    @return    hiddenの文字列
     */
    public function allHidden() {
        return $this->_arrHidden($this->data);
    }

    /**
     *    渡されたフォームデータの指定されたデータをhiddenにした文字列を生成する
     *
     *    @param     string    指定したいデータまでのキーを『.(ドット)』でつなげた文字列
     *
     *    @return    hiddenの文字列
     */
    public function givenHidden($keyName) {
        $nameArr = explode('.', $keyName);

        if(count($nameArr) == 2) {
            $givenData[$nameArr[0]][$nameArr[1]] = $this->data[$nameArr[0]][$nameArr[1]];
        } else {
            $givenData[$nameArr[0]] = $this->data[$nameArr[0]];
        }

        return $this->_arrHidden($givenData);
    }

    /**
     *	渡された配列を元にhiddenを生成する
     *
     *    @param     array    hiddenを生成したい配列
     *
     *    @return    hiddenの文字列
     */
    protected function _arrHidden($arr) {
        if(!is_array($arr)) return;

        $hdn = null;

        foreach($arr as $fKey => $fItem) {
            foreach($fItem as $sKey => $sItem) {
                if(is_array($sItem)) {
                    // 配列データならもう一度foreach
                    foreach($sItem as $tKey => $tItem) {
                        // hidden要素の作成
                        $hdn .= $this->Form->hidden("$fKey.$sKey.$tKey");
                    }
                } else {
                    // 配列データじゃなければ、そのままhidden要素の作成
                    $hdn .= $this->Form->hidden("$fKey.$sKey");
                }
            }
        }

        return $hdn;
    }
}

あとは、コントローラーでメンバ変数『$helpers』に作成したヘルパーを指定するだけで使用することができます。

▼コントローラー

class PostsController extends AppController {
    public $name  = 'Posts';
    public $users = array('Post');
    public $helpers = array('FormArr');

    public function index() {
        /*
         *    処理は省略します
         */
    }
}

▼ビュー

<?php echo $formArr->allHidden(); ?>

▼出力結果

<input id="PostText" type="hidden" value="test" name="data[Post][text]">
<input id="PostRadio" type="hidden" value="radio3" name="data[Post][radio]">
<input id="PostCheckbox1" type="hidden" value="1" name="data[Post][checkbox][1]">
<input id="PostCheckbox2" type="hidden" value="2" name="data[Post][checkbox][2]">
<input id="PostCheckbox3" type="hidden" value="0" name="data[Post][checkbox][3]">

配列のデータもちゃんと『hidden』要素になっていますので、通常のフォームなら何も意識せずに『$formArr->allHidden();』を使用するだけで『hidden』要素を出力することができます。

以上が、『まとめてhidden要素を出力する簡単なヘルパー』の作り方でした。

CakePHP 『App:import』について

CakePHPでは、『App:import』を使用することで、パスを意識せずにクラスを読みこむことができます。
よく使う処理をまとめたクラスなどを『vendors』に入れておき、『App:import』で読み込んだりすると楽になるみたいです。

マニュアルによると、「App::import()は次のことを保証します。クラスが1度しかロードされないこと。適切な親クラスがロードされること。そして数多のケースで自動的にパスを解決することです。『App::import』は、ファイルを『require』で読み込むことと同じです。その後にクラスを初期化する必要があるということを意識しておいてください。」らしいです。

当たり前といえば当たり前ですが、使用したい場合はインスタンス化を忘れないって事ですね。


コアライブラリ

App::import(‘Core’, ‘インポートしたいコアライブラリ名’)


コントローラ

App::import(‘Controller’, ‘インポートしたいコントローラー名’)


モデル

App::import(‘Model’, ‘インポートしたいモデル名’)


コンポーネント

App::import(‘Component’, ‘インポートしたいコンポーネント名’)


ビヘイビア

App::import(‘Behavior’, ‘インポートしたいビヘイビア名’)


ヘルパー

App::import(‘Helper’, ‘インポートしたいヘルパー名’)


プラグイン

プラグインについては、少し他とは違います。
マニュアルには、「プラグインのクラスを読み込むのは、appとコアのクラスを読み込むのとほぼ同じです。しかし、読み込み元のプラグイン名を指定してください。」とあります。

例1・Commentモデルの読み込み

App::import(‘Model’, ‘PluginName.Comment’);

例2・『APP/plugins/plugin_name/vendors/flickr/flickr.php』 の読み込み

App::import(‘Vendor’, ‘PluginName.flickr/flickr’);


Vendor ファイル

Vendor ファイルも、他とは少し違います。
マニュアルを例に紹介していきます。

例1・『vendors/geshi.php』の読み込み

App::import(‘Vendor’, ‘geshi’);

例2・『vendors/flickr/flickr.php』 の読み込み

App::import(‘Vendor’, ‘flickr/flickr’);

例3・『vendors/some.name.php』 の読み込み

App::import(‘Vendor’, ‘SomeName’, array(‘file’ => ‘some.name.php’));

例4・『vendors/services/well.named.php』 の読み込み

App::import(‘Vendor’, ‘WellNamed’, array(‘file’ => ‘services’.DS.’well.named.php’));


マニュアルはこちらから

『3.4.4 App クラス』

CakePHP 簡単なビヘイビアを作ってみる

前回の記事、『CakePHP サニタイズでエスケープされた改行コードを元に戻す簡単なコンポーネントを作ってみる』で簡単なコンポーネントを作成したのですが、色々考えた結果「データの加工などの処理はモデルで行うべきだ」との考えに至ったので、前回作成したコンポーネントと同じ処理をするビヘイビアを作成してみたいと思います。

作成するビヘイビアのメソッドとしては、『サニタイズでエスケープされた改行コードを元に戻す』『HTMLエンティティを特殊文字に置き換える』です。

ビヘイビアは、『ModelBehavior』クラスを継承して作成します。

▼『ModelBehavior』クラスを継承

class HtmlDecodeBehavior extends ModelBehavior {
    public $name = 'HtmlDecode';
}

CakePHPの命名規則に則り、『/app/models/behaviors』に保存します。

ビヘイビアの準備ができたので、メソッドを追加していきます。

コンポーネントの作成との違いは、モデルから呼び出せるメソッドの『第1引数がモデルの参照になる』ことです。
private宣言やprotected宣言のメソッドは『第1引数がモデルの参照になる』ことはありません。

▼作成したビヘイビアにメソッドを追加

class HtmlDecodeBehavior extends ModelBehavior {
    public $name = 'HtmlDecode';

    // 置き換え対象のHTMLエンティティ
    protected $_patterns = array(
        'before' => array(
            "/¥&amp;/",    // 『&』
            "/¥&lt;/",     // 『<』
            "/¥&gt;/",     // 『>』
            "/¥&quot;/",   // 『'』
            "/¥&#39;/",    // 『"』
        ),

        'after' => array(
            "&",        // 『&amp;』
            "<",        // 『&lt;』
            ">",        // 『&gt;』
            "'",        // 『&quot;』
            '"',        // 『&#39;』
        )
    );

    /**
     *    HTMLエンティティを特殊文字に置き換える
     *
     *    @param     string or array    $data      HTMLエンティティを特殊文字に置き換えたい配列、または文字列
     *    @param     pool               $script    置き換え後の特殊文字がScriptタグになった場合、削除するなら『true』を指定
     *    @param     pool               $space     置き換え後の文字列からスペースを削除するなら『true』を指定
     *
     *    @return    string or array    処理後、引数に指定された形式で返す。
     */
    public function htmlCheck(&$model, $data, $script = true, $image = true, $space = true) {
        if(is_array($data)) {
            $cnvData = array();

            foreach($data as $key => $item) {
                if(isset($item)) {
                    // 特殊文字へ置き換え
                    $cnvData[$key] = $this->_htmlDecode($item);

                    // スクリプトタグの削除
                    if($script) $cnvData[$key] = Sanitize::stripScripts($cnvData[$key]);

                    // 画像の削除
                    if($image) $cnvData[$key] = Sanitize::stripImages($cnvData[$key]);

                    // スペースの削除
                    if($space) $cnvData[$key] = Sanitize::stripWhitespace($cnvData[$key]);
                }
            }
        } else {
            if(isset($data)) {
                // 特殊文字へ置き換え
                $cnvData = $this->_htmlDecode($data);

                // スクリプトタグの削除
                if($script) $cnvData = Sanitize::stripScripts($cnvData);

                // 画像の削除
                if($image) $cnvData = Sanitize::stripImages($cnvData);

                // スペースの削除
                if($space) $cnvData = Sanitize::stripWhitespace($cnvData);
            }
        }

        return $cnvData;
    }

    /**
     *    配列、または文字列に含まれる『///n』を『/n』に置き換える
     *
     *    @param     string or array     $data         『///n』を『/n』に置き換えたい配列、または文字列
     *    @param     pool                $conversion    置き換え後の『/n』を『
』タグに変更する場合、trueを指定 * * @return string or array 処理後、引数に指定された形式で返す。 * */
public function nlCheck(&$model, $data, $conversion = false) { if(is_array($data)) { $cnvData = array(); foreach($data as $key => $item) { if(isset($item)) { // 『/n』へ置き換え $cnvData[$key] = $this->_nlDecode($item); // 『
』へ置き換え
if($conversion) $cnvData[$key] = nl2br($cnvData[$key]); } } } else { if(isset($data)) { // 『/n』へ置き換え $cnvData = $this->_nlDecode($data); // 『
』へ置き換え
if($conversion) $cnvData = nl2br($cnvData); } } return $cnvData; } /** * HTMLエンティティを特殊文字に置き換える * * @param string $data 置き換えたい文字列 * * @return string 置き換え後の文字列 */ protected function _htmlDecode($data) { return preg_replace($this->_patterns['before'], $this->_patterns['after'], $data); } /** * 『///n』を『/n』に置き換える * * @param string $data 置き換えたい文字列 * * @return string 置き換え後の文字列 */ protected function _nlDecode($data) { return preg_replace('/\\\n/', "\n", $data); } }

内容は、前回作成したコンポーネントと同じで、『preg_replace』関数を使用し文字を置き換えていくだけです。

後は、モデルから作成したビヘイビアを読み込み処理をしていきます。
ビヘイビアを読み込む場合は、メンバ変数『$actsAs』に使用したいビヘイビア名を指定します。

注意しなければならないのが、ビヘイビア側の第1引数はモデルの参照になっていますが、モデル側では第1引数を省略し第2引数から指定していくことです。
また、コンポーネントの『$this->コンポーネント名->コンポーネントのメソッド名』とは違い、『$this->ビヘイビアのメソッド名』となります。

class HtmlDecodeBehavior extends ModelBehavior {
    public function nlCheck(&$model, $data, $conversion = false) {
        /*
         *    処理は省略します
         */
    }
}
class Post extends AppModel {
    public $name   = 'Post';
    public $actsAs = 'HtmlDecode';

    public function find($type, $options = array()) {
        /*
         *    処理は省略します
         */

        $data = $this->nlCheck($data, true);
    }

このことに注意しながら、実装していきます。

▼モデル

class Post extends AppModel {
    public $name   = 'Post';
    public $actsAs = 'HtmlDecode';

    public function find($type, $options = array()) {
        switch($type) {
            case 'show':
                // データの取得
                $data = parent::find('first', $options);

                $data['Post'] = $this->nlCheck($data['Post'], true);

                return $data;
            break;

            default:
                return parent::find($type, $options);
            break;
        }
    }
}

▼コントローラー

class PostsController extends AppController {
    public $name  = 'Posts';
    public $users = array('Post');

    public function show($id = null) {
        if (!$id) $this->referer($this->referer());

        // データの取得
        $post = $this->Post->find('show', array('conditions' => array('Post.id = ?' => $id)));

        $this->set('post', $post);
    }
}

これでビヘイビアの作成は終了です。
いくつか注意点はありますが、基本的には簡単に作成することができました。

CakePHP サニタイズでエスケープされた改行コードを元に戻す簡単なコンポーネントを作ってみる

『Sanitize::clean』でサニタイズを行うと、改行コードもエスケープされます。

このままでは、『textarea』要素に入力された改行がない状態になり、想像していた表示にならないことがあります。
下記の赤文字の部分がサニタイズ時にエスケープされた改行コードです。

▼サニタイズされた『textarea』要素のテキスト

登録時に『Sanitize::clean』を使用しサニタイズを行いました。\n\nこのテキストは確認用です。

このままでは『nl2br』関数も使用できないので、コンポーネント作成の練習も兼ねて、『\\\n』を『\n』に戻す簡単なコンポーネントを作りたいと思います。

(表示では『\n』ですが、実際はエスケープシーケンスがあるので『\\\n』の状態になっています。)


コンポーネントは、『Object』クラスを継承し作成します。

▼『Object』クラスを継承

class HtmlDecodeComponent extends Object {
    public $name = 'HtmlDecode';
}

これをCakePHPの規約どおりに、『html_decode.php』として『/app/controllers/components』ディレクトリに配置します。
これでコンポーネントの準備ができました。

次は、メソッドを追加していきます。
改行コードを元に戻すメソッドだけでは寂しいので、HTMLエンティティを特殊文字に置き換えるメソッドも書いてみました。

▼作成したコンポーネントにメソッドを追加

class HtmlDecodeCompnent extends Object {
    public $name = 'HtmlDecode';

    // 置き換え対象のHTMLエンティティ
    protected $_patterns = array(
        'before' => array(
            "/¥&/",    // 『&』
            "/¥</",     // 『<』
            "/¥>/",     // 『>』
            "/¥"/",   // 『"』
            "/¥'/",    // 『'』
        ),

        'after' => array(
            "&",    // 『&amp』
            "<",    // 『&lt;』
            ">",    // 『&gt;』
            "'",    // 『&quot;』
            '"',    // 『&#39;』
        )
    );

    /**
     *	HTMLエンティティを特殊文字に置き換える
     *
     *	@param    string or array     $data      HTMLエンティティを特殊文字に置き換えたい配列、または文字列
     *	@param    pool                $script    置き換え後の特殊文字がScriptタグになった場合、削除するなら『true』を指定
     *	@param    pool                $space     置き換え後の文字列からスペースを削除するなら『true』を指定
     *
     *	@return   string or array     $data      処理後、引数に指定された形式で返す。
     */
    public function htmlCheck($data, $script = true, $image = true, $space = true) {
        if(is_array($data)) {
            $cnvData = array();

            // 配列の処理
            foreach($data as $key => $item) {
                if(isset($item)) {
                    // 特殊文字へ置き換え
                    $cnvData[$key] = $this->_htmlDecode($item);

                    // スクリプトタグの削除
                    if($script) $cnvData[$key] = Sanitize::stripScripts($cnvData[$key]);

                    // 画像の削除
                    if($image) $cnvData[$key] = Sanitize::stripImages($cnvData[$key]);

                    // スペースの削除
                    if($space) $cnvData[$key] = Sanitize::stripWhitespace($cnvData[$key]);
                }
            }
        } else {
            if(isset($data)) {
                // 特殊文字へ置き換え
                $cnvData = $this->_htmlDecode($data);

                // スクリプトタグの削除
                if($script) $cnvData = Sanitize::stripScripts($cnvData);

                // 画像の削除
                if($image) $cnvData = Sanitize::stripImages($cnvData);

                // スペースの削除
                if($space) $cnvData = Sanitize::stripWhitespace($cnvData);
            }
        }

        return $cnvData;
    }

    /**
     *	配列、または文字列に含まれる『///n』を『/n』に置き換える
     *
     *	@param    string or array    $data         『///n』を『/n』に置き換えたい配列、または文字列
     *	@param    pool               $conversion    置き換え後の『/n』を『
』タグに変更する場合、trueを指定 * * @return string or array $data 処理後、引数に指定された形式で返す。 * */
public function nlCheck($data, $conversion = false) { if(is_array($data)) { $cnvData = array(); foreach($data as $key => $item) { if(isset($item)) { // 『/n』へ置き換え $cnvData[$key] = $this->_nlDecode($item); // 『
』へ置き換え
if($conversion) $cnvData[$key] = nl2br($cnvData[$key]); } } } else { if(isset($data)) { // 『/n』へ置き換え $cnvData = $this->_nlDecode($data); // 『
』へ置き換え
if($conversion) $cnvData = nl2br($cnvData); } } return $cnvData; } /** * HTMLエンティティを特殊文字に置き換える * * @param string $data 置き換えたい文字列 * * @return string 置き換え後の文字列 */ protected function _htmlDecode($data) { return preg_replace($this->_patterns['before'], $this->_patterns['after'], $data); } /** * 『///n』を『/n』に置き換える * * @param string $data 置き換えたい文字列 * * @return string 置き換え後の文字列 */ protected function _nlDecode($data) { return preg_replace('/\\\n/', "\n", $data); } }

『preg_replace』関数を使用し、正規表現で文字を置き換えていくだけのメソッドたちです。

あとは、コントローラーからコンポーネントを呼び出せば、他のコンポーネント同様に使用することができます。

▼コントローラー

class PostsController extends AppController {
    public $name  = 'Posts';
    public $users = array('Post');
    public $components = array('HtmlDecode');

    public function index() {
        /*
        処理は省略します・・・
        */

        $text = $this->HtmlDecode->nlCheck('登録時に『Sanitize::clean』を使用しサニタイズを行いました。\n\nこのテキストは確認用です。', true);
        $this->set('text', $text);
    }
}

▼ビュー

<?php
echo $text;
?>

▼結果

登録時に『Sanitize::clean』を使用しサニタイズを行いました。
<br />
<br />
<br />
このテキストは確認用です。

コンポーネントを作成するのもとっても簡単ですね。
さすがCakePHPって感じです。

ちなみに、今回はコンポーネントとして作成しましたが、モデルの段階で処理したほうがいいのか(ビヘイビア)迷いました。

ビューで処理するのは、『コントローラーから渡されたデータをビュー側でループや条件分岐以外の処理をして表示する』のは間違っていると思うので止めましたが・・・

もっとMVCの理解を深めないといけないですね^^;


※記事を投稿後、『CakePHPを使ったMVC設計のベストプラクティス』を見て、モデルの段階で処理したほうがいい気がしてきました。
今回作成したコンポーネントと同じ処理を行うビヘイビアを作成しようと思います。