2012年11月26日月曜日

[CakePHP]recursive=2で取得してしまう不要なデータを取り除く

モデルのアソシエイションで多対多を実現するには、以下の2通りの
方法がある。

model Aとmodl Bが多対多である場合、

1.以下のようにHABTMを設定する。
 ・A HABTM B
 ・B HABTM A

2.belongsToとhasManyの関係に分割する。
 ・A hasMany AsB
 ・AsB belongsTo A,B
 ・B hasMany AsB

1の場合も中間テーブルAsBは必要なのだが、
AとBのidしかカラムに設定できず、そのほかの情報が保存できない、
というデメリットがあるため、私は2を使用する時がある。

そのような場合、Model::find()を使用すると、デフォルトだと中間テーブルまで
しかデータを取得してきてくれない。

なので、recursiveに2を設定する。

ただ、それだと、不要なデータまでたくさん取得してきてしまう可能性がある。
例えば上記の2のアソシエイションと同時に

 ・A belongsTo C
 ・C hasMany A

といったアソシエイションも持っている状況でAがカレントのモデルの際に
A->find()を行うと想定する。

すると、取得してくるデータは以下のようになってしまう。

①A
②A->AsB
③A->AsB->B
④A->AsB->A
⑤A->C
⑥A->C->A

④と⑥は重複している情報であり、無駄である。
なので、この場合は、「AsB belongsTo B」と「C hasMany A」というアソシエイションを
unbindModel()にて一時的に解除してあげればよい。

下記のサイトを参考にさせて頂いた。

CakePHP recursive=2で不要な関係データの取り除き方

2012年11月22日木曜日

[jQuery]コロンを含むセレクタを指定する方法

以下のサイトで説明されているよう、コロンの前に二つのバックスラッシュを
つけてエスケープしなければならない。
なぜ二つかはわからない。

JQueryでコロン付きのセレクターを使う場合

私の場合は既に変数で指定されている文字列を使う必要があったので
下記のようにコロンを置換して実装した。
なお、4行目に試しに置換後の文字列を表示しているが、
なぜか「abc\:1234」とバックスラッシュが一つだけで表示される。
//以下JavaScript部分 
var test ="abc:1234"
test2=test.replace(':',"\\:")
alert(test2);  //「abc\:1234」と表示される。
alert($("#"+test2).val());  //「5678」と表示される。
//以下HTML部分
<input type="hidden" id="abc:1234" value="5678"> 

[CakePHP]セッション上の配列の一番後ろに格納する方法

以下のページの「5.7.1.1 write」にあるよう、CakePHPのsessionコンポーネントの$this->Session->write()で多次元配列を格納できるが、
連想配列でなく通常の配列の一番後ろに格納する方法がわからなかった。

メソッド :: セッション :: 主要なコンポーネント :: マニュアル :: 1.3コレクション 

そこで、以下で紹介されているように

Can I use array_push on a SESSION array in php?
 
$_SESSION['names'] = array();
array_push($_SESSION['names'],$name);
もしくは
$_SESSION['names'][] = $name; 
というようにすれば、namesの下に配列として0から格納し、
最後の添え字に+1した添え字で格納してくれることになる。
なので、シンプルな後者を使うことにした。
ちなみに、0ではない数字(例えば1)からカウントアップしていきたい場合は
以下で紹介されているよう。最初の値を1に格納してしまえばいいそう。

配列について

また、私の場合は、配列の添え字(上の例で言えば'name')
を変数で指定したいと考えていた。
そのためには下記のサイトで説明されているよう変数をダブルクオテーション
で囲む必要があるとのこと。

PHP で、変数を連想配列(ハッシュ)のキーにしたい。

こんな感じ。
$items["$item->id"] = $item->name;
ただ、私の場合は以下のように変数に角括弧を含むものを使おうとしていたら
動かなかった。
$_SESSION["$this->data['TargetSite']"][] = $result;
なので、以下のように一度別の変数に移してから格納したら正常に動いた。
$TargetSite=$this->data['TargetSite'];
$_SESSION["$TargetSite"][] = $result;

【課題】
やはり、 CakePHP標準の$this->Session->write()を使用して
同じことができるようになりたいな。
そちらのほうが、CakePHPの機能のセッション領域をDBにしたりする
のに簡単に対応できそう。
まぁ、ひとまず上記のやり方でいいや。

2012年11月18日日曜日

[CakePHP]error_logを使う際の注意点

ログに配列の中身を表示する際、CakePHPで通常使用するCakeLog::write()だと、
配列の中身は表示できない。
そこで、下記のブログで紹介されているようにerror_logを使用していた。

[php][tips]phpのエラーログに、array配列の中身をきれいに吐く。

ただ、ここで不思議な現象がおきた。

error_logを使用すると、

「Notice (8): Undefined variable」や
「Warning (2): Invalid argument supplied for foreach()」
など、今まで画面に表示されなかったメッセージが画面に表示される
ようになる。
ただ、error_logに渡したメッセージは相変わらずログにしか表示されない。

AJAXの出力でこれが出力されてしまっていて、
動きがおかしくなっていた。
これを防止するには、下記のコマンドでメッセージを出さない
ようにする。
Configure::write('debug', 0);

[CakePHP]update文を発行する保存は同じselect文を二回発行する件について

Model::save()やModel::saveAll()でプライマリーキーがセットされていたら、
自動的にSELECT文を発行して、すでに存在するレコードかどうか確認してくれるんだけど、
なぜか、このSELECT文、まったく同じものが2回発行されるんだよね。

大してユーザがいないシステムなら問題なら良いけど、もし人気サイトなんかに
なったりしたら、アップデートを行う際は、1.5倍のSQL発行量にになってしまうのは
よろしくない気がする。

以下のサイトで対策を教えて下さっているので、
今後、パフォーマンスに問題を感じるようになったらやろう。

CakephpでUPDATEするときのクエリを(少しだけ)減らしてみた