ひさふぃの日記

DjangoとPythonとLaravelが好き。大阪でフリーランスエンジニアやってます。

CakePHPの小ネタ+Laravelとの比較

役立ちそうな人

さらっとCakePHPの便利機能をさらいたい人

この記事で書くこと

CakePHPで自分がはまった小ネタ
CakePHPで役立ちそうな小ネタ
最後に少しだけLaravelと比較

仕事で触っててやっと輪郭がわかってきたので情報整理も兼ねてメモ
あ、ちなみに3系想定です

基本

bake all で作られるテンプレートに乗っていれば特にはまることなく素早く開発できるはず
実際はそんなきれいな設計書ばかりじゃないよね

CakePHPは特にModel中心で考えられているフレームワーク(のように感じる)
ので、まずはModel(もしくはForm)から手をつけると効率がよさそう

キャッシュ

CakePHPはキャッシュ機能を使うことで高速化しているらしい
まぁでも開発するときに存在しているとテーブル構造変更したのに値取得できないぞ?!とか割とある
そんなときは下記のキャッシュクリアコマンド
なにか変更が反映されないと思ったらキャッシュクリアコマンド

bin/cake cache clear_all

自分は面倒なので開発時にはキャッシュを使わないようにしています
config/bootstrap.php

Cache::disabled() // <=これを最終行に追加

クエリビルダー

主にDBへのアクセスを担っているレイヤーのTableに書く
クエリオブジェクトをメソッドチェーンでつなげてSQLを組み立てる
クエリオブジェクトはTableオブジェクトのfindメソッドをcallして取得

getとfindなにが違うの?

getはprimarykeyを指定して取得するのですが、該当するprimarykeyの行がないとエラーが発生するようです
確実に存在するときに使うと簡潔に書けますがそれ以外のメリットなさそうなのでfindByIdなどを使った方がよさそう

$this->Article->findById($id);

アソシエーションや複数entitityの登録・更新

idを指定するとupdate
指定しないときはinsert

ライフサイクルコールバック

いくつかあるみたいだけど特に使いそうなもの
積極的にコードを移していくとロジックの役割が明確になりわかりやすくなる
・バリデーション前にcall
beforeMarshal
・データの保存前後にcall
beforeSave / beforeAfter

結果の取得で特に使う

・配列内にEntity
toArray()
・toArray()のIterator付与版。newEntitiesに渡すときとか
all()
・最初の結果のみ取得
first()
・行数カウント
count()
・指定したカラムの値のみの配列を取得find('list')のidなし版
extract()

QueryExpression

whereにて複雑な条件を書くときに使う
下の例ならクエリビルダの方がいいけど

$query = $articles->find()
    ->where(function (QueryExpression $exp) {
        return $exp
            ->eq('author_id', 2)
            ->eq('published', true)
            ->notEq('spam', true)
            ->gt('view_count', 10);
    });

sql

$query->func()->countみたいな感じでSQLの関数をそのまま使える
select内だと気にすることなく配列に書いてしまって良い
where内だと上のQueryExpressionと一緒に使うことが多い

$query = $articles->find()
    ->where(function (QueryExpression $exp, Query $q) {
        $year = $q->func()->year([
            'created' => 'identifier'
        ]);
        return $exp
            ->gte($year, 2014)
            ->eq('published', true);
    });

アソシエーション

containを使う
ドット(.)でつなげることで孫も取得できる
孫は子のエンティティに入る

アソシエーション先の取得条件を指定するときはコールバックメソッド内で指定
この時不要であっても外部キーをselect内に記述する必要がある

$query = $articles->find()->contain([
    'Comments',
    'Authors.Profiles' => function (Query $q) {
        return $q->where(['Profiles.is_published' => true]);
    }
]);

アソシエーション先の条件によってアソシエーション元の取得を制限するときはmatching()

$query = $products->find()->matching(
    'Shops.Cities.Countries', function ($q) {
        return $q->where(['Countries.name' => 'Japan']);
    }
);

Entity

そもそも何者?

newEntityやクエリビルダ結果取得メソッドのcallによって取得できる
DBから取得した各行のデータで便利機能をたくさん追加した一つの塊のイメージをしている
ちなみにEntityに変換せずに配列で返すことができる
値取得だけならこちらの方が使いやすいかも??

$query->enableHydration(false);

プロパティ操作

'get○○'や'set○○'という風に定義することで仮想的な値としてゲッターやセッターを実装できる
いったんEntityにて定義してしまえば使う側としては特に意識する必要がない

配列等にも入れ子みたい場合は下記

protected $_virtual = ['full_name'];

逆に存在するカラムで隠蔽したい場合は下記

protected $_hidden = ['password'];

Form

基本的にはTableで事足りますがView側の値とテーブル構造が違うときはFormを作る
Viewにて使いたい値を慎重にSchemaに登録するんやで!

SecurityComponent

Ajaxではまる
blackholeに行くならまずはSchemaに注意だ!

Laravelと比較

ここからは完全に個人的な好みのお話

cssとファイルアップ

Laravelではsassのコンパイラとファイルアップのインターフェースが標準装備されていたので面食らいました

MとVが割と密

CakePHPがmodel寄りのフレームワークなのが影響しているのかと

validatorをmodel層に書くのがどうもしっくりこない
各ページに各Tableが紐付くきれいなwebページだとたしかに簡潔かもしれないが、少し外れるとカラムを追加したりなどの書き方をする必要がある
まぁForm使えばいいやんとかTableのスキーマ調整すればいいやん。とか、defaultValidator以外を使えばいいやん。
って意見があると思うんですけどそれなら最初からTableに書く必要なくね?っていう
validatorはModelというよりもどちらかというとViewからのリクエスト処理に寄っていたほうがしっくりくる
テーブルごとに1つあるのではなく、各ページごとに1つあるイメージ
以上、個人的な好み

でも、Tableのライフサイクルコールバックは結構好きです
ただしこれも単一ページの想定だとばかすこ処理を入れ込めばいいのですが、色々な箇所で呼び出されてしまうとだんだんとカオスになっていきます...
何も考えずになんでも入れる人がいると白目

規約

大文字小文字単数形複数形暗黙的なロードなどなど...がつらい
覚えて慣れると確かに高速に開発できるかもしれないのですが、コードを追うときに検索引っかからないのが割と辛い
え、公式ドキュメント読め?...CakePHP長いやん(錯覚)
まぁでもこれはメリットでもありますね
ディレクトリ構成や命名など割と柔軟なLaravelに比べると型にはまっているのでわりと堅く開発したい時はよさそう

まとめ

はまったところとか使いそうな知識とかまとめたらModelのことばっかりになってしまいました
Model周り以外は公式ドキュメントの分量もそこまで多くないですし直感的でわかりやすいですしね
それでは誰かのお役に立てれば幸いです!

CakePHP 超入門