hasManyとbelongsTo


この投稿について
 基本的な使い方を書いてみました。

やりたいこと
親テーブルの1レコードに対して、子テーブルの複数レコードを紐付けたい
例:ユーザが複数の趣味を持っている
親テーブル:ユーザ
子テーブル:趣味

登場するMVC

  • /Controller/UsersController.php
  • /Template/Users/edit.ctp
  • /Table/UsersTable.php
  • /Table/UserHobbiesTable.php
  • /Entity/UserHhobby.php
テーブル
    users
      id integer not null

    user_hobbies
      id integer not null
      user_id integer not null
      hobby_id integer not null
/Table/UsersTable.php
    public function initialize(array $config)
    {
        $this->setTable('users');
        $this->setDisplayField('id');
        $this->setPrimaryKey('id');

        $this->hasMany('UserHobbies', [
            'foreignKey' => 'user_id',
            'saveStrategy' => 'replace',
            'dependent' => true,
        ]);
    }
    public function beforeMarshal(\Cake\Event\Event $event, $data, $options)
    {
        // リクエストデータを操作します。
        if (!empty($data['user_hobbies']['hobby_id'])) {
            foreach ($data['user_hobbies']['hobby_id'] as $value) {
                $hobbies[] = ['hobby_id' => $value];
            }
            $data['user_hobbies'] = $hobbies;
        }
    }
    public function beforeSave(\Cake\Event\Event $event, $entity)
    {
        // 更新の場合、userbeforeSaveuser_idに紐付く、UserHobbiesテーブルを全削除します。
        if (!$entity->isNew()) {
            $this->UserHobbies->deleteAll(['user_id' => $entity->id]);
        }
    }
/Table/UserHobbiesTable.php
    public function initialize(array $config)
    {
        $this->setTable('user_hobbies');
        $this->setDisplayField('id');
        $this->setPrimaryKey('id');

        $this->belongsTo('Users', [
            'foreignKey' => 'user_id',
            'joinType' => 'LEFT'
        ]);
    }

    public function buildRules(RulesChecker $rules)
    {
        $rules->add($rules->existsIn(['user_id'], 'users'));

        return $rules;
    }
    public function beforeSave(\Cake\Event\Event $event, $entity)
    {
        // 趣味がひとつも選択されていない場合、イベントを中止します。
        if (!$entity->hobby_id) {
            $event->stopPropagation();
        }
    }
/Entity/UserHobby.php
    protected $_accessible = [
        'user_id' => true,
        'hobby_id' => true
    ];
/Controller/UsersController.php
    public function edit($id = null)
    {
        try {
            $entity = $this->Users->get($id, ['contain' => ['UserHobbies']]);
        } catch (\Exception $e) {
            return $this->redirect('index');
        }
        if ($entity->user_hobbies) {
            $entity->user_hobbies = [
                'hobby_id' => Hash::extract($entity->user_hobbies, '{n}.hobby_id'),
            ];
        }
        $this->set(compact('entity'));
    }

こうすることによって更新画面(edit)で
user_hobbies (array)
 hobby_id (array)
  0 1
  1 3
でctpに値を渡すことが出来ます。

/Template/Users/edit.ctp
<?php
    $hobby = array(
        1=>"音楽鑑賞",
        2=>"映画鑑賞",
        3=>"読書",
        4=>"ジョギング"
    );
?>
<?= $this->Form->input('user_hobbies.hobby_id', [
    'type' => 'select',
    'options' => $hobby,
    'multiple' => 'checkbox',
    'label' => false,
    'required' => false,
]) ?>

音楽鑑賞と読書を選択して正しく保存されると下記のようになります。

user_hobbiesテーブル
    id  user_id  hobby_id 
    1         1         1
    2         1         3