Techracho

CakePHP hiddenでclassを設定できない

このエントリーをはてなブックマーク Share
2010.06.15    CakePHP, PHP, javascript, 馬場      baba   

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" />

CakePHP1.3 リンク付き画像の表示方法

このエントリーをはてなブックマーク Share
2010.06.14    CakePHP, PHP, 芝原      shibachan   

リンクのついた画像を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で、parent::beforeFilterの呼び忘れを防止

このエントリーをはてなブックマーク Share
    CakePHP, PHP, 馬場      baba   

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のノリです

IXRライブラリの使い方

このエントリーをはてなブックマーク Share
2010.06.09    CakePHP, PHP, 馬場   タグ: —    baba   

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モードでもエラー情報が出ない

このエントリーをはてなブックマーク Share
2010.06.08    Ruby, Ruby on Rails, 馬場   タグ: , —    baba   

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とか。

hasAndBelongsToManyで検索する

このエントリーをはてなブックマーク Share
2010.06.06    CakePHP, PHP, 馬場   タグ: , , —    baba   

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で検索する途中

hasAndBelongsToManyで検索する途中

Rails image_pathの動作が違う

このエントリーをはてなブックマーク Share
2010.06.05    Ruby, Ruby on Rails, 馬場   タグ: —    baba   

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は、実はすごく優秀な子だったんだと見直すばかりです。

windowsでacts_as_paranoidをインストール

このエントリーをはてなブックマーク Share
2010.06.04    Ruby, Ruby on Rails, 馬場      baba   

Railsの必須プラグインの一つ、論理削除を実現するacts_as_paranoidですが、Windowsだと上手くインストールできないことがあります。

gitをちゃんと設定すればいけるはずですが、ダウンロードした方が早いです。

http://github.com/technoweenie/acts_as_paranoid の上部から、「download」ができます。

ZIPを解凍して、以下のようなフォルダ構成になるように、vendors/plugin にぶち込めばOKです。

  • project
    • app
    • config
    • ….
    • vendor
      • plugins
        • acts_as_paranoid
          • lib
          • test
          • init.rb

置いた後は、サーバを再起動しましょう。

すごく便利なのは良いんですが、
・標準で入れて欲しい
・名前が微妙
・サーバ再起動めんどくさい
といった点が気になります・・・

RailsのActiveRecordで日付処理

このエントリーをはてなブックマーク Share
2010.05.26    Ruby, Ruby on Rails, 馬場   タグ: —    baba   

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のHash.from_xmlに注意

このエントリーをはてなブックマーク Share
2010.05.25    Ruby, Ruby on Rails, 馬場   タグ: —    baba   

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などのライブラリを使うと良さそうですね。

« 新しい投稿古い投稿 »

COPYRIGHT [C] 2009 BEYOND PERSPECTIVE SOLUTIONS LTD. ALL RIGHTS RESERVED.