CakePHP4のテーブルが無い場合のモデルの対処、書く方法

コントローラーにビジネスロジックを書かずにモデルに書くための方法をまとめておきます（備忘録用）。


### CakePHP2ならば...

```Model/User.php

<?php
App::uses('AppModel', 'Model');
/**
 * User Model
 */
class User extends AppModel {

    public $useTable = false;
.
.
.

}

```

上記のように`public $useTable = false;`と記述すればテーブルを持たないモノでもロジックを書く事ができましたが、CakePHP4では下記のように書いてもエラーが返ってくる。


```Model/Table/User.php
<?php
declare(strict_types=1);

namespace App\Model\Table;

use Cake\ORM\Table;

/**
 * User Model
 */
class UsersTable extends Table
{
    /**
     * Initialize method
     */
    public function initialize(array $config): void
    {
        parent::initialize($config);
        $this->setTable(false);  //エラーになる
    }
}
```

（CakePHP2の経験者なら必ずやるエラー。）
コアな部分を読むとわかりますが`$this->setTable()`はStringしか入れられず、読み込むテーブルを無効化することができない仕様になっています。
色々と調べてみると、Model/Tableではテーブルが存在しないモデルは作れないみたいです。

## How to do...?

CakePHP4では、<a href="https://book.cakephp.org/4/ja/core-libraries/form.html">モデルのないフォーム</a>を使用することで
対処することができます。

一例で写真のバリデーションをモデルのないフォームで記述してみます。
Formの記述

```Form/PhotoForm.php

<?php
namespace App\Form;

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

/**
 * PhotoForm
 */
class PhotoForm extends Form{

    protected function _buildSchema(Schema $schema): Schema{
        return $schema
            ->addField('img',['type' => 'img']);
    }


    public function validationDefault(Validator $validator): Validator
    {
        $validator
            ->add('img','validator_msg',[
                 //自作のバリデーションの読み込み
                'rule' => [$this,'uploadValidator'],
            ]);
        return $validator;
    }

    protected function _execute(array $data): bool
    {
         $result = true;
        if (!empty($errors)) {
            $this->setErrors($errors);
            $result = false;
        }

        return $result;
    }


    /**
     * uploadValidator - 画像のバリデーション
     */
    function uploadValidator($check, array $context){

        //PHPのアップロードのバリデーション
        $error = $check->getError();
        switch ($error) {
            case UPLOAD_ERR_OK:
                break;
            case UPLOAD_ERR_INI_SIZE:
                return '10MB以下のファイルをアップロードしてください。';
            case UPLOAD_ERR_FORM_SIZE:
                return 'ファイルが大きすぎます。';
            case UPLOAD_ERR_PARTIAL:
                return 'アップロードに失敗しました。もう一度アップロードをお試しください。';
            case UPLOAD_ERR_NO_TMP_DIR:
                return 'アップロードに失敗しました。もう一度アップロードをお試しください。';
            default:
                return 'エラーが発生しました。アップロードファイルをご確認ください。';
        }
        return true;
    }
}

```

Contorollerの記述。

```Contoroller/PhotosContoroller.php
<?php
    $data = [];
    $data['img'] = $this->request->getData('data.Photo');

    //PhotoFormをインスタンス化してバリデーションを噛ませる
    $contact = new PhotoForm();

    pr($contact->getErrors());
    //アップロードした容量が多かった場合のエラー内容
    //Array
    //(
    //    [img] => Array
    //        (
    //            [validator_msg] => 10MB以下のファイルをアップロードしてください。
    //        )
    //)

    /* アップロードエラー検出時 ⇒ 次画像のアップロード状況調査処理を中断 */
    if ($contact->execute($data)) {
        $this->Flash->warning($contact->getErrors()['img']['validator_msg']);
        break;
    }

```


_executeメソッドが返せる値はbool値のみなので、バリデーションで弾かれた場合false、弾かれない場合はtrueを返すように書く。
そして、Controllerにif文で分岐を作るときれいにおさまる。