

# 前提・実現したいこと

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

* * *

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

[code]

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

* * *

# 該当のソースコード

Model/Entity/User

[code]

    <?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'
        ];
    }
    
[/code]

Model/Table/UsersTable

[code]

    <?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;
        }
    }
    
[/code]

Controller/Admin/UsersController

[code]

    <?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']);
        }
    
    }
    
[/code]

Template/Admin/Users/add.ctp

[code]

    <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>
    
[/code]

テーブル構成

[code]

    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
    );
    
[/code]

* * *

# 試したこと

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

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

* * *

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

Controller/Admin/UsersController

[code]

    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;
    =====上に同じなので、省略====
    }
    
[/code]

[code]

    [
        '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'
    
    }
    
[/code]

* * *

# 補足情報（FW/ツールのバージョンなど）

CakePHP3.1.14


CC BY-SA 4.0
2019-01-25T22:06:17.460
52325
2019-07-13T05:00:55.080
31909
question
php mysql cakephp
patchEntity()を通すとNULLに変換されてしまう


CakePHPではアソシエーションで定義した関連は自動的にエンティティのフィールドと認識されます。

UsersTableで

[code]

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

としているので、User.role は Roleエンティティとして処理するようになっています。

ここで、User.roleは既にテキストフィールドとして定義されているので、競合が起きています。

OOPerさんの指摘の通り、POST値を`'role' => ['role' => 'admin']`のような形式で与えるか、  
Rolesのアソシエーションのプロパティ名を変更して、競合を解消します。

[code]

            $this->belongsTo('Roles', [
              'foreignKey' => 'role',
              'joinType' => 'INNER',
              'propertyName' => 'role_obj',
            ]);
    
[/code]

これで、User.roleはテキスト値、User.role_objはRoleエンティティでの指定ができるようになります。


CC BY-SA 4.0
2019-01-28T02:53:53.200
52365
2019-01-28T02:53:53.200
2668
52325
answer