[CakePHP4]クエリビルダーのselect句指定が超面倒と思った時の対処法

[CakePHP4]クエリビルダーのselect句指定が超面倒と思った時の対処法
2021年05月11日2023年10月10日

CakePHP4でデータ取得する際にクエリビルダーを使用してデータ取得するのですが、クエリビルダーのselectメソッドでcolumn指定してしまうと、必要なcolumn全て記述するのが超面倒です。

ちなみにselectAllExcept()がありますが、こちらは指定したもの以外をselect句に自動でセットしてくれる便利なメソッドではあるのですが、指定したものだけを取得したい場合は使えません。

色々試してみる

selectに何も指定しない

selectに何も指定しない場合は、CakePHPが自動的にすべてのカラムを指定して取得してくれます。

$articles = $this->Articles->find()
    ->contain([
    'EyecatchImage',
    'Categories'
])
    ->limit(10)
    ->orderDesc('publiced');


//生成されたSQL
SELECT 
  Articles.id AS Articles__id, 
  Articles.title AS Articles__title, 
  Articles.body AS Articles__body, 
  Articles.publiced AS Articles__publiced, 
  Articles.created AS Articles__created, 
  Articles.modified AS Articles__modified, 
  EyecatchImage.id AS EyecatchImage__id, 
  EyecatchImage.name AS EyecatchImage__name, 
  EyecatchImage.file_name AS EyecatchImage__file_name, 
  EyecatchImage.created AS EyecatchImage__created, 
  EyecatchImage.modified AS EyecatchImage__modified 
FROM 
  articles Articles 
  LEFT JOIN images EyecatchImage ON EyecatchImage.id = (Articles.eyecatch_image_id) 
ORDER BY 
  publiced DESC 
LIMIT 
  10

selectに任意の項目を指定する

つぎにselectに任意の項目を指定します。当たり前ですが任意の項目なので指定したものだけを取得します。

$articles = $this->Articles->find()
    ->select([
    'Articles.title'
])
    ->contain([
    'EyecatchImage',
    'Categories'
])
    ->limit(10)
    ->orderDesc('publiced');


// 生成されたSQL
SELECT 
  Articles.title AS Articles__title 
FROM 
  articles Articles 
  LEFT JOIN images EyecatchImage ON EyecatchImage.id = (Articles.eyecatch_image_id) 
ORDER BY 
  publiced DESC 
LIMIT 
  10

selectに指定するものが少なければ問題ないのですが、10個20個と指定しないといけない、JOINするものはすべてのカラムを取得したい場合などは、どのカラムが必要なのか調べないといけないため面倒になってきます。

select指定してもまとめて取ってくれる方法があった

公式サイトを読んでいると、select指定しても他のカラムもまとめて取ってくれる方法が紹介されていました。
(今まで全然気づかなかった)

selectにテーブルクラスを渡してあげます。

$articles = $this->Articles->find()
    ->select([
    'test' => 'Articles.title'
])
    ->select($this->Articles)
    ->contain([
    'EyecatchImage',
    'Categories'
])
    ->limit(10)
    ->orderDesc('publiced');

// 生成されたSQL
SELECT 
  Articles.title AS test, 
  Articles.id AS Articles__id, 
  Articles.title AS Articles__title, 
  Articles.body AS Articles__body, 
  Articles.publiced AS Articles__publiced, 
  Articles.created AS Articles__created, 
  Articles.modified AS Articles__modified 
FROM 
  articles Articles 
  LEFT JOIN images EyecatchImage ON EyecatchImage.id = (Articles.eyecatch_image_id) 
ORDER BY 
  publiced DESC 
LIMIT 
  10
テーブルクラスを指定する場合は、配列指定ではないので注意が必要です。

アソシエーション(join)をしているテーブルのカラム取得したい場合は、下記のようにします。

$articles = $this->Articles->find()
    ->select([
    'test' => 'Articles.title'
])
    ->select($this->Articles)
    ->select($this->Articles->EyecatchImage)
    ->contain([
    'EyecatchImage',
    'Categories'
])
    ->limit(10)
    ->orderDesc('publiced');

// 発行されたSQL
SELECT 
  Articles.title AS test, 
  Articles.id AS Articles__id, 
  Articles.title AS Articles__title, 
  Articles.body AS Articles__body, 
  Articles.publiced AS Articles__publiced, 
  Articles.created AS Articles__created, 
  Articles.modified AS Articles__modified, 
  EyecatchImage.id AS EyecatchImage__id, 
  EyecatchImage.name AS EyecatchImage__name, 
  EyecatchImage.file_name AS EyecatchImage__file_name, 
  EyecatchImage.created AS EyecatchImage__created, 
  EyecatchImage.modified AS EyecatchImage__modified 
FROM 
  articles Articles 
  LEFT JOIN images EyecatchImage ON EyecatchImage.id = (Articles.eyecatch_image_id) 
ORDER BY 
  publiced DESC 
LIMIT 
  10
アソシエーションを組んでいるものはわざわざテーブルのインスタンスを生成しなくても「$this->Articles->EyecatchImage」が可能です。

このように任意もカラム指定とカラムを全てを取得することができます。

やり方は公式サイトにも記載されています。

select($fields) を呼んで、なおもテーブルのすべてのフィールドを選択したいなら、 次の方法でテーブルインスタンスを select() に渡すことができます。

// 計算された slug フィールドを含む、 articles テーブルのすべてのフィールドを取得
$query = $articlesTable->find();
$query
    ->select(['slug' => $query->func()->concat(['title' => 'identifier', '-', 'id' => 'identifier'])])
    ->select($articlesTable); // articles のすべてのフィールドを選択する

参考:特定のフィールドを選択 CakePHP - Cookboo

さいごに

今回紹介した方法は公式サイトに普通に紹介されていました。

コメント

コメントを残す

お名前(任意)
コメント:新規