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

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

ただいまの
回答率

87.34%

CakePHP3のフォームヘルパーで作成したSelectボックスのEmpty値の項目選択するとエラーが出る

受付中

回答 2

投稿 編集

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

score 4

前提・実現したいこと

資料の貸出管理のシステムを作っています。
Materialsテーブルのuser_idフィールドが空だったら貸し出していない状態
同user_idフィールドがセットされていたら、貸出状態という仕組みを考えています。

該当のソースコード

bin\cake bake all materials
を実行後、追加画面(add.ctp)をカスタマイズ中です。

add.ctp

echo $this->Form->control('user_id', ['label' => '貸出先', 'options' => $users, 'empty' => '貸出なし']);


上記のようにemptyを指定して、Selectボックスの最初に「貸出なし」という項目をSelectボックスの最初に表示することに成功しました。
「貸出なし」以外のユーザーを選択しているときは何の問題もなくデータを追加できます。
しかし、「貸出なし」つまりEmptyの項目を選んでいると下記のエラーが発生します。

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

Error: SQLSTATE[HY000]: General error: 1364 Field 'user_id' doesn't have a default value 


気になるのはエラーメッセージに表示されているINSERT文にuser_idが見当たらないことです。

Emptyの値の場合、コントローラ側で何か処理をするべきなのか、
それともMaterialsTable.phpで定義を変えれば済む話なのかさっぱり分かりません。

CakePHPで開発を始めたばかりの手探り状態で、完全に行き詰りました。
皆さんのお知恵をお貸しください。

試したこと

Insert時に空の時のデフォルト値が指定されていないのが原因かと思い、テーブルのバリデーション定義を書き換えてみました。
MaterialsTable.php

        $validator
            ->integer('user_id')
            ->allowEmptyString('user_id', null, 'create');

また、アソシエーションのjoinTypeにINNERを指定しているのが原因かと思い、LEFTに変えてみましたが変化ありませんでした。

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

質問の投稿以降に行った検証で気づいた点があります。
追加画面で貸出なし(empty)を選択しても、
MaterialsController.phpのadd()で
$material('user_id') =0;
と強制的に値を代入すれば、追加は成功するということです。

ソースコードは下記のとおりです。

public function add()
    {
        $material = $this->Materials->newEntity();
        if ($this->request->is('post')) {
            $material = $this->Materials->patchEntity($material, $this->request->getData());
            $material['status'] = '';
            $material['creator'] = $this->Auth->user('id');
            $material['user_id'] = 0; //この行が追加した部分です。
            if ($this->Materials->save($material)) {
                $this->Flash->success(__('資料情報は保存されました。'));

                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('資料情報は保存されませんでした。もう一度お試しください。'));
        }
        $users = $this->Materials->Users->find('list', ['limit' => 200]);
        $this->set(compact('material', 'users'));
    }

この結果から推測してみました。
貸出先でEmpty以外のユーザーが選ばれている場合、
$this->request->getData() にはuser_idが含まれた状態になります。
その値はpatchEntityによって、$material['user_id']に一括代入されるので、
無理に代入しなくても、正常にレコードの追加が実行されます。

しかし、貸出先でEmpty(貸出なし)を選択した場合、
$this->request->getData()にuser_idの項目が存在しなくなるのではないかと思います。
その場合、patchEntityも$material['user_id']に代入は行わず、
レコードの追加実行時に
「Field 'user_id' doesn't have a default value 」というエラーを出力するに至るのではないかと思うのです。

それで、前出のコードのようにpatchEntityの実施後、
$material['user_id'] = 0;
のように強制的に値を代入してやれば、レコードの追加実行時にもエラーにならずに済むということになります。

ということは$this->request->getData()に
user_id の値が含まれているかいないかを判定して、
含まれていない時のみ、貸出されていないことを示す 0 を強制的に代入すればいいはずです。

でも、その判定式の書き方が分かりません。

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

PHP 7.2.4
CakePHP 3.8.2
MySQL 8.0.17

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • nojimage

    2019/09/09 10:56

    データベースの materials テーブルの定義(特にuser_idの定義)を提示できますか?

    キャンセル

  • gunung

    2019/09/11 01:41

    bin\cake bake migration CreateMaterials name:string[100] user_id:integer status:string[10] created creator:integer

    bin\cake bake migration CreateUsers username:string[100] password:string[100] sex:integer authority:string[10] status:string[10] created creator:integer

    関連部分はこんな感じです。
    migrationを使ってテーブル作りましたので、migrationに打ち込んだコマンドです。
    MaterialsTable.phpではMaterials.user_idとUsers.idのアソシエーションが適切に設定されているように見えます。

    キャンセル

回答 2

0

"Materialsテーブルのuser_idフィールドが空だったら貸し出していない状態" としたいのであれば、 materials.user_id のNOT NULL制約を外すのがよいと考えます。

bin/cake bake migration CreateMaterials name:string[100] user_id:integer status:string[10] created creator:integer

を実行した場合、マイグレーションファイルには以下のように書き出されているはずです。

<?php
use Migrations\AbstractMigration;

class CreateMaterials extends AbstractMigration
{
{
// (略)
        $table->addColumn('user_id', 'integer', [
            'default' => null,
            'limit' => 11,
            'null' => false, // ここを true に置き換え
        ]);
// (略)

この 'null' => false, を 'null' => true, に置き換えて、user_idのNOT NULL制約を外しましょう。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

デフォルト値を与えたいのであれば以下のようarray_mergeでデフォルト値にPOST値をマージするのが簡単でしょう。

$default = [
    'user_id' => 0;
];
$data = array_merge($default, $this->request->getData());

$material = $this->Materials->patchEntity($material, $data);

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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

  • トップ
  • PHPに関する質問
  • CakePHP3のフォームヘルパーで作成したSelectボックスのEmpty値の項目選択するとエラーが出る