LoginSignup
11
6

More than 3 years have passed since last update.

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

Posted at

何が起こったか

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

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

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

XxxRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
class InformationRegistrationRequest extends FormRequest
{
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        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
namespace Illuminate\Validation\Concerns;
trait ValidatesAttributes
{
(省略)
    /**
     * Validate the size of an attribute is less than a maximum value.
     *
     * @param  string  $attribute
     * @param  mixed   $value
     * @param  array   $parameters
     * @return bool
     */
    public function validateMax($attribute, $value, $parameters)
    {
        $this->requireParameterCount(1, $parameters, 'max');

        if ($value instanceof UploadedFile && ! $value->isValid()) {
            return false;
        }

        return $this->getSize($attribute, $value) <= $parameters[0];
    }
(省略)
    /**
     * Get the size of an attribute.
     *
     * @param  string  $attribute
     * @param  mixed   $value
     * @return mixed
     */
    protected function getSize($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)) {
            return count($value);
        } elseif ($value instanceof File) {
            return $value->getSize() / 1024;
        }

        return mb_strlen($value); // <----------------------- ここ
    }
(省略)
}

やったこと

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

XxxRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
class InformationRegistrationRequest extends FormRequest
{
    /**
     * @overRide
     * Get data to be validated from the request.
     *
     * @return array
     */
    protected function validationData()
    {
        $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
     */
    public function rules()
    {
        return [
            'body_text' => ['max:10'],
        ];
    }
}

注意点

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

XxxController.ophp
<?php

namespace App\Http\Controllers;

use App\Http\Requests\XxxRequest;
class XxxController extends Controller
{
    public function register(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
    }
}

11
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
6