CakePHP3.6 validationの実行の順番は下から行われている


ユーザ登録をする際に、18歳未満の人は登録できないようにしたかったのですが、 バリデーションの順番でエラーになってしまった。

元のコード

src/Model/Table/UsersTable.php
<?php
namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\Database\Schema\TableSchema;
use DateTime;

class ProfilesTable extends Table
{

    public function validationDefault(Validator $validator)
    {
        $validator
            ->date('birthday')
            ->requirePresence('birthday', 'create')
            ->notEmpty('birthday', '生年月日を入力してください')
            ->add('birthday', [
                'date' => [
                    'rule' => function ($value, $context) {
                        return (bool) preg_match('/^([1-9][0-9]{3})\/(0[1-9]{1}|1[0-2]{1})\/(0[1-9]{1}|[1-2]{1}[0-9]{1}|3[0-1]{1})$/', $value);
                    },  
                    'message' => '生年月日はYYYY/MM/DDの形式で入力してください'
                ],  
            ])  
            ->add('birthday', [
                'date' => [
                    'rule' => function($value, $context){
                        $birthdate = new DateTime($value);
                        $today     = new DateTime();
                        $interval  = $today->diff($birthdate);

                        return (bool) ($interval->y > 18);
                    },  
                    'message' => '18歳未満の方は登録できません',
                ],  
            ]);

YYYY/MM/DDの形式かどうかと18歳以上かどうか確認したい。
この時、1984/11/のような入力をするとエラーになってしまう。

DateTime::__construct(): Failed to parse time string (1984/11/) at position 4 (/): Unexpected character

18歳未満かどうか調べる時にもYYYY/MM/DDの形式かどうか調べてもいいのですが、それだとYYYY/MM/DDの形式でないときにエラー文が18歳未満の方は登録できませんになってしまうので、あまり良くない。

調べた結果、下から順番にバリデーションが行われていたので順番を変えてみた。

修正コード(順番を変えた)

src/Model/Table/UsersTable.php
<?php
namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\Database\Schema\TableSchema;
use DateTime;

class ProfilesTable extends Table
{

    public function validationDefault(Validator $validator)
    {
        $validator
            ->date('birthday')
            ->requirePresence('birthday', 'create')
            ->notEmpty('birthday', '生年月日を入力してください')
            ->add('birthday', [
                'date' => [
                    'rule' => function($value, $context){
                        $birthdate = new DateTime($value);
                        $today     = new DateTime();
                        $interval  = $today->diff($birthdate);

                        return (bool) ($interval->y > 18);
                    },
                    'message' => '18歳未満の方は登録できません',
                ],  
            ])
            ->add('birthday', [
                'date' => [
                    'rule' => function ($value, $context) {
                        return (bool) preg_match('/^([1-9][0-9]{3})\/(0[1-9]{1}|1[0-2]{1})\/(0[1-9]{1}|[1-2]{1}[0-9]{1}|3[0-1]{1})$/', $value);
                    },
                    'message' => '生年月日はYYYY/MM/DDの形式で入力してください'
                ],
            ]);

とりあえず、エラーにはならなくなった。

JUST HACK IT!