CakePHP

CakePHP ヘルパーの出力を echo を使わずに出力する方法

2

※この投稿の方法はバージョン1.1 までしか使用できないようです。1.2 のソースを確認したところ、$html->input の$return パラメータはなくなっていました。/app/config/core.php に AUTO_OUTPUT という定数もなくなっていました。ご注意ください。

Cakephp のヘルパーを使用していて
<?php echo $html->input('Model/filed', array()); ?>と書くところを
<?php $html->input('Model/field', array()); ?>と echo を使用せずに出力するには
/app/config/core.php
AUTO_OUTPUT = true;にすれば、自動的に出力するようになります。

私はつい echo を書き忘れてしまうので助かります。

ヘルパーのメソッドの $return パラメータを true にするとこの AUTO_OUTPUT を無効にすることもできます。
ただ、各メソッドによって、$return パラメータの位置がバラバラなので、書くときにはヘルパーのソースを確認する必要があるかと思います。

ちなにみに input の場合は
<?php $html->input('Model/field', array(), true); ?>とすると自動出力を抑止できますので、上記は何も表示されなくなります。

CakePHP 日本語マニュアル
10章 ヘルパー

CakePHP アソシエーションのまとめ(4)

0

まとめ(1) では1対1のアソシエーション hasOne でしたが、
今回は1対多のアソシエーション hasMany です。

例えば、一人のユーザに複数のコメント投稿があるような場合です。
データベースは

CREATE TABLE users(
    id int unsigned auto_increment primary key,
    nickname varchar(30) NOT NULL,
    created datetime default NULL,
    modified datetime default NULL
);

CREATE TABLE posts(
    id int unsigned auto_increment primary key,
    user_id int unsigned unique,
    comment text,
    created datetime default NULL,
    modified datetime default NULL
);

のような場合、アソシエーションの定義は下記のようになります。

< ?php
class User extends AppModel
{
    var $name = 'User';
    var $hasMany = array('Post' =>
                    array('className' => 'Post',
                          'conditions' => '',
                          'order' => '',
                          'foreignKey' => 'user_id',
                          'dependent' => true,
                          'exclusive' => false,
                          'finderQuery' => ''
                    )
                  );
}
?>

これで、
$this->User->findById($id);
とすると下記のようにデータを取得できます。

Array
(
    [User] => Array
        (
            [id] => 1
            [nickname] => boze
        )
    [Post] => Array
        (
            [0] => Array
                (
                    [id] => 1
                    [user_id] => 1
                    [comment] => コメント1です
                )
            [1] => Array
                (
                    [id] => 1
                    [user_id] => 1
                    [comment] => コメント2です
                )
            ....
        )
);

また、hasOne などと同様に CakePHP の命名規約に従っていれば、foreignKey は指定しなくても大丈夫ですので、最低限
var $hasMany = array('Post');
でもアソシエーションを定義できます。

また、hasMany で指定する配列の意味は下記の通りです。

className
関連付けたいモデルのクラス名

conditions
hasMany で取得したいデータの条件を指定する。 SQL の条件文。

order
関連するモデルのデータの並び順。SQL の ORDER 句の指定方法。
Post.created DESC
テーブル名をカラム名の前に付けないと、User.created と区別できません。

limit
Cake が取り出す関連モデルのデータの最大数。
‘limit’ => 5 のように数字で指定。

foreignKey
関連しているモデルを指している外部キーの名前。
Cake の命名規約に従っている場合は省略可能。従っていない場合はここで指定する。

dependent
true に設定すると、モデルのデータの削除時に関連しているモデル側のデータも削除される。

exclusive
true に設定すると、関連しているすべてのオブジェクトが一つの SQL ステートメントで削除される。

finderQuery
アソシエーションを取り出すために、完全な SQL ステートメントを指定する。

CakePHP 日本語マニュアル
6.4. アソシエーション

CakePHP アソシエーションのまとめ(3)

1

今回は belongsTo です。
CakePHP アソシエーションのまとめ(1) で hasOne をやりましたが、belongsTo はその逆です。

まとめ(1) で使用したテーブルで説明すると、Profile モデルにUser モデルを関連付けるのが belongsTo になります。

profile.php

<?php
class Profile extends AppModel {
    var $name = 'Profile';
    var $belongsTo = array('User' =>
                        array('className'  => 'User',
                              'conditions' => '',
                              'order'      => '',
                              'foreignKey' => 'user_id'
                        )
                      );
}
?>

配列のそれぞれの意味は CakePHP アソシエーションのまとめ(2) に説明があります。

これで、
$this->profile->findById($id);
とかすると、Profile モデルの該当するID のデータ内容とそのProfile モデルの元の User モデルのデータが取得できます。

もちろん、CakePHP の命名規則に従っていれば、

profile.php

<?php
class Profile extends AppModel {
    var $name = 'Profile';
    var $belongsTo = array('User');
}

?>

でもいけます。

6.4. アソシエーション

CakePHP アソシエーションのまとめ(2)

0

アソシエーションのまとめ(1) で書いた

< ?php
class User extends AppModel
{
    var $name = 'User';
    var $hasOne = array('Profile' =>
                    array('className' => 'Profile',
                          'conditions' => '',
                          'order' => '',
                          'dependent' => true,
                          'foreignKey' => 'user_id'
                    )
                  );
}
?>

この部分の補足です。
それぞれの意味するところは

var $hasOne = array('関連先のモデル名'=>
                array('className'  => '関連先のモデルのクラス名',
                      'conditions' => '関連を定義するSQL条件の一部',
                      'order'      => '関連先のデータの並び順',
                      'dependent'  => '関連先データの同時削除',
                      'foreignKey' => '関連先テーブル側の外部キー'
                )
              );

conditions は条件を付けて関連付けるモデルのデータを制限するときに指定する。
dependent は関連元のデータが削除されたときに、一緒に関連先のデータも削除するかどうか。
関連先のモデルで関連元のモデルの主キーを定義しているカラム名です。

dependent などはかなり便利に使えそうですね。
アソシエーションのまとめ(1) に書いたように、 foreignKey は CakePHP の命名規則に従っていれば省略可能です。

CakePHP アソシエーションのまとめ(1)

1

CakePHP のアソシエーションのまとめです。
タイトルに (1) とつけたので多分何回か続けます。

アソシエーションとはモデル間のつながりのことです。

例えば、下記のような2つのテーブルがあると

CREATE TABLE users(
    id int unsigned auto_increment primary key,
    nickname varchar(30) NOT NULL,
    created datetime default NULL,
    modified datetime default NULL
);

CREATE TABLE profiles(
    id int unsigned auto_increment primary key,
    user_id int unsigned unique,
    blog_url varchar(256),
    hobby varchar(256),
    created datetime default NULL,
    modified datetime default NULL
);

CakePHP では2つのモデルになります。
User モデルと Profile モデルです。
この2つのモデルは1対1の関係になります。
「一人のユーザは1つのプロファイルを持つ」とういことです。

この2つのモデルの関係を CakePHP に指示するには次のようになります。

< ?php
class User extends AppModel
{
    var $name = 'User';
    var $hasOne = array('Profile' =>
                    array('className' => 'Profile',
                          'conditions' => '',
                          'order' => '',
                          'dependent' => true,
                          'foreignKey' => 'user_id'
                    )
                  );
}
?>

これで、
$this->User->findById($id);とすることにより、

Array
(
    [User] => Array
        (
            [id] => 1
            [nickname] => boze
        )
    [Profile] => Array
        (
            [id] => 1
            [blog_url] => http://www.syuhari.jp/blog
            [hobby] => 読書
        )
);

のようにUser モデルだけでなく、Profileモデルの情報も同時に取得することができます。

CakePHP の命名規則を守っていれば、
var $hasOne = array('Profile');だけでもいけるようです。
※外部キーはモデル名+_id

CakePHP 日本語マニュアル
6.4. アソシエーション

PHPフレームワークのベンチマーク

0

PHPベンチマーク: Zend Framework vs Symfony vs CakePHP vs CodeIgniter vs PHP on TRAX – 徒然なるままにBlog

  • Zend-Framework
  • Symfony
  • CakePHP
  • Codelgniter
  • PHP on TRAX

のベンチマーク結果が載っています。

ただ、

モデル作成を行わないはずなのにCakePHPだけモデルが生成されるようになっていました。
PHPベンチマーク CakePHPでモデルを使用しない | Shin x blog

なので、是非CakePHP もモデル使用しないソースで実行した結果を見てみたいですね。

実行環境が元エントリとは異なるので絶対的な数値にはあまり意味が無いですが、両者を比べるとモデル生成なしの方が20%ほど速いようです。
PHPベンチマーク CakePHPでモデルを使用しない | Shin x blog

モデル使用しないと20% ほど早いそうです。

CakePHP HtmlHelper::tagValue のHTMLエスケープ処理

1

view でモデルの値などを出力する際に
$html->tagValue("Model/hoge");
とすれと、HTMLエスケープ処理をしてくれるので便利です。

ただ、
CakePHP 1.1.15.5144以降はHtmlHelper#tagValueに注意 | Shin x blog
に書かれているように、CakePHP のバージョン1.1.15.5144 からエスケープするかのフラグが追加されたようです。

以前はエスケープしないを選択できなかったので、それはそれで不便な点もありました。
MOONGIFT ブログ ? CakePHP覚書(HTML Helpler)

しかし今回の変更で注意しなければいけないのは、デフォルトでエスケープされていたのがデフォルトでエスケープ処理されなくなったことです。

cake/libs/view/helpers/html.php
function tagValue($fieldName, $escape = false)

以前のようにエスケープ処理させるには
$html->tagValue("Model/hoge", true);
とする必要があります。

また、

ちなみに1.2.0.5147ではtagValueがdeprecatedになっており、代わりにvalueメソッドを使うようになるようです。

ということになるようです。

CakePHP flash メソッドでレイアウトを指定する

2

コントローラ毎にレイアウトを変えていていると flash メソッドは使えないなあと思っていたのですが、下記のようにすることによりレイアウトをコントローラで指定できますね。

/app/app_controller.php に下記の flash メソッドを追加

function flash($message, $url, $pause = 1) {
    $this->autoRender = false;
    $this->autoLayout = false;
    $this->set('url', $this->base . $url);
    $this->set('message', $message);
    $this->set('pause', $pause);
    $this->set('page_title', $message);

    if (file_exists(VIEWS . 'layouts' . DS . $this->layout.'_flash.thtml')) {
        $flash = VIEWS . 'layouts' . DS . $this->layout.'_flash.thtml';
    } elseif ($flash = fileExistsInPath(LIBS . 'view' . DS . 'templates' . DS . "layouts" . DS . 'flash.thtml')) {
    }
    $this->render(null, false, $flash);
}

これで、
/app/views/layouts/指定したレイアウト名_flash.thtml
というレイアウトファイルがあれば、それを使用して
なければ、デフォルトの flash.thtml を使用します。

やっていることはすごく単純でコントローラクラスの flash メソッドをオーバライドして、使用するレイアウトファイルを選択するところで

if (file_exists(VIEWS . 'layouts' . DS . 'flash.thtml')) {
    $flash = VIEWS . 'layouts' . DS . 'flash.thtml';
}

となっていたところを

if (file_exists(VIEWS . 'layouts' . DS . $this->layout.'_flash.thtml')) {
    $flash = VIEWS . 'layouts' . DS . $this->layout.'_flash.thtml';
}

に変更しただけです。

他にいい方法があれば教えてください。

CakePHP コントローラのflash メソッド

2

データを更新した後などに「更新しました」などのメッセージを簡単に出せるのがコントローラのflash メソッド。
コントローラで、
$this->flash('更新しました', '/users');
などとすることにより、メッセージを簡単に出せる。
2つ目の引数は飛び先URL を指定できる。

ちなみにこの flash メソッドは下記のようになっています。
/cake/libs/controller/controller.php

function flash($message, $url, $pause = 1) {
    $this->autoRender = false;
    $this->autoLayout = false;
    $this->set('url', $this->base . $url);
    $this->set('message', $message);
    $this->set('pause', $pause);
    $this->set('page_title', $message);

    if (file_exists(VIEWS . 'layouts' . DS . 'flash.thtml')) {
        $flash = VIEWS . 'layouts' . DS . 'flash.thtml';
    } elseif ($flash = fileExistsInPath(LIBS . 'view' . DS . 'templates' . DS . "layouts" . DS . 'flash.thtml')) {
    }
    $this->render(null, false, $flash);
}

上のソースを見ると分かるように、この flash メソッドのレイアウトは
$layout = "hoge";
とか指定しても指定したレイアウトでは表示されません。

flash メソッドでは flash.thtml というレイアウトを使用することしかできません。
/app/views/layouts/flash.thtml
を探してなければ、
/cake/libs/view/templates/layouts/flash.thtml
のデフォルトのレイアウトを使用します。

また、デフォルトの flash.thtml は

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title><?php echo $page_title?></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<?php if(Configure::read() == 0) { ?>
<meta http-equiv="Refresh" content="<?php echo $pause?>;url=<?php echo $url?>"/>
<?php } ?>
<style><!--
P { text-align:center; font:bold 1.1em sans-serif }
A { color:#444; text-decoration:none }
A:HOVER { text-decoration: underline; color:#44E }
--></style>
</head>
<body>
<p><a href="<?php echo $url?>"><?php echo $message?></a></p>
</body>
</html>

のようになっていて、
DEBUG モードで DEBUG = 0 を指定していると meta タグを利用して勝手に画面遷移します。DEBUG モードが 0 以外の場合はメッセージをクリックすると指定したURLに画面遷移します。

非常にシンプルなレイアウトですので、使用するときは、
/app/views/layouts/flash.thtml
にコピーしてカスタマイズした方がよいでしょう。

CakePHP ヘルパーからコンポーネントへのアクセス方法

1

ヘルパーからコンポーネントへのアクセス方法が分からず
教えていただいたのでメモ。

例えば HogeComponent にアクセスしたいヘルパーに
$this->view->controller->HogeComponent->method();というように書けば、HogeComponent の method にアクセスできる。

これで、ヘルパーがかなり使いやすくなりました。

Go to Top