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

アーカイブ

タグ

作業実績 (9) Server (8) PHP (4) Linux (4) Webアプリ (4) C++ (3) laravel (3) EC-CUBE (2) JavaScript (2) Nginx (2) Vue.js (2) AWS (1) CreateJS (1) デモ (1) 書籍 (1)

技術的な情報は以下にもあります。