Formヘルパーを使う際、inputタグなどなら、
$form->input('User.name', array('class' => 'test'));
のようにclassを指定できますが、
hiddenタグの場合、class指定は無視されます。
$form->input('User.id', array('type' => 'hidden', 'class' => 'test'));
これは、formヘルパーが以下のように意図的に無視している為で、仕様のようです。
function hidden($fieldName, $options = array()) {
//(略)
return sprintf(
$this->Html->tags['hidden'],
$options['name'],
$this->_parseAttributes($options, array('name', 'class'), '', ' ')
);
}
JavaScriptから制御する際など、hiddenにもclassを付けたいことは多々ありますが、考慮されていないようですね。
直接タイプしても面倒じゃないので、まあそれほど困らないのですが。
<input type="hidden" name="data[User][id]" class="test" />
リンクのついた画像をHtmlヘルパーで表示する方法がCakePHP1.3から変更になりました。
CakePHP1.2までであればこれは
$html->link($html->image('sample.png'),
array('action' => 'hoge'), null, null, false);
と書けました。
第5引数のタイトル文字のエスケープ設定にfalseをセットし、imgタグをエスケープせずに、そのまま出力しています。
ですが、
CakePHP1.3からはこのやり方ができなくなってしまいました。
1.2
HtmlHelper#link($title, $url = null, $options = array(), $confirmMessage = false, $escapeTitle = true);
1.3
HtmlHelper#link($title, $url = null, $options = array(), $confirmMessage = false);
と$escapeTitle引数が削除されてしまいました。
エスケープができないんじゃ、画像表示されないじゃん!!と早合点したあなた、安心してください。
CakeBookによれば$options['escape']を使用してくださいとのことです。
なるほど。
というわけで、前置きが長くなりましたがCakePHP1.3からはリンク付きの画像を出力するには
$html->link($html->image('sample.png'), $url, array('escape' => false));
のようにします。
すこし、スマートになった気がしていいですね。
CakePHPでは、AppControllerのbeforeFilter()で、Authコンポーネントの設定など色々な処理を書くと思います。
各コントローラでbeforeFilter()をオーバーライドした際、よくparent::beforeFilter()を呼び忘れてハマるのは、たぶん私だけではないですよね?
そこで、呼び忘れを防止する簡単な対策です。
class AppController extends Controller {
public function __construct() {
parent::__construct();
$this->layout = 'error';
}
public function beforeFilter() {
$this->layout = 'front';
//ほにゃらら
}
}
このようにすることで、各コントローラでparent::beforeFilter()を呼び忘れると、エラーレイアウトが表示されます。
目立って、すぐに分かりますね。
コンストラクタでerrorをセットすることで、各コントローラで
public $layout = 'front';
のようにやっていても上書きできます。
各コントローラのbeforeFilter()でlayoutをセットしていたら検知できないですが、簡易的な対策として、一定の効果はあるのでは無いでしょうか?
開発が終わったら、最適化のため消しておきましょう。
# 気づいていると思いますが、AndriodのSuperNotCalledExceptionのノリです
PHPでXML-RPCサーバを作るには、IXRのライブラリが簡単です。
PEAR::XML_RPC2も良いですが、こっちの方がCakePHPで使うには良さそうですね。
以下のページで、CakePHPでの使い方もまとまっています。
http://bakery.cakephp.org/articles/view/how-to-create-an-xml-rpc-server-with-cakephp
基本的に、インデックスアクションでレシーバを登録するだけで、後は各アクションでPHPのオブジェクトをreturnできます。
あまりに簡単すぎて、基本的なことを調べるのがめんどくさくなります。
たとえば、base64やdateTimeで囲んだ値を返したいときはどうしたら良いでしょうか?
このようにやればOKです。
function _hogeAction($data) {
return new IXR_Base64($bytedata);
}
function _piyoAction($data) {
return new IXR_Date(time());
}
Base64は、内部でbase64_encodeが呼ばれるので、バイトデータを直接渡すようにします。
dateは、タイムスタンプ値かISO形式を渡します。
dateのパースは、あまり柔軟性は無いので気をつけてください。
function parseIso($iso) {
$this->year = substr($iso, 0, 4);
$this->month = substr($iso, 4, 2);
$this->day = substr($iso, 6, 2);
$this->hour = substr($iso, 9, 2);
$this->minute = substr($iso, 12, 2);
$this->second = substr($iso, 15, 2);
}
Railsではエラーが発生した際、developmentモードではstacktraceなどが詳細に表示され、productionモードでは詳細が全部隠れて表示されます。

プロダクションモード

デベロップメントモード
しかし、developmentモードで動作しているはずなのに、詳細なエラーが出ないことがあります。
DBへの接続でエラーになった際は、productionと同じ画面が出たことがありました。
他にありがちなのは、エラー詳細画面をrenderする際のエラーです。
今回は、エラーログに、以下のようなエラーが出ていました。
ActionView::TemplateError (wrong number of arguments (1 for 2)) in C:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/templates/rescues/_request_and_response.erb:
-e:2:in `load’
-e:2
Rendered rescues/_trace (42.0ms)
/!\ FAILSAFE /!\ Fri May 28 11:45:52 +0900 2010
Status: 500 Internal Server Error
エラー画面は
/lib/action_controller/templates/rescues/_request_and_response.erb:
ですが、このテンプレートの中では、debugなどの関数を使っています。
今回の原因は、自前で「引数2個の」debug関数を作っていたことでした。
上記エラーからそれが読み取れます。
関数の名前を変えたら、ちゃんとした画面が出ました。
当然、直ってもエラー画面なんですけどね・・・
ありがちな名前を付けないように気をつけようというお話です。
フレームワークが変な名前を予約しないで欲しい・・・ typeとか。
CakePHPでfindする際、belongsToしているオブジェクトを条件にすることができます。
//例:datas → categories で、dataはcategoryに属している(data belongsto category)
$datas = $this->Data->find('all', array(
'conditions' => array('Category.id' => 5)
));
しかし、hasManyしているものに関しては、そのまま実行すると unknown column と怒られてしまいます。
hasManyでは、デフォルトでJOINされないためです。
hasAndBelongsToManyでも、同じことが発生します。
これを回避するには、明示的なJOINを実行する必要があります。
たとえば、データが複数のカテゴリに属するように、data hasandbelongstomany category にした場合、以下のように指定します。
//例:datas← categories_datas→ categories で、
//hasAndBelongsToManyしている
$datas = $this->Data->find('all', array(
'joins' => array(array(
'type' => 'LEFT',
'alias' => 'CategoriesData',
'table' => 'categories_datas',
)),
'conditions' => 'CategoriesData.data_id = Data.id'
));
joinsは配列なので、いったん取得して[]演算子で追加するか、このように最初から二重の配列で指定してあげる必要があります。
(もちろん、既存のJOINを消してしまわないかチェックしつつ)
ところで、hasAndBelongsToManyでググると、途中で「破産」がたっぷり出てきた後、↓のようになって若干悲しくなりますね・・・

hasAndBelongsToManyで検索する途中
Railsで画像のパスを取得したいときは、image_pathを使います。
<%= image_tag 'test.png' %>
<%= image_path 'test.png' %>
これらのヘルパーメソッド、Viewの中で使う分には問題なく動くのですが、Controller内で使うと、prefixが付かない問題が発生します。
たとえば、http://example.com/myapp/ をアプリのルートにしているとき、
View内で使用: /myapp/images/test.png
Controller内で使用:/images/test.png
このように戻り値が違います。
完全にバグとしか言えないのですが、とりあえず修正しないことには仕方ないので、
prefix = ActionController::Base.relative_url_root
path = image_path 'test.png'
return prefix + path
みたいな処理を行うことにしました。ControllerとView両方で呼び出すメソッドでは、判定式も必要になりそうです。
Railsはprefix周りで問題が多いですね。
サーバに明示的な引数まで指定してこれですか・・・と
何も指定しなくても勝手にやってくれるCakePHPは、実はすごく優秀な子だったんだと見直すばかりです。
Railsの必須プラグインの一つ、論理削除を実現するacts_as_paranoidですが、Windowsだと上手くインストールできないことがあります。
gitをちゃんと設定すればいけるはずですが、ダウンロードした方が早いです。
http://github.com/technoweenie/acts_as_paranoid の上部から、「download」ができます。
ZIPを解凍して、以下のようなフォルダ構成になるように、vendors/plugin にぶち込めばOKです。
置いた後は、サーバを再起動しましょう。
すごく便利なのは良いんですが、
・標準で入れて欲しい
・名前が微妙
・サーバ再起動めんどくさい
といった点が気になります・・・
RailsでDBにdatetime型のカラムがある場合、特に意識しなければ、DBにはUTC時刻が保存されます。
ActiveRecordでデータを取得すると、ActiveSupport::TimeWithZone 型のオブジェクトが取得できます。
http://www.51773.com/tools/api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html
to_sやinspectをすればそれっぽい文字列になってくれますが、日本人なら、ちゃんとフォーマットして欲しいですよね。
to_sにフォーマットを渡せるようになっているので、
obj.date.to_s(:db)
と指定すると、2010-05-01 10:00:00 のような形式で取得できます。
ただ、これだとUTC時刻のままになるので、
obj.date.localtime.to_s(:db)
のようにやると良さそうです。
また、日本語の形式などに変換したいときは、
Time::DATE_FORMATS[:jp] = "%Y年%m月%d日 %H時i分s秒"
のように指定すれば良いみたいです。
http://japan.zdnet.com/blog/yoshimi/2008/04/22/entry_27016455/
RailsのActiveSupportは大変便利で、生Ruby使うときも
irb -r rubygems -r active_support
をデフォルトにしたくなります。
# 個人的には .blank? が一番便利だと思います。
ところで、Hash.from_xmlを使うとお手軽にXMLをパースできますが、若干癖があるので注意が必要です。
・子要素も属性も同じように扱われる
・同じ名前の要素が複数あると自動で配列になる
・typeという名前の属性は、無視されることがある
・ハイフンはアンダースコアに置換される
たとえば、user-listの中にuserが複数ある場合、
{"user_list" => {"user" => ["yamada", "tanaka"]}}
のように変換されるため、扱いやすいのですが、userがたまたま1件だと、
{"user_list" => {"user" => "yamada"}}
のようになり、userが配列と期待しているプログラムは動かなくなります。
特に、検索系のXMLを使う際は注意が必要です。
また、属性は子要素のように使えますが、属性が1つだけだったり、子要素が文字列の場合、扱いが変わったり消えてしまうことがあるので、注意しましょう。
以下に例を挙げます。
<a><b size="123">456</b></a>
=> {"a" => {"b" => "456"}}
<a><b size="123" /></a>
=> {"a" => {"b" => "123"}}
<a><b type="123" /></a>
=> {"a" => {"b" => nil}}
<a><b size="123"><c>456</c></b></a>
=> {"a" => {"b" => {"size" => "123", "c" => "456"}}}
<a><b type="123"><c>456</c></b></a>
=> {"a" => {"b" => {"c" => "456", "type" => "123"}}}
複雑なXMLをパースする際は、ちゃんとnokogiriなどのライブラリを使うと良さそうですね。