CakePHP3のkeyFieldとvalueFieldでカラムを登録したら他のカラムを自動登録する

11 回閲覧されました
みなさんこんにちは、jonioです。
今回はCakePHP3で、テーブルのあるカラムの値を登録すると同じレコードの他のカラムの値も自動的に登録される方法を解説します。
CakePHPのバージョン
3.2で動作確認をしています。
状況
例えばusersテーブルにnameカラムとemailカラムとpasswordカラムがあったとします。
postsテーブルに紐づくビューでusersテーブルのnameカラムのデータをリストで表示させて名前を選択して登録すると自動的に名前に紐づいているemailカラム・passwordカラムもpostsテーブルに登録させたいみたいな時に使えます。
簡潔にすると「レコードの1つのカラムを登録すると他のカラムが自動的に登録される」と言えます。
今回はnameカラムを登録した時にemailカラムも登録できるようにします。

allコマンドでモデル・ビュー・コントローラーは作成したのを前提に話を進めます。
ユーザーの登録(usersテーブル)はコントローラー・ビュー共にデフォルトのまま使っているのでコードの掲載はしません。
テーブル
usersテーブルは下記です、nameカラム・emailカラム・passwordカラムがありますがnameカラムとemailカラムを使います。

postsテーブルは下記です、titleカラム・contentカラムがあります。

ビュー
コードを下記にします。
<div class="row">
<aside class="column">
<div class="side-nav">
<h4 class="heading"><?= __('Actions') ?></h4>
<?= $this->Html->link(__('List Posts'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
</div>
</aside>
<div class="column-responsive column-80">
<div class="posts form content">
<?= $this->Form->create() ?>
<fieldset>
<legend><?= __('Add Post') ?></legend>
<?= $this->Form->select('title', ['options' => $users]) ?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
</div>
</div>
13行目が登録フォームです、「’options’ => $users」の$usersはコントローラーから情報を渡します。
コントローラー
コードを下記にします。
public function add()
{
$this->loadModel('Users');
$users = $this->Users
->find('list', ['keyField' => 'name', 'valueField' => 'name'])
->toArray();
$post = $this->Posts->newEmptyEntity();
if ($this->request->is('post')) {
$postData = $this->request->getData();
$user = $this->Users->find()->where(['name' => $postData['title']])->first();
if ($user) {
$postData['content'] = $user->email;
$post = $this->Posts->patchEntity($post, $postData);
if ($this->Posts->save($post)) {
$this->Flash->success(__('The post has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The post could not be saved. Please, try again.'));
}
}
$this->set(compact('users'));
}
5行目の「[‘keyField’ => ‘name‘, ‘valueField’ => ‘name‘]」は「KeyField」が連想配列のkeyに当たりValueFieldが連想配列のvalueに当たります。
keyとvalueに「name」を使っていますがusersテーブルのnameカラムになります、存在しないカラム名を使うとエラーになります。
valueFieldのnameが登録フォームに表示される名前でkeyFieldのnameが登録フォームで名前を選択して登録した時にテーブルに保存される値です。
本当に値が入っているかを確認します。
dd関数を使います。
public function add()
{
$this->loadModel('Users');
$users = $this->Users
->find('list', ['keyField' => 'name', 'valueField' => 'name'])
->toArray();
dd($users); //←この行を追加
・
・
・
登録画面をリロードすると下記の表示になります。
//左の項目がkeyFieldで右の項目がvalueField
[
'jonio' => 'jonio',
'jonio2' => 'jonio2',
]
右の項目が登録フォームに表示される名前で左の項目が実際に登録される値です。

カラム名を2つともnameにして見にくいですが表示される名前と登録される名前が同じの方が対応がわかりやすいと思いわざと同じにしています。
コントローラーのコードに話を戻して登録フォームで名前を選択して登録した時にpostsテーブルのtitleカラムにusersテーブルのnameカラムの値が登録されているかを確認します。
dd関数を使います。
public function add()
{
$this->loadModel('Users');
$users = $this->Users
->find('list', ['keyField' => 'name', 'valueField' => 'name'])
->toArray();
$post = $this->Posts->newEmptyEntity();
if ($this->request->is('post')) {
$postData = $this->request->getData();
dd($postData); //←を追加
登録フォームで名前を選択して登録すると下記の表示になります。
//名前の選択でjonio2を選択しています
[
'title' => 'jonio2',
]
「dd($postData)」ではなく「dd($postData[‘title’])」にするとtitleカラムの値(jonio2)を取得できているのを確認できます。
話をまたコントローラーのコードに戻して今のままではcontentカラムに値を登録することができませんがそれをできるようにする媒体みたいな物が13行目です。
これがあることで登録フォームで選択した名前(今回の例だとjonio2)があるレコードを取得しています。
ちなみに「where([‘name’ => $postData[‘title’]])」で登録レコードを選択して「$postData[‘title’]」でjonio2が取得できます。
今からcontentカラムにemailを登録しますが13行目の$userに何が入っているかをdd関数で見てみます。
public function add()
{
$this->loadModel('Users');
$users = $this->Users
->find('list', ['keyField' => 'name', 'valueField' => 'name'])
->toArray();
$post = $this->Posts->newEmptyEntity();
if ($this->request->is('post')) {
$postData = $this->request->getData();
$user = $this->Users->find()->where(['name' => $postData['title']])->first();
dd($user); //←この行
するとemailカラムの値を含んだオブジェクトが取得できます。
object(App\Model\Entity\User) id:0 {
'id' => (int) 2
'name' => 'jonio2'
'email' => 'b@demo.com'
'password' => 'xxxxxxx'
'[new]' => false
'[accessible]' => [ ]
'[dirty]' => [ ]
'[original]' => [ ]
'[virtual]' => [ ]
'[hasErrors]' => false
'[errors]' => [ ]
'[invalid]' => [ ]
'[repository]' => 'Users'
}
よって16行目の「$user->email」でjonio2に紐づいたemailカラムの値を取得してpostsテーブルのcontentカラム($postData[‘content’])に登録します。
あとはコードを読めば分かると思います。