質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.98%

CakePHP3 アソシエーションテーブルのレコードも同時に保存する方法

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 549

worker

score 6

実現したいこと

UsersControllerのaddアクションでUsersテーブルにレコードを新規登録する際、UserInformationsテーブルにも同時に新規登録させたいです。
今までは下に書いている「妥協したやり方」で行っていましたが、他に賢い方法があるなら変えていきたいと思っています。

DB

Usersテーブル

id email password
1 hoge@xxx.com hfeuwafjiewa

UserInformationsテーブル

id user_id hundlename
1 1 hogeさん

Usersテーブルは、UserInformationsテーブルの親テーブルとなっている
Usersテーブルにはログイン情報だけを保存し、UserInformationsテーブルにはユーザーの名前などを保存する。

妥協したやり方

これまでは以下のように、

  1. Usersテーブルに保存
  2. Usersテーブルへの保存が成功したらUserInformationsテーブルへ保存

というような2段式にしていました。
社内の先輩エンジニアのコードも同じでした。

//add.ctp
<div class="users form large-9 medium-8 columns content">
    <?= $this->Form->create($user) ?>
    <fieldset>
        <legend><?= __('Add User') ?></legend>
        <?php
            echo $this->Form->control('user_informations.handlename');
            echo $this->Form->control('email');
            echo $this->Form->control('password');
            echo $this->Form->control('status');
            echo $this->Form->control('role');
            echo $this->Form->control('last_login_at');
        ?>
    </fieldset>
    <?= $this->Form->button(__('Submit')) ?>
    <?= $this->Form->end() ?>
</div>


//UsersController
    public function add()
    {
        //newEntity
        $user = $this->Users->newEntity();
        $info = $this->Users->UserInformations->newEntity();

        if ($this->request->is('post'))
        {
            $data = $this->request->getData(); //postデータ

            //まずUsersテーブルだけ保存
            $user = $this->Users->patchEntity($user, $data);

            if ($this->Users->save($user)) 
            {
                //UserInformationsテーブル保存
                $data['user_informations']['id'] = $user['id']; //UsersID
          $info = $this->Users->UserInformations->patchEntity($info, $data['user_informations']);
                if($this->Users->UserInformations->save($info)){
                    $this->Flash->success(__('登録完了'));
                    return $this->redirect(['action' => 'index']);
                }
            }
            $this->Flash->error(__('登録失敗'));
        }
        $this->set(compact('user'));
    }

やったこと & 結果 その1

以下のようにすれば、Usersテーブルを保存したらUsersInformationsテーブルも保存となるかなと思ったのですが、Usersテーブルにのみレコードが増えており、UsersInformationsにはレコードが増えませんでした。

//Model/Entity/Users.php
class User extends Entity
{

    protected $_accessible = [
        'email' => true,
        'password' => true,
        'user_informations' => true,
    ];

    protected $_hidden = [
        'password'
    ];

    //パスワードハッシュ化
    protected function _setPassword($value)
    {
        if (strlen($value)) {
            $hasher = new DefaultPasswordHasher();

            return $hasher->hash($value);
        }
    }

}


//Model/Entity/UserInformations.php
class UserInformation extends Entity
{

    /**
     * Fields that can be mass assigned using newEntity() or patchEntity().
     *
     * Note that when '*' is set to true, this allows all unspecified fields to
     * be mass assigned. For security purposes, it is advised to set '*' to false
     * (or remove it), and explicitly make individual fields accessible as needed.
     *
     * @var array
     */
    protected $_accessible = [
        'user_id' => true,
        'handlename' => true,
        'user' => true
    ];
}


//add.ctp
<div class="users form large-9 medium-8 columns content">
    <?= $this->Form->create($user) ?>
    <fieldset>
        <legend><?= __('Add User') ?></legend>
        <?php
            echo $this->Form->control('user_informations.handlename');
            echo $this->Form->control('email');
            echo $this->Form->control('password');
            echo $this->Form->control('status');
            echo $this->Form->control('role');
            echo $this->Form->control('last_login_at');
        ?>
    </fieldset>
    <?= $this->Form->button(__('Submit')) ?>
    <?= $this->Form->end() ?>
</div>


//UsersController
    public function add()
    {
        //newEntity
        $user = $this->Users->newEntity();
        $UserInformation = $this->Users->UserInformations->newEntity();

        if ($this->request->is('post'))
        {
            $data = $this->request->getData(); //postデータ

            $user = $this->Users->patchEntity($user, $data);
            $UserInformation = $this->Users->UserInformations->patchEntity($UserInformation, $data['user_informations']);
            $user->user_information = $UserInformation;

            if ($this->Users->save($user)) {
                $this->Flash->success(__('登録完了'));

                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('登録失敗'));
        }
        $this->set(compact('user'));
    }

やったこと & 結果 その2

CakePHP3でアソシエーションごとsaveする
この方の記事を真似てみたところ、CakePHP3のバージョンが違うためか、Usersテーブルにのみレコードが増え、UserInformationsテーブルにはレコードが増えませんでした。

参考サイト

Cookbook

追記依頼事項

dbのリレーションは次のように設定してbakeしました。CASCADEにしても結果に変化はないです。
イメージ説明

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Orlofsky

    2019/03/13 23:01

    URLは https://teratail.com/help#about-markdown の [リンク] に修正してください。

    キャンセル

  • asahina1979

    2019/03/14 08:39

    dbにリレーション設定がされていればbakeコマンド作成された状態でリレーションな保存ができたはずだが。

    キャンセル

回答 2

checkベストアンサー

0

参考にされた Cookbook の後ろの方に、「アソシエーションの保存」という部分があります。
これも参考にされましたでしょうか?

試してはいませんが、

$user = $this->Users->patchEntity($user, $data);
$UserInformation = $this->Users->UserInformations->patchEntity($UserInformation, $data['user_informations']);
$user->user_information = $UserInformation;

$user->dirty('user_information', true);  // この行を追加

if ($this->Users->save($user)) {
  $this->Flash->success(__('登録完了'));

  return $this->redirect(['action' => 'index']);
}
...

でうまくいったりしないでしょうか。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/19 15:10

    dirtyを記すことが必須だったのですね、意味を理解していませんでした。
    ありがとうございました。

    キャンセル

  • 2019/03/19 16:08

    親エンティティは変更があったかどうかをチェックしていて、変更があった部分だけを更新するような仕組みになっています。
    が、関係している子エンティティについてはこのチェックができないため、「子が更新されている」ことを示すために dirty() を呼び出す必要があります。

    キャンセル

0

Usersテーブルに登録する情報にUserInformationsテーブルの
ハンドルネームの情報がないので、いずれにしろ一度ではできませんね

triggerでの処理

triggerをつかって別テーブルにnullデータをつくっておくという考え方もあります

  • table作成
create table tbl_a(aid int primary key,val_a int);
create table tbl_b(bid int primary key auto_increment,aid int,val_b int null);
  • trigger作成
drop trigger if exists trg_aft_insert_a;
delimiter //
create trigger trg_aft_insert_a after insert on tbl_a
for each row begin 
insert tbl_b(aid) values(new.aid);
end
//
  • データ投入
insert into tbl_a(aid,val_a) values(1001,2010),(1002,2020),(1003,2100);

tbl_aデータ投入によりtbl_bにデータが追加されます

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.98%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる