Quantcast
Channel: laravel5.8タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 87

Laravelでtextareaのmaxlengthと、ValidationRuleのmaxが改行によって判定ずれする

$
0
0

何が起こったか

例えば maxlength=10のtextareaがあったとして、このような入力をする。

1[改行]
2[改行]
3[改行]
4[改行]
5[改行]
form.html
<html>
(省略)
  <textareaname="body_text"maxlength="10"></textarea>
(省略)
</html>

FormRequestを継承したRequestFileでこういうRuleを指定したとすると、弾かれてしまう。

XxxRequest.php
<?phpnamespaceApp\Http\Requests;useIlluminate\Foundation\Http\FormRequest;classInformationRegistrationRequestextendsFormRequest{/**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */publicfunctionrules(){return['body_text'=>['max:10'],];}}

なぜ弾かれるか

原因は、この問題が再現した環境の改行コード差異。

役割OS改行コード
Client側Windows\r\n
Server側Linux\n

Client側では、\r\nは1文字として扱われるが、Server側では、maxルールで使われているmb_strlen()により2文字にカウントされてしまう。
vendor\laravel\framework\src\Illuminate\Validation\Concerns\ValidatesAttributes.php

ValidatesAttributes.php
namespaceIlluminate\Validation\Concerns;traitValidatesAttributes{(省略)/**
     * Validate the size of an attribute is less than a maximum value.
     *
     * @param  string  $attribute
     * @param  mixed   $value
     * @param  array   $parameters
     * @return bool
     */publicfunctionvalidateMax($attribute,$value,$parameters){$this->requireParameterCount(1,$parameters,'max');if($valueinstanceofUploadedFile&&!$value->isValid()){returnfalse;}return$this->getSize($attribute,$value)<=$parameters[0];}(省略)/**
     * Get the size of an attribute.
     *
     * @param  string  $attribute
     * @param  mixed   $value
     * @return mixed
     */protectedfunctiongetSize($attribute,$value){$hasNumeric=$this->hasRule($attribute,$this->numericRules);// This method will determine if the attribute is a number, string, or file and// return the proper size accordingly. If it is a number, then number itself// is the size. If it is a file, we take kilobytes, and for a string the// entire length of the string will be considered the attribute size.if(is_numeric($value)&&$hasNumeric){return$value;}elseif(is_array($value)){returncount($value);}elseif($valueinstanceofFile){return$value->getSize()/1024;}returnmb_strlen($value);// <----------------------- ここ}(省略)}

やったこと

てっとりばやく、かつ見逃しにくい方法として、バリデーション実行前に改行コードを上書きすることにした。
(ミドルウェアで変換するなども検討したものの、変換処理を見逃しそうだなと考えて保留、もっといい方法がある気がする。)
FormRequestのvalidationDataをOverRideし、\r\n\nへ変換する。

XxxRequest.php
<?phpnamespaceApp\Http\Requests;useIlluminate\Foundation\Http\FormRequest;classInformationRegistrationRequestextendsFormRequest{/**
     * @overRide
     * Get data to be validated from the request.
     *
     * @return array
     */protectedfunctionvalidationData(){$all=$this->all();if(isset($all['body_text'])){$all['body_text']=preg_replace("/\r\n/","\n",$all['body_text']);}return$all;}/**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */publicfunctionrules(){return['body_text'=>['max:10'],];}}

注意点

Controllerで入力値を受け取る時は、バリデートされた値を取得するvalidated()を利用しないと、変換後のデータを取ってこれない。

XxxController.ophp
<?phpnamespaceApp\Http\Controllers;useApp\Http\Requests\XxxRequest;classXxxControllerextendsController{publicfunctionregister(XxxRequest$request){$input=$request->validated();// "1\n1\n2\n1\n3\n4\n5" ←OK$input=$request->body_text;// "1\r\n1\r\n2\r\n1\r\n3\r\n4\r\n5" ←NG$input=$request->input('body_text');// "1\r\n1\r\n2\r\n1\r\n3\r\n4\r\n5" ←NG}}

Viewing all articles
Browse latest Browse all 87

Trending Articles