Laravel5.2で存在する"年月”のvalidateしてみた

Laravel5.2で、正しい年月か？をvalidateしようとして理不尽なことになったのでメモ。


# やりたいこと
Laravel5.2で、年月のフォーマットだけでなく、存在する年月かをValidateする。

# いきさつ
Laravel5.2の[使用可能なバリデーションルール](https://readouble.com/laravel/5.2/ja/validation.html#available-validation-rules)によると、「日付」「日付形式」の２パターンが利用出来る。

が。ですよ。

```ExampleController.php
$validator = Validator::make($input, [
	'from' => 'bail|required|date_format:Ym|before:"now"',
	'to' => 'bail|required|date_format:Ym|before:"now"|after:from',
]);
if ($validator->fails()) {
	throw new MyValidatorException();
}
```

のように日付形式をチェックするよう書いてやって、


from=201514
to=201520
とパラメータを渡してテストしてもエラーにならない！？

# 中を見てみる

```Illuminate\Validation\Validator.php

    /**
     * Validate that an attribute matches a date format.
     *
     * @param  string  $attribute
     * @param  mixed   $value
     * @param  array   $parameters
     * @return bool
     */
    protected function validateDateFormat($attribute, $value, $parameters)
    {
        $this->requireParameterCount(1, $parameters, 'date_format');

        if (! is_string($value) && ! is_numeric($value)) {
            return false;
        }

        $parsed = date_parse_from_format($parameters[0], $value);

        return $parsed['error_count'] === 0 && $parsed['warning_count'] === 0;
    }
```

のように、「指定された日付フォーマット」として分割できるか？だけチェックして、それぞれの正当性をチェックしてくれない。
さっきの、"201520"を渡してデバッグすると、
![スクリーンショット 2016-09-13 17.01.28.png](https://qiita-image-store.s3.amazonaws.com/0/125918/0ee9e997-1e07-3ec1-9c24-ee05636dd2de.png)
っていうありさまである。
**month=20が存在するかはチェックされない。**

参考までに、「日付」の場合こんなことをしている。

```Illuminate\Validation\Validator.php
 /**
     * Validate that an attribute is a valid date.
     *
     * @param  string  $attribute
     * @param  mixed   $value
     * @return bool
     */
    protected function validateDate($attribute, $value)
    {
        if ($value instanceof DateTime) {
            return true;
        }

        if ((! is_string($value) && ! is_numeric($value)) || strtotime($value) === false) {
            return false;
        }

        $date = date_parse($value);

        return checkdate($date['month'], $date['day'], $date['year']);
    }
```


# カスタムバリデーションの作成

Validatorクラスを自作します。
※requireParameterCount()はIlluminate\Validation\Validator.phpから引っ張ってきただけなので消してもオK。

```Validator.php
<?php

namespace Myapp\Services\Validator;


class Validator {
	
	/**
	 * Validate that an attribute is a valid year month.
	 *
	 * @param  string  $attribute
	 * @param  string $parameters 日付パラメータ
	 * @param  mixed  $value 検証値
	 * @return bool
	 */
	public function validateYearMonth($attribute, $value, $parameters)
	{
		$this->requireParameterCount(1, $parameters, 'date_ym');

		if (! is_string($value) && ! is_numeric($value)) {
			return false;
		}

		$parsed = date_parse_from_format($parameters[0], $value);

		return checkdate($parsed['month'], 1, $parsed['year']);
	}


	protected function requireParameterCount($count, $parameters, $rule)
	{
		if (count($parameters) < $count) {
			throw new InvalidArgumentException("Validation rule $rule requires at least $count parameters.");
		}
	}
}
```

アプリに登録します。

```AppServiceProvider.php
<?php

namespace Myapp\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        \Validator::extend(
        	'date_ym', 'Myapp\Services\Validator\Validator@validateYearMonth'
        );
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
    }
}

```

Controllerで検証内容を定義します。

```ExampleController.php
$validator = Validator::make($input, [
	'from' => 'bail|required|date_ym:Ym|before:"now"',
	'to' => 'bail|required|date_ym:Ym|before:"now"|after:from',
]);
if ($validator->fails()) {
	throw new MyValidatorException();
}
```

以上で、意図した通り、パラメータに"201520"のような不正な年月が指定されてもエラーになりました。

とりあえずここまで。


追記）
ちなみに、before:"now"が期待通りに行きませんね。。。。


# 参考
https://readouble.com/laravel/5.2/ja/validation.html#available-validation-rules
https://readouble.com/laravel/5.2/ja/validation.html#custom-validation-rules
https://blog.e2info.co.jp/2016/06/05/laravel_custom_validation/