CakaPHP4でユーザ追加時に関連するテーブルも同時に追加を行う方法

# はじめに
実務でCakePHPを使って社内業務用のWEBアプリケーションを開発しました。
その際、細かい仕様の部分を悩みながら開発を進めたので、共有させていただきたいと思います。
（※ 実務で開発した内容そのままではなく、公開できるように内容を修正しております。）

# 開発環境
Mac Monteray 12.2.1
CakePHP 4.3.7
PHP 7.4.2
MySQL 5.7.26

# 本記事記載内容
ユーザ追加時に、ユーザのテーブルだけではなく、関連するその他のテーブルに関しても更新を行う、という方法について記載いたします。
所々解説用にXdebugのスクリーンショットを記載いたします。

# DB設計
### ER図
![20220413_ER図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419781/f1a3a292-c924-0c3b-fa53-57ee1075a482.png)

### 補足
本記事ではusers, affiliation_committees, committees, rolesを利用いたします。
ユーザは複数の委員会に所属可能で、委員会ごとに役割があるとしています。

### 投入済みデータ
**committees**
![20220513_committees_DB登録データ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419781/d307f4f6-ca98-00c0-dbb8-ef941e428b6f.png)

**roles**
![20220513_roles_DB登録データ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419781/6af51d98-6e98-91f5-06f1-185e42ef13a6.png)


# コード内容
```php: UsersController.php
    public function add()
    {
        $user = $this->Users->newEmptyEntity();

        if ($this->request->is('post')) {
            # associated の内容を追記                                         
            $user = $this->Users->patchEntity($user, $this->request->getData(), ['associated' => ['AffiliationCommittees']]);
            if ($this->Users->save($user)) {
                $this->Flash->success(__('The user has been saved.'));

                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The user could not be saved. Please, try again.'));
        }

        // 部署一覧を取得
        $departments = $this->Users->Departments->find('list', [
            'keyField' => 'id',
            'valueField' => 'department_name'
        ]);
        // 委員会一覧を取得
        $committees = $this->Users->Committees->find('list', [
            'keyField' => 'id',
            'valueField' => 'committee_name'
        ]);
        // 役割一覧を取得
        $roles = $this->Users->Roles->find('list', [
            'keyField' => 'id',
            'valueField' => 'role_name'
        ]);

        $this->set(compact('user', 'departments', 'committees', 'roles'));
    }
```
**注意点**
getData() の後にassociated をキーにした連想配列を記載します。
ここに関連するモデルの名称を記載します。
```php:User.php
class User extends Entity
{
    protected $_accessible = [
        'user_name' => true,
        'email' => true,
        'password' => true,
        'department_id' => true,
        'created' => true,
        'modified' => true,
        # POSTした値として受け取れるように下記を追記
        'affiliation_committees' => true,
    ];
}
```
```php:UsersTable.php
    public function initialize(array $config): void
    {
        parent::initialize($config);

        $this->setTable('users');
        $this->setDisplayField('id');
        $this->setPrimaryKey('id');

        $this->addBehavior('Timestamp');
        # AffiliationSubjects とのアソシエーションを追記
        $this->hasMany('AffiliationCommittees');
    }
```
```php:add.php
<div class="row">
    <aside class="column">
        <div class="side-nav">
            <h4 class="heading"><?= __('メニュー') ?></h4>
            <?= $this->Html->link(__('ユーザ一覧'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
        </div>
    </aside>
    <div class="column-responsive column-80">
        <div class="users form content">
            <?= $this->Form->create($user) ?>
            <fieldset>
                <legend><?= __('ユーザ追加') ?></legend>
                <?php
                echo $this->Form->control('user_name', ['label' => 'ユーザ名']);
                echo $this->Form->control('email', ['label' => 'Eメールアドレス']);
                echo $this->Form->control('password', ['label' => 'パスワード']);
                echo $this->Form->control('department_id', ['label' => '部署', 'options' => $departments]);
                # 下記2行追記                
                echo $this->Form->control('affiliation_committees.0.committee_id', ['label' => '委員会', 'options' => $committees]);
                echo $this->Form->control('affiliation_committees.0.role_id', ['label' => '権限', 'options' => $roles]);
                ?>
            </fieldset>
            <?= $this->Form->button(__('追加')) ?>
            <?= $this->Form->end() ?>
        </div>
    </div>
</div>
```
**注意点**
add.phpで記載している追記の内容は、「テーブル名.要素の番号(0から始める).カラム名」と記載します。

# 動作確認
下記画像のように入力してから、追加ボタンをクリックします。
![20220514_ユーザ追加前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419781/a770938f-45b3-0044-6a59-1dadfcb6d713.png)

Xdebugでデバッグして中身をみてみます。
$this - request - data にPOSTされたデータが入っています。
![20220515_POST時Xdebug.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419781/b591fbf6-6c98-01a2-840e-bdc6f74fdad8.png)

patchEntityを実行する前の $userの _fiels には何も値が入っていませんが…
![20220515_Userエンティティ確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419781/7f6e5542-5caa-cf70-062c-16ca99522c39.png)

patchEntityを実行した後には下記画像のように値が入っています。
add.php で要素の番号を入れたのはアソシエーションが1対多だからです。
画面の構成上、追加する値が1つだけだったので0という値を入れていました。
逆に言うと、要素の番号を増やせば、1度に大量のデータの更新も可能です。
![20220515_Userエンティティ更新後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419781/45b30529-1406-3720-96a1-101011164a6c.png)

# 確認
ユーザテーブルに入力した内容のユーザが追加されました。
![20220515_ユーザテーブル確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419781/bb38d48e-725e-720c-6ebb-229e6dbaf32f.png)
関連するテーブルの方にも同時に追加が行われています。
![20220515_所属委員会確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419781/3b282baa-c324-5370-636d-670adf076b4b.png)

# 終わりに
ネットで調べると1つのテーブルの追加や編集の方法はすぐにわかるのですが、関連するテーブルもあわせてとなるとなかなかいいものが見つからず、手を焼きました...
ご時世的にPHPのフレームワークといえばLaravelなのかとも思いますが、実務で困っている方のご参考になればと思います。
この他にも実務の中で調査した内容に関しては随時更新していこうと思います。
何かご不明な点があればご連絡のほどよろしくお願いいたします。