2014年6月6日金曜日

[Composer]installer-pathsの優先順位について

Composerでここで説明されているように、packageごとにinstallされるpathをカスタマイズできるんだけど、なかなか反映されなくて困った。

"extra": {
    "installer-paths": {
       "plugins/{$name}/": ["type:cakephp-plugin"],
       "app/Plugin/{$name}/": ["somevendor/some-package"]
    }
}



こんな感じだったんだけど、上記のsome-package(もちろん仮名ね)っていうpluginもcakephp-pluginなんだけど、上にあるcakephp-plugin用のpathが先に適用されてしまったからでした。

なので、以下みたいにしたら適用されました。

"extra": {
    "installer-paths": {
         "app/Plugin/{$name}/": ["somevendor/some-package"],
         "plugins/{$name}/": ["type:cakephp-plugin"]
    }
}

2014年6月5日木曜日

[CakePHP]test.phpのCode coverageが表示されない件

テストケースのファイル名をPost01Test.phpみたいに、番号をつけたらcode coverageが表示されなくなった。テスト自体は正常に実行することができるのに。
なので、番号をつけるのをやめてPostTest.phpに戻した。

2014年6月4日水曜日

[CakePHP]find('list')はDISTINCT使えないのね。

$this->Comment->find('list',array(
  fields' => array('DISTINCT user_id'),
  'conditions' => $conditions,
));


こんな感じでやったら、以下のようなエラーが。

PDOEXCEPTION
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DISTINCT `Comment. user_id` FROM `test`.`comments` AS `Comment` WHERE 1 = 1 ' at line 1



このサイトによると、DISTINCTってSELECTの直後に来ないといけないいたい。でもCakePHPのfind('list')ってデフォルトでは、SELECTの直後でIDを指定しているので、DISTINCTがその後になってしまうんですよね。

なので、group byを使うといいみたい。

$this->Comment->find('list',array(
  fields' => array('user_id'),
  'conditions' => $conditions,
  'gruop' => 'user_id'
));

※Group Byの豆知識
 デフォルトだとGROUP BYで指定されたカラムの昇順になる。いきなり順番変わってびっくりした。ORDER BY を使って任意のカラムの昇順、降順を指定できる。

2014年5月29日木曜日

[CakePHP]Controllerのテストで主要モデル以外のモデルのモックを作る方法

※CakePHP2.4.9の話

例えば、AmazonsControllerのテストの場合、

AmazonsControllerTest.php内
$this->controller = $this->generate('Amazons', array(
    'models' => array(
        'Amazon' => array('hardToTest')
    )
));
$this->controller->Amazon->expects($this->once())
        ->method('hardToTest')
        ->will($this->returnValue(true));

主要モデル(コントローラー名に名前が入るモデル)のAmazonモデルの場合は、こんな感じでモックを生成できる。(hardToTestというメソッドのモックを作りたい時)

でも、それ以外のモデル、例えば、Amazonモデルにアソシエーションで紐づくモデルやControllerの処理内でloadModel()したモデル、さらにそれにアソシエーションに紐づくモデルetcはこのやりかたで生成できない。

上記でいう、6行目のexpects()で下記のエラーが発生する。

Error: Call to a member function expects() on a non-object

これに対処する方法のひとつにテスト対象のControllerの$usesにモックを生成したいモデルを入れる、というものもある。ようは、モック化対象のモデルを主要モデルの一つにしちゃうという方法だ。これなら上記のような形で生成できる。
でもそもそもテストのためにプロダクトのコードに手を入れるのってやだよね。

そんな場合は、getMockForModel()を使う。これで簡単にControllerのテスト内で任意のモデルのモックを生成できる。

$this->Controller = $this->generate('Amazons');
$this->getMockForModel('Vote', array('hardToTest'))
        ->expects($this->any())->method('hardToTest');

上記で登場するVoteモデルはちなみにAmazonモデルには直接アソシエーションで紐づいていないモデルだ。Controllerの処理内でloadModelされるItemというModelにアソシエーションで紐づいているモデルである。

でも上記のコードでモックを生成することができる。
注意点としては、かならず1行目でControllerのモックを生成しておく必要があること。

余談だが、上記の$this->Controllerて変数はどんな変数名にしてもOK。$Amazonsにしてもいいし$aでも$bでもOK。generate()メソッドは自動的に$this->Controllerを生成してくれるみたいです。※参考 CakePHP 2.xのtestActionをちゃんと理解しよう

もう一つ余談ですが、当然ですが、モック化したメソッド以外のメソッドは本物のメソッド使ってくれるみたいですね。

2014年5月26日月曜日

[CakePHP]deleteAllはcounterCache更新してくれないみたいね。

※CakePHP2.4.9の話

これは古い記事だけど、こういう対処法があるんだね。

counterCache and deleteAll: How to Make Them Friends

※追記
すみません。引数でcallbackを動かすか指定できるので、それをtrueにするとcoutercacheも更新されます。

http://book.cakephp.org/2.0/en/models/deleting-data.html#deleteall

2014年5月18日日曜日

[Bootstrap]collapseでクリックしたらdata-targetではなくhrefを使うべし。

※Bootstrap 3.1.1のお話。

まず、http://getbootstrap.com/javascript/#collapseでは、以下のように説明されていて、開閉したい要素のidをdata-targetで指定するように書いてある。
たとえば data-target="#demo" といったような感じだ。

Via data attributes
Just add data-toggle="collapse" and a data-target to element to automatically assign control of a collapsible element. The data-target attribute accepts a CSS selector to apply the collapse to. Be sure to add the class collapse to the collapsible element. If you'd like it to default open, add the additional classin.

しかし、iphone5cで試すと、開閉しない。

クリック(iphoneだとtap)する要素がbuttonタグの場合や、classに.btnなどがついている場合、type="button"がついている場合は開閉する。しかし、ただのaタグの場合は開閉しない。

aタグでtype="button"やclass="btn"をつけたくない場合は、data-target="#demo"ではなく、href="#demo"をつけるとうまく動く。
両方つけてしまうと、対象の#demoが画面トップにくようスクロールしてしまう。



2014年5月13日火曜日

[CakePHP]Validation の'required' => trueは外部キーにはいれないほうがいい。

なんでかというと、$Model->saveAssociated()等でassociation先のモデルと同時に保存する場合、外部キーを自動的に補ってくれる。
でもvalidationの処理が走るのは、外部キーを補う前なので、'required' => trueになってしまい、validation errorが発生してしまうから。


※CakePHP2.4.9の話