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

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

ただいまの
回答率

89.20%

patchEntity()を通すとNULLに変換されてしまう

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,982

shinnnn

score 15

前提・実現したいこと

PHP(CakePHP)でユーザー管理システムを作っています。
管理画面でユーザーを追加する入力フォームで、権限の入力箇所にセレクトボックスを使っております。フォームを送信すると、以下のエラーメッセージが発生しました。

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

Error: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'role' cannot be null

該当のソースコード

Model/Entity/User

<?php
namespace App\Model\Entity;

use Cake\ORM\Entity;

/**
 * User Entity.
 *
 * @property int $id
 * @property string $name
 * @property string $password
 * @property string $role
 * @property \Cake\I18n\Time $endlogined
 */
class User 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 = [
        '*' => true,
        'id' => false,
    ];

    /**
     * Fields that are excluded from JSON an array versions of the entity.
     *
     * @var array
     */
    protected $_hidden = [
        'password'
    ];
}

Model/Table/UsersTable

<?php
namespace App\Model\Table;

use App\Model\Entity\User;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

/**
 * Users Model
 *
 */
class UsersTable extends Table
{

    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->table('users');
        $this->displayField('name');
        $this->primaryKey('id');

        $this->belongsTo('Roles', [
          'foreignKey' => 'role',
          'joinType' => 'INNER'
        ]);
    }

    /**
     * Default validation rules.
     *
     * @param \Cake\Validation\Validator $validator Validator instance.
     * @return \Cake\Validation\Validator
     */
    public function validationDefault(Validator $validator)
    {
        $validator
            ->add('id', 'valid', ['rule' => 'numeric'])
            ->allowEmpty('id', 'create');

        $validator
            ->requirePresence('name', 'create')
            ->notEmpty('name');

        $validator
            ->requirePresence('password', 'create')
            ->notEmpty('password');

        $validator
            ->requirePresence('role', 'create')
            ->notEmpty('role');

        $validator
            ->add('endlogined', 'valid', ['rule' => 'datetime'])
            ->allowEmpty('endlogined');

        return $validator;
    }
}

Controller/Admin/UsersController

<?php
namespace App\Controller\Admin;

use App\Controller\AppController;
use Cake\Event\Event;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\Error\Debugger;

class UsersController extends AppController
{

    public function add()
    {
        $user = $this->Users->newEntity();
        if ($this->request->is('post')) {
            $user = $this->Users->patchEntity($user, $this->request->data);
            if ($this->Users->save($user)) {
                $this->Flash->success(__('The user has been saved.'));
                return $this->redirect(['action' => 'index']);
            } else {
                $this->Flash->error(__('The user could not be saved. Please, try again.'));
            }
        }
        $roles = $this->Users->Roles->find('list', ['limit' => 200]);
        $this->set(compact('user', 'roles'));
        $this->set('_serialize', ['user']);
    }

}

Template/Admin/Users/add.ctp

<nav class="large-3 medium-4 columns" id="actions-sidebar">
    <ul class="side-nav">
        <li class="heading"><?= __('Actions') ?></li>
        <li><?= $this->Html->link(__('List Users'), ['action' => 'index']) ?></li>
    </ul>
</nav>
<div class="users form large-9 medium-8 columns content">
    <?= $this->Form->create($user) ?>
    <fieldset>
        <legend><?= __('Add User') ?></legend>
        <?php
            echo $this->Form->input('name');
            echo $this->Form->input('password');
            echo $this->Form->input('role', ['options' => $roles]);
        ?>
    </fieldset>
    <?= $this->Form->button(__('Submit')) ?>
    <?= $this->Form->end() ?>
</div>

テーブル構成

create table roles (
  role char(100) not null default 'admin',
  PRIMARY KEY (`role`)
);

create table users (
  id int unsigned not null auto_increment,
  name varchar(100) not null,
  password varchar(255) not null,
  role char(100) not null default 'admin',
  endlogined datetime default null,
  PRIMARY KEY (`id`),
  CONSTRAINT fk_role
    FOREIGN KEY (role)
    REFERENCES roles (role)
    ON UPDATE CASCADE ON DELETE RESTRICT
);

試したこと

エラーメッセージは単純にroleカラムにNOT NULLを指定しているので、NULLを怒られているだけ。
アソシエーションで紐付けて、セレクトボックスの生成はできています。原因は、フォームで送信した値がpatchEntity()を通すとroleがNULLに変換されてしまうことだと思われます。つまり、validationDefault()に問題があるのかと思いきや、そういったおかしなバリデーションもしていないし、ということで、色々調べたりしたのですが、解決できませんでした。

みなさんのお知恵をお貸しください。よろしくお願いいたします。

問題あると思われるpatchEntityの前後の出力結果

Controller/Admin/UsersController

public function add()
{
$user = $this->Users->newEntity();
         if ($this->request->is('post')) {
             Debugger::dump($this->request->data);
             $user = $this->Users->patchEntity($user, $this->request->data);
             Debugger::dump($user);
             exit;
=====上に同じなので、省略====
}
[
    'name' => 'yamada',
    'password' => 'ymymym',
    'role' => 'admin'
     // セレクトボックスでちゃんとRolesの値が表示できていて、選択肢のうち、adminを選択して送信。
     // postで送信された値はadminなのでここまでは想定通り。
]
object(App\Model\Entity\User) {

    'name' => 'yamada',
    'password' => 'ymymym',
    'role' => null, // nullに変換される!!!!!
    '[new]' => true,
    '[accessible]' => [
        '*' => true
    ],
    '[dirty]' => [
        'name' => true,
        'password' => true,
        'role' => true
    ],
    '[original]' => [],
    '[virtual]' => [],
    '[errors]' => [],
    '[repository]' => 'Users'

}

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

CakePHP3.1.14

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • asahina1979

    2019/01/23 07:53 編集

    bake コマンドで 一式を作ってみてください。
    そして差を確認してみたらわかる気がする

    キャンセル

  • shinnnn

    2019/01/23 08:29

    bake コマンドで一式を作ってから、アソシエーション等の必要な箇所を追記・変更したものが、上記のコードになります。

    キャンセル

回答 2

checkベストアンサー

+1

https://book.cakephp.org/3.0/ja/orm/saving-data.html#belongsto
patchEntityでassociatedを指定する必要があると思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/02/02 08:01

    mosaxivさん、ありがとうございます。belongsToに'propertyName' => 'role_obj'を追記して、patchEntityの第3引数に['associated' => ['Roles']]を指定したら、正常に動作しました。

    キャンセル

0

ふむ‥

Roles に対する one to one の場合 role はネストされた配列のキーになっている。

改善しないとは思うが

echo $this->Form->input('role.id', ['options' => $roles]);

で設定されてるだろうな(更新が通らない気が一番)

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/01/23 19:27

    1つの権限は複数のユーザーに該当するので、Users 多対1Roles で設計しております。
    上記の内容では、<select>のnameまたはidが'role.id'になりますよね。もしくは、テーブル自体の設計を変更して、Rolesにidを持たせるということですか?

    キャンセル

  • 2019/01/24 08:31

    もしかして、cakephpは、テーブルに必ずidを持たせないといけない仕様なんでしょうか?

    キャンセル

  • 2019/01/24 08:57

    create tableまで見てないのでなんとも言えないけど

    belongsTo の設定だと user.role と roles.id をつなぐ構成だったはず。


    ちなみに cakeはaiが設定されてないテーブルは tableクラスから通常のインサートはできない

    キャンセル

  • 2019/01/24 22:06

    create tableは、テーブル構成のところに記述していますよ。

    キャンセル

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

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

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