1
1

More than 3 years have passed since last update.

CakePHPのvirtualFieldSeparatorを変更する

Posted at

はじめに

TL;DR

  • virtualFieldSeparatorが含まれるテーブルカラム名があると結果セットが想定通りにならない
  • virtualFieldSeparatorを変更することで想定通りの結果セットを得られる
  • 正しい対応方法はテーブルカラム名を修正できるならする、カラム参照しているコードも修正する(なのでバッドノウハウ)

結論

$db = $this->getDataSource();
$db->virtualFieldSeparator = "___";
$res = $db->query("
  SELECT hoge_table.id, hoge_table.item__1, fuga_table.item_2 
  FROM hoge_table 
    LEFT JOIN fuga_table ON hoge_table.id = fuga_table.id
  WHERE hoge_table.id = 100"
);

var_dump($res);
/*
[0] => Array
  (
    [hoge_table] => Array
      (
        [id] => 100
        [item__1] => hoge
      )
    [fuga_table] => Array
      (
        [item_2] => fuga
      )
  )
*/

virtualFieldSeparatorとは

  • CakePHP2.xに実装されたvirtual fieldという機能で使用する
  • SELECT句で別名定義したカラムを結果セットの配列で任意のテーブルへ含めるための命名ルールテーブル名__カラム名__のことです

例えば、以下のクエリではfieldsにSQL関数を書いた場合、モデル名の下にSQL関数の結果が入ります

$res = $this->query("SELECT id, CONCAT(item1, item2) as item FROM hoge_table WHERE id = 100")

var_dump($res);
/*
[0] => Array
  (
    [hoge_table] => Array
      (
        [id] => 100
      )
    [0] => Array
      (
        [item] => hoge
       )
   )
*/

virtual fieldの命名ルールを使うとhoge_table以下にまとまります

$res = $this->query("
  SELECT id, CONCAT(item1, item2) as hoge_table__item 
  FROM hoge_table 
  WHERE id = 100"
)

var_dump($res);
/*
[0] => Array
  (
    [hoge_table] => Array
      (
        [id] => 100
        [item] => hogefuga
      )
  )
*/

以下のようなカラム名の場合、意図せずvirtual fieldの命名ルールに該当してしまい、想定と異なる結果セットとなってしまいます

$res = $this->query("
  SELECT id, item__1 
  FROM hoge_table 
  WHERE id = 100"
)

var_dump($res);
/*
[0] => Array
  (
    [hoge_table] => Array
      (
        [id] => 100
      )
    [0] => Array
      (
        [item__1] => hoge
       )
   )
*/

カラム名が変更できればitem__1item_1などに変更するのが一番いい対応方法になります。
とはいえ、稼働中のソフトウェアであればDBのカラム変更は影響範囲も大きく、場合によっては稼働停止してメンテナンスする必要があるかもしれないため、場合によってはカラム変更が現実的にできない場合もあるでしょう。

次善策としてはカラムに別名をつける、が挙げられそうです

$res = $this->query("
  SELECT id, item__1 as item_1 
  FROM hoge_table 
  WHERE id = 100"
)

var_dump($res);
/*
[0] => Array
  (
    [hoge_table] => Array
      (
        [id] => 100
        [item__1] => hoge
       )
   )
*/

別名で回避できるならそうしたいところですが、対象カラムが多いと参照箇所も比例して多くなり、コードの修正やテストに時間がかかることが予想されます。
このような事象が問題と認識されずに開発が進み、リリース間近になってレビューで指摘されて修正時間がない、というような状況で特定の機能だけは想定通りの結果セットでないと設計上問題がある、のような特殊な状況においてはvirtualFieldSeparator自体を一時的に変更することで他機能への影響なしで対応することができます。

本記事では""(デフォルト値、アンダースコア2個)から"_"(アンダースコア3個)に変更することでvirtual field命名ルールを変更しています。
virtualFieldSeparatorの変更はこのセッションでのDB Connection closeまで有効となり、他セッションでには影響しません。

$db = $this->getDataSource();
$db->virtualFieldSeparator = "___";

このような反則的な対応をしないようにDB設計やソースレビューを細分化するなどで早期発見して本来あるべき対応方法になるようにすることが大切だと骨身に染みました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1