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

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

ただいまの
回答率

87.90%

cakephpで1つのフォームで2つのテーブルにエンティティを保存したい

解決済

回答 1

投稿

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

score 8

前提・実現したいこと

PHP(CakePHP)で1つの入力フォームから2つのテーブルにエンティティを保存するシステム作っています。
CookBookを参考にpatchEntity()でassociatedオプションを使えばできるとのことでしたがうまくいきません。

発生している問題・エラーメッセージ

エラーメッセージは出ませんが、Aテーブルにはレコードが保存されるがBテーブルには保存されません。

該当のソースコード

テーブル構造

ATable
id|int(10) unsigned auto_increment|primary_key
device_no|varchar(10)
device_bit_no|tinyint(4)
report_item_name|varchar(50)
decimal_point|tinyint(4)
display_unit|varchar(10)
print_position_p|decimal(9,3)
print_position_x|decimal(9,3)
print_position_y|decimal(9,3)

BTable
id|int(10) unsigned auto_increment|primary_key
device_no|varchar(10)
device_bit_no|tinyint(4)
base_valueF|decimal(12,3)
base_valueT|decimal(12,3)

ATable.php

    public function initialize(array $config)
    {
        parent::initialize($config);

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

        $this->addBehavior('Timestamp');

        $this->hasOne('B', [
            'foreignKey' => ['device_no', 'device_bit_no'],
            'bindingKey' => ['device_no', 'device_bit_no'],
            'joinType' => 'INNER'
        ]);
    }

BTable.php

    public function initialize(array $config)
    {
        parent::initialize($config);

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

        $this->addBehavior('Timestamp');

        $this->belongsTo('A', [
          'foreignKey' => ['device_no', 'device_bit_no'],
          'bindingKey' => ['device_no', 'device_bit_no'],
          'joinType' => 'INNER'
        ]);

      }

AController.php

public function add()
    {
        $this->loadModel('B');
        $a = $this->A->newEntity(['associated'=>['B']]);
        if ($this->request->is('post','put')) {
            $data = $this->request->getData();
             $a = $this->A->patchEntity($a, $data, ['associated'=>['B']]);
            if ($this->A->save($a)) {
                $this->Flash->success(__('登録に成功しました'));

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

add.ctp

<script>

function save(){
   document.MainForm.submit();
}

</script>

<div class="box_page_bg">
  <div class="box_main">
    <?php echo $this->Form->create($a, ['name'=>'MainForm', 'templates'=>'input_template']); ?>
    <div class="box_main_table">
      <table>
        <tbody id="categories">
          <tr>
            <td>デバイス番号</td>
            <td><?php echo $this->Form->control('device_no'); ?></td>
          </tr>
          <tr>
            <td>デバイスビット番号</td>
            <td><?php echo $this->Form->control('device_bit_no'); ?></td>
          </tr>
          <tr>
            <td>帳票項目名称</td>
            <td><?php echo $this->Form->control('report_item_name'); ?></td>
          </tr>
          <tr>
            <td>小数点</td>
            <td><?php echo $this->Form->control('decimal_point'); ?></td>
          </tr>
          <tr>
            <td>表示単位</td>
            <td><?php echo $this->Form->control('display_unit'); ?></td>
          </tr>
          <tr>
            <td>基準値To</td>
            <td><?php
            $value = $a['b.base_valueT'];
            $finalval = number_format((float)$value, 3);
            echo $this->Form->control('b.base_valueT',['value'=>$finalval]);
            ?></td>
          </tr>
          <tr>
            <td>基準値From</td>
            <td><?php
              $value = $a['b.base_valueF'];
              $finalval = number_format((float)$value, 3);
              echo $this->Form->control('b.base_valueF',['value'=>$finalval]);
            ?></td>
          </tr>
          <tr>
            <td>印刷位置ページ</td>
            <td><?php echo $this->Form->control('print_position_p'); ?></td>
          </tr>
          <tr>
            <td>印刷位置X</td>
            <td><?php echo $this->Form->control('print_position_x'); ?></td>
          </tr>
          <tr>
            <td>印刷位置Y</td>
            <td><?php echo $this->Form->control('print_position_y'); ?></td>
          </tr>
        </tbody>
      </table>
    </div>
    <div class="clearfix">
      <div class="float_l">
        <a href="<?php echo SITE_URL;?>/<?php echo $controller?>/index" class="btn min color4">キャンセル</a>
        <a href="javascript:void(0)" class="btn min color2" onclick="save()">保存</a>
      </div>
    </div>
</div>

試したこと・気づいたこと

・print_rで変数の内容を確認
・フォームから送られたdataはBの配列を含む複合配列だった。
・patchEntity()を実行するとBの配列が消える。

$data(patchEntity前)

Array
(
    [device_no] => test
    [device_bit_no] => 0
    [report_item_name] => test
    [decimal_point] => 0
    [display_unit] => test
    [b] => Array
        (
            [base_valueT] => 100.000
            [base_valueF] => 10.000
            [judgment_id] => 4
        )

    [print_position_p] => 1
    [print_position_x] => 1
    [print_position_y] => 1
    [create_user] => 2
    [modified_user] => 2
)

$a(patchEntity後)

App\Model\Entity\A Object
(
    [device_no] => test
    [device_bit_no] => 0
    [report_item_name] => test
    [decimal_point] => 0
    [display_unit] => test
    [print_position_p] => 1
    [print_position_x] => 1
    [print_position_y] => 1
    [create_user] => 2
    [modified_user] => 2
    [[new]] => 1
    [[accessible]] => Array
        (
            [device_no] => 1
            [device_bit_no] => 1
            [report_item_name] => 1
            [decimal_point] => 1
            [display_unit] => 1
            [print_position_p] => 1
            [print_position_x] => 1
            [print_position_y] => 1
            [delete_flg] => 1
            [create_user] => 1
            [created] => 1
            [modified_user] => 1
            [modified] => 1
        )

補足情報(FW/ツールのバージョンなど)

外部キーはdevice_noとdevice_bit_noの2つを使ってつなぐように言われたんですがそもそもそんなアソシエーション組めるのでしょうか?

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • nojimage

    2019/09/09 11:09 編集

    Entity/A.php のエンティティクラスを提示できますか?($_accessible の設定を知りたいです。)

    キャンセル

  • htkhtk

    2019/09/09 11:19

    エンティティクラスはbakeしたまま変更してないです。
    Entity/A.php
    ```
    class A 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 = [
    'device_no' => true,
    'device_bit_no' => true,
    'report_item_name' => true,
    'decimal_point' => true,
    'display_unit' => true,
    'print_position_p' => true,
    'print_position_x' => true,
    'print_position_y' => true,
    'created' => true,
    'modified' => true,
    ];
    }

    ```

    device_no, device_bit_noはセンサーの識別番号らしいです。センサーを定義するテーブルでもあればいいと思うのですがどうも現場のセンサーエンジニア的にはこの構造がいいらしいです

    キャンセル

回答 1

checkベストアンサー

0

Entity/A.php の $_accessible へ 'b' => true を追加してください。

CakePHPの、Table::newEntity/patchEntityには一括代入(Mass Assignment)攻撃に対しての保護機構があります。これはエンティティクラスの $_accessible で許可されたフィールド以外には、newEntity/patchEntity時にエンティティの値をセットしないというものです。

この機構により、フォームから送信された b のプロパティはAのエンティティにはセットされない状態となっています。

参考:
一括代入 (Mass Assignment) | エンティティー - 3.8
アクセス可能なフィールドの変更 | データの保存 - 3.8

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/09/09 12:33

    単一外部キー(a_id)の場合は、bakeが対応しているので b => true が追加されていて、bakeしたエンティティがそのまま使えます。しかし、今回のように複合キーの場合は考慮されていないので、手動で追加してやる必要があります。

    キャンセル

  • 2019/09/09 15:16

    すごい!できました!ありがとうございました!

    キャンセル

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

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

関連した質問

同じタグがついた質問を見る