さんさろ

さんさろ

プロダクトエンジニアの雑記&技術、たまにドイツ

CakePHPでリファクタリングする際にとりあえず見る所

やっぱりDB周りです

今引き継いでやっているプロジェクトがCakePHP2系で、そろそろ△年経つシステムですのでちょこちょこリファクタリングすることもあるのですが。

以前、「別サーバから API を叩きに来るタイミングでLAが凄い上がるのでリファクタリングしましょう」という課題があって以下の事をした時に、LAが10分の1だか15分の1だかになった事があって、「リファクタリング……だいじ………!!」としみじみと思った経験から、CakePHP に限らず DB とのやり取りには気を使うようになったのでした……。

影響範囲が少ない時には、結構他の課題と一緒にやってしまいますが、「処理重くね……?」と思った時にまず見る点を備忘録的にちょっとまとめてみます。

多分、CakePHPer さんにしたら、「え……今更……?」という内容だと思いますががが。

1.データ取得時の recursive 設定

これ超大事。だと思う。

https://book.cakephp.org/2.0/ja/models/model-attributes.html#recursive

そもそものお話ですけど、CakePHPの公式的には、AppModel で recursive を -1 に設定しておきましょうとなってます。

recursive レベルは -1 にしておいたほうがよいでしょう。 こうしておくと、不要な関連データを取得してしまうのを回避できます。 これは、おそらく find() を呼び出すほとんどの場合に望ましい結果になります。 必要な場合にのみ recursive レベルを設定して関連データを取得させるか、もしくは Containable ビヘイビアを使いましょう。

AppModel に次のような設定を追加します。:

public $recursive = -1;

まあね、最初からそうなってればいいんですけどね。なってないんだからしょうがないですよね。

リファクタリングでこれするには、流石に怖すぎて出来ないかなぁ……(;´∀`)

(数えたらモデル94個あった……)

なので、まあ、こういう対応が必要になってくるわけなんですが……。

recursive 設定されていない場合のデフォルトはレベル1です。つまり基本全部の find で recursive がレベル1なわけです。困った。

例えば、10個近いテーブルとリレーション(CakePHPだとアソシエーション)張られてるのに recursive 設定されてなかったりとか、たまに

$array = $this->ModelName->find('all', array(
    'recursive' = 2,
    ...
));

とかなってると「……………\(^o^)/」ってなります。いや、あのね。それね、全部………ぜーーんぶ取ってきてますけど、それホントにいります?ってなります。

いくつもテーブルがぶらさがってて、しかもそれが結構な量だと洒落になりません、マジで。

現に、これで凄まじい重さのことがたまに(結構?)ありました。

特にアクションログのような物まで一緒に取って来てると、天を仰ぎたくなります。

あまり情報要らないのであれば、私は大人しく

$array = $this->ModelName->find('all', array(
    'recursive' = -2,
    ...
));

を設定します。 あるいは、

$this->ModelName->recursive = -2;

ただ、これだと肝心の欲しいデータが取れない時があるんですよねぇ。確かに、便利だからリレーション張ってるのに、それ意図的にちょん切るわけですからね。必要な時に取れないのでは困ります。

そこで出てくるのが、これ。

2.ContainableBehavior

そう、データ取得の際にリレーションが深い所の値が欲しい時は確かにあります。けど、recursive が 2 で深い所のを全部取ってくるのは多すぎる。

という時に、recursive は 2 だけど、その中のこれとこれだけ取ってくる、ということをやります。

$this->User->Behaviors->load('Containable');
$users = $this->User->find('all', array(
    'contain' => array(
        'Post' => array(
            'title',
            'modified',
        ),
        'Profile' => array(
            'age',
            'postcode',
        ),
    ),
));

要は、本当に「必要なものだけ」取ってくるわけですよね。理に適ってます。

でもこれ注意する必要があるのは、リファクタリングの際だと特に「本当は Profile の address も必要だったのに!」って怒られたりすることがあることです。

本当に必要なものがちゃんと取れてるのかは、debug などしてデータをしっかりと確認する必要があるということですかね。

「最初からやってくれれば後から数多あるキーを虱潰しに確認する手間も省けたのになぁ〜!(・∀・)」とかは言ってはいけないのです……。

長く使うつもりで、それでも少しずつ、コードを綺麗に、少しでも早い処理になるように、日々リファクタリングしていかねばなぁ、と思った今日この頃。