Home > ブログ > Laravelにおける空文字の扱い

ブログ

Laravelにおける空文字の扱い

Laravelでは5.4以降、リクエスト内の空文字をnullに変換するようになりました。以前、Laravel 5.3でとあるシステムの開発を行っており、開発途中で新にリリースされた5.4に更新したところ、このnull化のあたりでハマりました。その時のメモです。

まず、空文字のnull変換はConvertEmptyStringsToNullミドルウェアで処理しています。5.4からこのミドルウェアが登録されたため、空文字がnullに変換されるようになりました。このため、app/Http/Kernel.phpの$middlewareからConvertEmptyStringsToNullを削除してしまえば、元の動作に戻すことはできます。

当時、ConvertEmptyStringsToNullを削除してしまおうとも考えたのですが、Laravelがそのような作り変更したのであれば、それに則った作りにした方がいいと判断し、ConvertEmptyStringsToNullはそのままにしてアプリケーション側を修正することにしました。

空文字がnullに変換されるようになったことで、多くの場合、以下の問題が発生することになると思います。

(1) データベースへの保存時

フォーム上のテキストボックス等の入力を省略した時に、従来はデータベースに空文字を設定していましたが、NULLを設定しようとします。このため、データベース側のカラムがNOT NULL属性だと保存時にエラーとなります。

このため、値が存在しない時にNULL値を格納するのが適切なカラムであればNULLを受け付けるカラムに変更したり、そうでなければリクエスト内のnull値を空文字に戻す処理が必要になります。

私は各フォームのフォームリクエストクラス内で、入力がなかった時のデフォルト値を定義するようにし、入力が省略された項目に対してデフォルト値を設定してからデータベースに保存するようにしています(実装の詳細は省きますが)。

デフォルト値の定義例

class XXXRequest extends FormRequest
{
    ...
    protected $defaultValues = [
        'page_title'      => '',
        'page_body'       => '',
        'page_published'  => null,
    ];
    ...

(2) 省略可能項目のバリデーションルール指定

5.4以前では例えば数字を受け付ける必須項目の場合は'required|numeric'のようにルールを指定し、省略可能項目の場合は'numeric'のように'required'を省略した形で指定していました。

例1: Laravel 5.4以前での省略可能項目の指定

必須項目:     'required|numeric'
省略可能項目: 'numeric'

しかし、5.4以降では'numeric'の指定だと入力を省略した時に「nullはnumericでない」と判断されてエラーとなってしまいます。5.4以降では省略可能項目には以下のように'nullable'を指定する必要があります。

例2: Laravel 5.4以降での省略可能項目の指定

必須項目:     'required|numeric'
省略可能項目: 'nullable|numeric' ← 'numeric'だとnullがnumericでないと判断されエラーになる

これはバリデーション時、入力値が空文字の場合、「入力が省略された」とみなしrules(上の例1では'numeric')を適用しないのですが、入力値がnullだと「nullが入力された」とみなし'numeric'ルールを適用してしまうためです(詳細は*1参照)。

このため、入力を省略してもバリデーションが実行されエラーになってしまいます。これを防ぐため、省略可能項目ではnullableを指定しておく必要があります。


ConvertEmptyStringsToNullに関連して発生した問題は以上です。システム内の全てのフォームで見直しが必要になるので、運用中のシステムを5.4以降にアップグレードする場合は、ConvertEmptyStringsToNullを削除してしまう方が安全かもしれません。

ConvertEmptyStringsToNullの追加に関しては結構インパクトのある変更だと思うのですが、https://readouble.com/laravel/5.4/ja/upgrade.htmlの5.4へのアップグレードガイドにはなぜか記述がありません。当時、nullの扱いに悩んだのでその時のまとめでした。

[参考]

(*1) このあたりのrulesを適用するしないの制御はvendor/laravel/framework/src/Illuminate/Validation/Validator.phpのpresentOrRuleIsImplicit()で行っています。入力が空文字だった場合、$implicitRulesに指定されているruleのみ実行し、その他は無視するようになっています。

$implicitRules = [
        'Required', 'Filled', 'RequiredWith', 'RequiredWithAll', 'RequiredWithout',
        'RequiredWithoutAll', 'RequiredIf', 'RequiredUnless', 'Accepted', 'Present',
    ];

$implicitRulesにはrequred系のルールが並んでいるので、空文字が入力された場合、入力の必須チェックだけが実行されることになります。

このため、空文字の場合、'required'での必須チェックは行われるが'numeric'によるチェックは行われません。一方、nullの場合、通常の入力として扱われるので、'numeric'のチェックが必ず行われます。このため、'nullable'がない'とnumeric'でエラーになります。

省略可能項目に対するnullable指定についてはhttps://readouble.com/laravel/5.4/ja/validation.html#a-note-on-optional-fieldsにも説明があります。

※このあたりの処理はLaravel 5.4, 5.5 LTSで確認しています。

投稿日:2019/05/14 14:24

タグ: PHP laravel

Top

アーカイブ

タグ

Server (24) 作業実績 (17) PHP (15) プログラミング (15) ネットワーク (12) C (8) OpenSSL (8) C++ (7) PHP関連更新作業 (7) EC-CUBE (6) Webアプリ (6) Nginx (5) AWS (4) 書籍 (4) laravel (4) Linux (4) JavaScript (4) Rust (3) Vue.js (3) 与太話 (3) Symfony (2) Golang (2) OSS (1) MySQL (1) お知らせ (1) デモ (1) CreateJS (1) Apache (1)