CakePHPの最近のブログ記事
スクリプト内に以下記述を行うことでロケールの変更が可能。
Configure::write('Config.language', 'ja');
参考サイトの記述を元に bootstrap.php に
define('DEFAULT_LANGUAGE', 'en');
とかしても上手くいかないので、そんな時は手動で設定すればよい。
このサイトを参考に
extract コマンドで po ファイルを生成するところまではいけたけど、
言語の切り替えが上手くいかなくてハマリ中。
どうやっても日本語になってしまう。
まあいいけど。
あと、最初に生成されるファイルが default.pot で、それをそのまま
local/jpn/LC_MESSAGES にコピーしたら動かなくてハマった。
default.pot → default.po に改名しなきゃなのね。
※僕の cake は 1.2.0.5427alpha。
php の画像処理ライブラリのasidoが便利そうだったのでひどい web アプリを作ってみた。
url でサイズと画像を指定したら、そのサイズでその画像を出力するアプリ。
まず、元画像 500x334

これを、以下のような url でアクセスした時の画像
![]()
![]()
![]()
動作の説明がめんどいので、さらっと。
- 元画像は ドキュメントルートからの絶対パス指定でも、http:// から始まる絶対パスでもok
- url で指定されたサイズにリサイズする
- 縦横比が異なる場合は、リサイズした上で画像の中心から指定サイズ分トリミングする
- 元画像より大きいサイズを指定した場合は無視される
- 本来は一度生成した画像はこの辺に書き出される→ http://pm11op.xii.jp/pm11op/anysizenizer/img/thumbs/test/100x100/
- けど、借りてるサーバに得体の知れない画像が生成されるのは嫌なので、表示直後に unlink する富豪っぷり。
- 中身は CakePHP と asido で動いてますよと。
一番下からダウンロードできますが、
実際に使用した際に発生したいかなる不利益も自己責任で処理してください。
ちょっと考えただけでも以下の問題点・改善すべき点があります。
- 富豪過ぎるので、画像もDBに格納して管理する -扱いがめんどうなLOB(ラージオブジェクト)は使わない方法も含めを参考に mod_rewrite でキャッシュぽくしたら、案外使えると思う
- とはいえ、ループで大きい画像を 1x1 ~ 5000x5000 とかされたらひどい目に合うと思うので、指定サイズ以外は無視するとかの処理はすべきだと思う。
CakePHP をいきなり実践投入して2ヶ月程たった。
最近は Web アプリ作る時に当たり前のように Ajax 使うし、
それを当たり前のように要求される。
(要求されるのはほとんどがアニメーションとかの部分であって、特に ajax ではないけど。)
で、それを当たり前のようにこなすに当たり、CakePHP は便利なんだけど、
何が便利かっていうと、 決して php から Ajax コードを記述する、
Ajax helper があるからではない。
せっかくだから Ajax helper を何度か使おうとしたけど、
結局全く使わなかった。(使えなかった)
php から javascript を記述しようとすると、痒いところが多すぎる。
じゃあ CakePHP は何が便利かっていうと、
controller 内 の action で、
$this->layout = '';
こうするだけで、簡単に layout なしの HTML パーツを生成できるから便利。
それを javascritp で受け取って表示するコードなんて、すぐ書ける。
- HTML の生成は PHP が行う
- javascript はそれを受け取って表示する
- それ以外のアニメーションを含む HTML の加工は javascript が行う
それって当たり前のことなんだけど、改めてそう決めておけばメンテナンス性が落ちることもない。ハズ。
下手に Ajax helperを使ってしまうと、
どうしてもある部分では PHPで、ある部分は javascript に書かれてて、
しかもその境界があいまいになっちゃったりする。
関係ないけど javascript HTML テンプレートとか使い出したらいよいよ危ない。
html helper もいろいろあるけど、
html タグを書きたくない病の人向けすぎる。 form 関連だけで十分。
div タグとか img タグとか、そんなもん手で書けばよいと思う。
最近のデザイナーさんは smarty 混じりの html くらいなら普通に手を入れてくれるし。
僕のまわりのデザイナーさんが優秀なだけかもしれんけど。
それが smarty じゃなくて php コードだったらまた話は違ってくると思う。
確かめたわけじゃないけど。
なんでかっていうと、 「<?」これとか「?>」これとかが ぱっと見で html タグと区別しづらいから。
僕自身、PHP 混じり HTML 文見るとイラっとするし。
だから、PHP プログラマはできるだけ HTML 書きたくない病にかかったりするんだと思ってる。
※僕の cake は 1.2.0.5146
CakePHP で /app/tests/cases/ 以下に testCase を作成すると、
App Test Cases でテストの一覧を表示してくれる。
勝手に表示してくれるのはありがたいが、
でもどうせだったら全部まとめて実行したい。
勝手に表示してくれるくらいだから、
全部まとめて取得するメソッドあるんだろうと思ったら、やっぱりあった。
こんな group test を作ると、App Test Groups のところに表示されて、
testCase 全部まとめて実行してくれる。重いけど。
/app/tests/groups/app_all.group.php
class AppAllGroupTest extends GroupTest {
var $label = 'run all app test';function AppAllGroupTest() {
TestManager::addTestCasesFromDirectory($this, APP_TEST_CASES . DS );
}
}
前エントリで書いた、任意の url で任意の controller, action を実行する方法で、
例外が一つある。
url と controller, action のマッピングメモ
それは、既に存在するディレクトリ名を含むマッピングができないという点である。
例えば、既に /blog ディレクトリが存在する場合、
いくら routes.php にこう書いても、 /blog/ へのアクセスで
BlogController は実行されない。
Router::connect ('/blog/:action/*', array('controller'=>'Blog', 'action'=>'index'));
これは、ドキュメントルート直下の .htaccess で以下のように記述されているためである。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>
リクエストに該当するファイルやディレクトリが無い場合に限り、
index.php に Rewrite されるのだ。
/blog が既に存在する場合、 Rewrite はされない。
でもどうしてもどうしても /blog で BlogController を実行したい場合は、
/blog/index.php に、以下内容を記述すればよい。
<?php $_GET['url'] = '/blog/';// ドキュメントルート直下の index.php をインクルードする
require('/../index.php');
?>
因みにこのことは .htaccess の↓の行をコメントアウトしたら
実現しそうな気もするが、それは罠である。ムリ。
RewriteCond %{REQUEST_FILENAME} !-d
さらに言えば、静的 html も view ディレクトリ下に配置して、
Cakephp から(例えば pagesController)から呼び出すという方法もあるが、
全ファイルが php になってしまうので、時と場合を選ぶ。
Session を保持しつづけなければならないようなサイトでは、
その方法がよさそうだと思った。
※CakePHP1.2 の話です。
CakePHP で既存の controller, action を任意の url で実行するには
/app/config/routes.php にマッピングを記述すればよい。
例えば、マニュアルには /blog/history/05/june という url で
BlogController の history アクションに 05, june というパラメータを
渡す方法を書いてある。
次の例では、/blog のすべてのURLを、 BlogController に接続します。デフォルトのアクションは、 BlogController::index() になります。
例 4.3. Route の例
connect ('/blog/:action/*', array('controller'=>'Blog', 'action'=>'index'));
Routes の設定
でも実はこれ、特に設定しなくても普通に実行される。
何も設定しなくても、ディレクトリの第一階層は controller として扱われるし、
第二階層は action として扱われる。
そして、それ以下はパラメータになる。
マニュアルで言わんとしてるのは、これが任意の url で可能であるってことだ。
例えば、 /archive/05/june/ という url で上記と同じ action を実行するには、
以下のように記述するとよい。
Router::connect('/archive/*', array('controller' => 'blog', 'action' => 'history'));
また、 /blog ではなく、 /cms 以下全ての url を BlogController に接続するには、以下のようにする。
Router::connect('/cms/:action/*', array('controller' => 'blog', 'action' => 'index'));
ここまでがマニュアルに書いてあることだ。
試してみた限りでは、さらに /archives/something/category/ という url で、
BlogController の category アクションに、 "something" というパラメータを渡すことが可能だ。
こうする。
Router::connect('/archives/:pass/:action/*', array('controller' => 'blog'));
ただし、/archives/something/category/hoge/ としても、 hoge はパラメータとして渡されない。
ぽい。
メモ。
plugin の存在を完全にスルーしてた。
汎用的な controller は plugin として作るべきなのか。
しまった。component として作ってた。
CakePHP 1.2.0.5427alpha がリリースされました。
まだ alpha。
console 周りが結構変更されてます。
bake コマンドの元ファイルが template になったみたいですね。
妥当な流れだと思います。
とはいえ。
そのテンプレートの中身はこんなんなんですよ。
/cake/console/libs/templates/views/view.ctp より一部抜粋。
$i = 0;
foreach ($fields as $field) {
$class = null;
if ($i++ % 2 == 0) {
$class = ' class="altrow"';
}if (in_array($field['name'], array_keys($foreignKeys))) {
$otherModelClass = $foreignKeys[$field['name']][1];
$otherModelKey = Inflector::underscore($otherModelClass);
$otherControllerName = Inflector::pluralize($otherModelClass);
$otherControllerPath = Inflector::underscore($otherControllerName);
if (isset($foreignKeys[$field['name']][2])) {
$otherModelClass = $foreignKeys[$field['name']][2];
}
$otherSingularVar = Inflector::variable($otherModelClass);
$otherModelObj =& ClassRegistry::getObject($otherModelKey);
$otherPrimaryKey = $otherModelObj->primaryKey;
$otherDisplayField = $otherModelObj->displayField;
echo "\t\t<dt{$class}>".Inflector::humanize($otherModelClass)."</dt>\n";
echo "\t\t<dd{$class}>\n\t\t\t<?php echo \$html->link(\${$singularVar}['{$otherModelClass}']['{$otherDisplayField}'], array('controller'=> '{$otherControllerPath}', 'action'=>'view', \${$singularVar}['{$otherModelClass}']['{$otherPrimaryKey}'])); ?>\n\t\t\t \n\t\t</dd>\n";
} else {
echo "\t\t<dt{$class}>".Inflector::humanize($field['name'])."</dt>\n";
echo "\t\t<dd{$class}>\n\t\t\t<?php echo \${$singularVar}['{$modelClass}']['{$field['name']}']?>\n\t\t\t \n\t\t</dd>\n";
}
}
?>
</dl>
</div>
だめだ、やっぱりなじめない。。。
cake_smarty の対応はどうしよっかな。
今回はスルーして、ベータ版出てからにしよっかな。
作ってみたものの、実はあんまり用途が思い浮かんでない。
なんとなく、 array_walk がキーワードに浮かんでは来るけど。
てことで、とりあえず smarty で使ってみようと思う。
CakePHP を完全 smarty 化してみたものの、
smarty 内での配列の扱いが微妙だ。
↓事の顛末はこの辺に。
http://d.hatena.ne.jp/am11op/20070614/1181837256
要は、php ならこう書くべきところを
<?php echo $html->link('Edit', array('action'=>'edit', 2));?>
smarty では view に変なメソッド作った上でこう書く必要があった。
{$html->link('Edit', $view->_array('action=>edit', 2))}
まあ、これでもよいんだけど、せっかくなんで closure (的なもの)を使ってみる。
Closure はこんなの。
class Closure {
var $_args;
var $_func;
/**
* @private
*/
function Closure($func=null, $args=null) {
if ($func) {
$this->_args = $args;
$func = ereg_replace("^function\([^\)]*\) *\{", "", $func);
$func = ereg_replace("\}$", "", $func);
$this->_func = $func;
}
}
/**
* @param $func string closure
* @param $arg1 $mixed first object/var which is used in $func
* @param $arg2 $mixed second object/var which is used in $func
* ...
*/
function bind() {
$args = func_get_args();
return new Closure(array_shift($args), $args);
}/**
* use this method to call closure
*/
function call() {
$func = create_function('$args', $this->_func);
return $func($this->_args);
}
function func() {
$args = func_get_args();
$obj = new Closure(array_shift($args), $args);
return $obj->call();
}
}
無駄にインスタンス量産してるのは、書き直すのが面倒だったから。
このクラスを closure という名前で assign_by_ref してやって、
smarty 内からこう使う。
{*$html->link('Edit', $view->_array('action=>edit', 2))*}
{$html->link('Edit', $closure->func("return array('action'=>'edit', 2);"))}
文字数増えてる!
ネタです!
まあ、ちょっと変数を php で処理して何かのメソッドに渡したい時、
一回しか使わないような modifier をいちいち作らなくてよいので便利かも。
ただし、入力値に対して使う場合は任意のコードを実行されないように気をつけること(棒読み)。