【CakePHP3】ORMでSELECT時、任意のcolumnを[key => value]で返す方法
2020/04/12
masyus
CakePHP2を経験された方がCakePHP3で躓きやすいポイントの1つとして、
find('list')とselect(['id', 'name'])で組み合わせれば [key => value] で返せる
ことを期待する点かと思います。
実際は、PRIMARY KEYのidはkeyとして返してくれますがvalueはselect()の指定では返してくれませんでした。どのように実装をすれば良いか、備忘録を兼ねて方法を2つ紹介いたします。検証したCakePHPのバージョンは3.6.13、PHPは7.3です。
実装方法1: find('list')時にkeyFieldとvalueFieldを指定する
find('list')の際にkeyField
とvalueField
を指定することで実現可能です。keyおよびvalueは好きなcolumnを指定できます。サンプルコードは下記になります。
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\View\View;
class ArticlesController extends AppController
{
public function initialize()
{
parent::initialize();
$this->loadModel('Articles');
$this->loadModel('MasterArticleTypes');
}
/**
* 一覧画面
*/
public function index()
{
$this->paginate = [
'contain' => ['Authors'],
];
$articles = $this->paginate($this->Articles);
// 記事タイプのkey・valueを取得
$articleTypeList = $this->MasterArticleTypes
->find('list', [
'keyField' => 'id',
'valueField' => 'name',
])
->toArray();
$this->set(compact('articles', 'articleTypeList'));
}
}
おそらくこの実装方法を一番よく使うことになると思われますが、keyとvalueの後ろに都度Field
をつける必要があるのが手間ですし冗長に感じます。その意味ではCakePHP2のほうが直感的に書けましたが、こればかりは仕方ないかもしれません。
実装方法2: 対象Tableのinitialize()にてsetDisplayField()を設定する
Tableクラスのinitialize()でsetDisplayField()
関数を活用することで、予めfind('list')でデータ取得時のデフォルトvalueを決め打つ方法です。[key => value]で返すcolumnを固定したい場合には効果的かと思われます。サンプルコードは下記になります。
<?php
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
/**
* MasterArticleTypes Model
*
* @method \App\Model\Entity\MasterArticleType get($primaryKey, $options = [])
* @method \App\Model\Entity\MasterArticleType newEntity($data = null, array $options = [])
* @method \App\Model\Entity\MasterArticleType[] newEntities(array $data, array $options = [])
* @method \App\Model\Entity\MasterArticleType|bool save(\Cake\Datasource\EntityInterface $entity, $options = [])
* @method \App\Model\Entity\MasterArticleType|bool saveOrFail(\Cake\Datasource\EntityInterface $entity, $options = [])
* @method \App\Model\Entity\MasterArticleType patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
* @method \App\Model\Entity\MasterArticleType[] patchEntities($entities, array $data, array $options = [])
* @method \App\Model\Entity\MasterArticleType findOrCreate($search, callable $callback = null, $options = [])
*
* @mixin \Cake\ORM\Behavior\TimestampBehavior
*/
class MasterArticleTypesTable extends Table
{
/**
* Initialize method
*
* @param array $config The configuration for the Table.
* @return void
*/
public function initialize(array $config)
{
parent::initialize($config);
$this->setTable('article_types');
$this->setPrimaryKey('id');
$this->setDisplayField('name'); // bakeすると、デフォルトはidになることが多いので注意
}
}
setDisplayField()
を指定しておきますと、Controller側では下記の記述で実現できるようになります。
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\View\View;
class ArticlesController extends AppController
{
public function initialize()
{
parent::initialize();
$this->loadModel('Articles');
$this->loadModel('MasterArticleTypes');
}
/**
* 一覧画面
*/
public function index()
{
$this->paginate = [
'contain' => ['Authors'],
];
$articles = $this->paginate($this->Articles);
// 記事タイプのkey・valueを取得
$articleTypeList = $this->MasterArticleTypes
->find('list')
->toArray();
$this->set(compact('articles', 'articleTypeList'));
}
}
1点気になることがあるとすれば、初見の方はTableクラスを確認しに行かないと「何故[key => value]を[id => name]で返せるのか?」が分からないため、やや暗黙知的な印象はあります。