モデルに存在しないフィールドのヴァリデーション

CakePHP

モデルに含まれていないカラムのヴァリデーションをどうするかという問題があります。
ヴァリデーションを書いたとしても、Entytyをインスタンス化する過程でブロックされてしまうので意味がありません。

モデルがないフォームのヴァリデーションを行うには
Cake\Form\Form クラスを使います。
モデルのないフォーム - 4.x

こちらはたまに紹介しているブログがありますが、実はこのクラスを使ってモデルに含まれていないカラムのヴァリデーションの問題も解決できますのでご紹介します。

対象バージョン
CakePHP4.3(たぶんCakePHP3.8以降ぐらいなら同じ)

書き方が若干違う

cakebookの更新ミスだと思いますがメソッドの名前が説明と若干違いました。
エラーメッセージやコアソースを追っていけば修正できると思いますが一応修正しておきます。

// src/Form/UserCreateForm.php の中で
namespace App\Form;

use Cake\Form\Form;
use Cake\Form\Schema;
use Cake\Validation\Validator;

class UserCreateForm extends Form
{

    //protected function _buildSchema(Schema $schema)  下記に修正
    protected function _buildSchema(Schema $schema): Schema
    {
        return $schema->addField('hogehoge', 'string');
    }

    //protected function _buildValidator(Validator $validator) 下記に修正
    public function validationDefault(Validator $validator): Validator
    {
        //ここでモデルにないカラムのヴァリデーションをセット
        $validator->add('hogehoge', 'length', [
                'rule' => ['minLength', 10],
                'message' => 'ほげほげは必須です'
            ]);

        return $validator;
    }
}

ここでポイントは、フォームのヴァリデーションを全部書く必要はないということです。
ではコントローラー側です

// 何らかのコントローラー中で
namespace App\Controller;

use App\Controller\AppController;
use App\Form\UserCreateForm;  //作ったクラスを追加する

class UsersController extends AppController
{
    public function Create()
    {
        $form= new UserCreateForm();
        if ($this->request->is('post')) {
            $userEntyty = $this->Users->newEntity($this->request->getData());
            //モデル側のエラーを取得して合体
            $errors = array_merge($userEntyty->getErrors(),$form->getErrors());
            $form->setErrors($errors);
            if ($form->getErrors()) {
                $this->Flash->error('失敗');                
            } else {
                //ここでUserの保存処理をする
                $this->Flash->success('成功');
            }
        }
        $this->set('contact', $contact);
    }
}

ポイントは下記

$errors = array_merge($userEntyty->getErrors(),$form->getErrors())
$form->setErrors($errors);

Formクラスの場合はsetError()メソッドでエラー内容を渡すことができるので、モデル側のヴァリデーション結果を渡してあげればOKです。ただ、setErrors()で渡すと、セットした配列で上書きになってしまうようなので、両方のエラー配列をマージして渡してあげる必要があります。

本当はFormクラスで全部やるつもりだったのですが、中間テーブルにセットした_joinDataを使ったヴァリデーションがなぜか認識してくれなかったので、この方法を思いつきました。
一応、ネストされたデータのヴァリデーションもaddNestedやaddNestedManyメソッドでできましたのでやりたい方は調べてみてください。
ですが、既存のモデルのヴァリデーション流用したほうが、二重に書く必要がないので良いと思います。

タイトルとURLをコピーしました