cakePHP4のバリデーションを動的に変更する方法(既存のルールをcontroller側で一時的に無効化する方法)

概要

cakePHP4のバリデーションを動的に変更する方法(既存のルールをcontroller側で一時的に無効化する方法)について解説します。

例えば、ユーザー情報の登録、パスワード変更において以下のような要件があったとします。このとき、UsersTableのvalidationDefaultに設定済みのルールを動的に変更するにはどうしたら良いでしょうか?

  • ユーザー追加時には現在のパスワードのチェックは不要
  • ユーザー情報編集時に、パスワード欄に入力があれば、現在のパスワード、確認用パスワードをチェックする。
  • ユーザー情報編集時に、パスワード欄に入力が無ければ、現在のパスワード、確認用パスワードをチェック不要。
前提環境
  • Hosting Server: さくらインターネット
  • PHP: 7.4.9
  • cakePHP: 4.1.1

コードのポイント

動的にValidatorの設定を変更する方法はいくつか考えられますが、今回はあらかじめ必要な設定を全て記入し、必要に応じて設定を外していく方法を使用します。

controllerからvalidatorを取得するには「$this->(モデル名->getValidator();」を使います。

設定を外していくには「offsetUnset」を使用します。

サンプルコード

※注意!
本来ならばパスワードの文字数や確認用パスワードとの一致など、ルールがもっと必要ですが、サンプルコードのため除外しています。

/src/Model/Table/UsersTable.php

public function validationDefault(Validator $validator): Validator
{
    $validator
        ->requirePresence('email')
        ->notEmptyString('email', 'メールアドレスは省略出来ません。')
        ->email('email', false, 'メールアドレスの形式ではありません。')

        ->requirePresence('username')
        ->notEmptyString('username', 'ユーザ名は省略出来ません。')

        ->requirePresence('password')
        ->notEmptyString('password', 'パスワードは省略出来ません。')

        ->requirePresence('password_check')
        ->notEmptyString('password_check', '確認用パスワードは省略出来ません。')


    return $validator;
}

/src/Controller/UsersController.php

public function edit($id = null)
{
    $user = $this->Users->get($id);
    if ($this->request->is(['patch', 'post', 'put'])) { 
        // パスワードに入力があるか確認、パスワードがなければ更新対象としない。
        if (empty($this->request->getData('password'))) {
            // バリデーションのルールを解除する。
            $this->offsetPasswordValidator();
            // 更新対象からも外しておく
            $user->setAccess('password', false);
        } else {
            $user->setAccess('password', true);
        }

        $user = $this->Users->patchEntity($user, $this->request->getData());

        if ($this->Users->save($user)) {
            $this->Flash->success(__('ユーザー情報の変更を保存しました。'));
            return $this->redirect(['controller'=>'pages', 'action' => 'index']);
        } else {
            $this->Flash->error(__('ユーザー情報の変更の保存に失敗しました。もう一度お試しください。'));
        }
    }

    // 編集画面表示時点ではパスワードの入力があるかどうかわからないので
    // バリデーションのルールを解除する。
    // ここで解除しないと、パスワードが必須のままになる。
    $this->offsetPasswordValidator();
    $this->set(compact('user'));
}

// バリデーションのルール解除用の関数。
// 複数回呼ばれるので関数にまとめた。
private function offsetPasswordValidator()
{
    $validator = $this->Users->getValidator();
    $validator->offsetUnset('password_now');
    $validator->offsetUnset('password');
    $validator->offsetUnset('password_check');
}

cakePHP4のページネーションで、1ページ当たり100件までしかデータが表示できない場合の対応方法

概要

cakePHP4のページネーションで、1ページ当たり100件までしかデータが表示できない場合の対応方法について解説します。

前提環境
  • Hosting Server: さくらインターネット
  • PHP: 7.4.9
  • cakePHP: 4.1.1

コードのポイント

cakePHP4ではPaginatorComponentによる1ページの表示件数の上限がデフォルト値で100件になっています。このため「maxLimit」を使って上限数を変更する必要があります。
CookBook:ページネーション「1ページあたりの最大行数を制限する」

ページネーションは一覧画面でよく使用されますが、一覧画面の表示件数をユーザーが変更できる機能が設けられていることがあります。このとき、ユーザーが10,000のような大きな数字を設定すると、サーバーに大きな負荷がかかってしまいます。
アプリ開発者が上限数を設定すればよいのですが、必ずしも設定するとは限りません。そのためフレームワーク側でデフォルト値を設けています。

サンプルコード

// 商品検索のクエリ
$itemQuery = $this->Items->find();

// ページネータの処理
$paginate = [
    'limit' => 50,  // ユーザーが指定可能な数字
    'maxLimit' => 150, // アプリ側で制限する上限値
];

// ページネーションの設定
$items = $this->Paginator->paginate($itemQuery, $paginate);

cakePHP4の日時についてFrozenTimeクラスの使い方を簡単に説明

概要

cakePHP4では日時についてFrozenTimeクラスを使います。
(※confi/app.phpで「defaultTimezone」を「Asia/Tokyo」にしておけば、そのタイムゾーンで動作します。)
しかしながらCookBookやAPIリファレンスを参照しても使い方がよくわからないので、自分がよく使うものだけでも簡単なメモを残そうと思います。

前提環境
  • cakePHP: 4.1.1

一応、該当のソースコードの場所について

簡単な説明だけでは不足だ!という方はソースコードを眺めてみてください。
場所がわかりにくく、かつ複数のファイルにまたがっているのでいくつか記載します。
(※下記以外にもあるので必要な方は探してみてくださいー。)

FrozenTimeクラス
/vendor/cakephp/cakephp/src/I18n/FrozenTime.php
親であるChronosクラス
/vendor/cakephp/chronos/src/
ChronosのTraitであるDifferenceTrait
/vendor/cakephp/chronos/src/Traits/DifferenceTrait.php

使い方

DBのdatetime型のカラムの取得と保存

/*
 * itemsテーブルのregistered_timeカラムがdatetime型の場合
 *

// 取得した「$item->registered_time」はFrozenTimeクラスのオブジェクトになっています。
$item = $this->Items->get($id);
echo $item->registered_time;

// 保存する場合はそのまま入れます。
// その1:現在時刻
$datetme = FrozenTime::now();
// その2:日時指定
$datetme = new FrozenTime('2000-01-10 00:00');

$item->registered_time = $datetme;

時間の加算について

// 分数の差分は「addMinutes」メソッドを使います。
// 第1引数はintで分を表します。

$expiration = $expiration->addMinutes(5);

// これ以外にも「add~」メソッドが同様に年、月、日の単位で加算を返します。

時間の減算(差分)について

// 秒数の差分は「diffInSeconds」メソッドを使います。
// 第1引数はFrozenTimeクラスのオブジェクトです。
// この場合は「$item->registered_time」-「$nowTime」になります。
// 第2引数は差分を絶対値とするかどうか。省略はtrueで絶対値扱いです。
// 下記の例ではfalseなので絶対値ではなく、正負の値になります。
$second = $nowTime->diffInSeconds($item->registered_time, false);

// これ以外にも「diffIn~」メソッドが同様に年、月、日の単位で差分を返します。

cakePHP4でログイン時にメールを送信する

概要

不正ログイン防止の一環として、cakePHP4でログイン時にユーザーにメールを送信する方法について解説します。

前提環境
  • Hosting Server: さくらインターネット
  • PHP: 7.4.9
  • cakePHP: 4.1.1

コードのポイント

  • ログイン時の処理なので、UsersControllerのloginメソッドのログイン成功したあと、の箇所に記載します。
  • cakePHP4ではMailerクラスを使用するので、UsersControllerのnamespaneの後に「use Cake\Mailer\Mailer;」を記載してください。
    ※詳しくはcakePHP4のマニュアルのMailerの項を参照してください。

サンプルコード

UsersController

<?php
namespace App\Controller;
use Cake\Mailer\Mailer;  // Mailerを使用する宣言を追加

class UsersController extends AppController
{
    public function login()
    {
        $this->request->allowMethod(['get', 'post']);
        $result = $this->Authentication->getResult();
        if ($result->isValid()) {
            // ログイン成功時にメール送信処理を行います。
            // Authenticationを使ってusersテーブルのユーザー情報を取得
            $user = $this->Authentication->getIdentity();
            $mailer = new Mailer('default');
            $mailer->setEmailFormat('html')
                ->setFrom([FROM_MAIL => FROM_MAIL_NAME]) // Fromのメールアドレス等はconfig/bootstrap.phpで定数宣言しています。
                ->setTo($user->email)
                ->setSubject('ログインのお知らせ')
                ->viewBuilder()
                ->setTemplate('login') //  templates/email/html/login.php
                ->setLayout('default')  // templates/layout/email/html/default.php
                ->setVar('username', $user->username);  // usersテーブルに追加したusernameカラムの情報をテンプレートに適用
            $mailer->deliver();

            $redirect = $this->request->getQuery('redirect', [
                'controller' => 'Pages',
                'action' => 'home',
            ]);
    
            return $this->redirect($redirect);
        }

        // 認証失敗した場合は、エラーを表示します
        if ($this->request->is('post') && !$result->isValid()) {
            $this->Flash->error(__('メールアドレスパまたはパスワードに誤りがあります。'));
        }
    }

// 以下省略

cakePHP4のインストール方法

概要

cakePHP4系を下記環境にインストールしたときのメモ書きです。

前提環境
  • Hosting Server: さくらインターネット
  • PHP: 7.4.9
  • cakePHP: 4.1.1

composerによるインストール

(注意:composerを使用しないインストールについては、下記「プラグインのインストールについて」に注意点を記載しておりますので、そちらも御覧ください。)

SSH(ターミナル)でサーバーに接続し。公開ディレクトリ(wwwなど)で下記コマンドを入力します。

php composer.phar create-project --prefer-dist "cakephp/app:4.*" my_app_name

※「my_app_name」は自分で作成するアプリ用に適当に変更してください。
※変更したこのアプリ名と同じディレクトリが作成されます。

設定変更

app_local.phpの修正

config/app_local.phpを修正します。※cakePHP3では.envを編集しましたがcakePHP4ではapp_local.phpになります。

  • 「SECURITY_SALT」を任意の文字列に修正します。(キーボードを適当に叩いて「数字」と「アルファベット」のランダムな文字列にします。)
  • 「Datasources」の「default」にDBの設定を記載します。「host」「username」「password」「database」をお使いのDBの設定に変更してください。

app.phpの修正

config/app.phpを修正します。

  • 「App」の「defaultLocale」を「en_US」から「ja-JP」に修正
  • 「App」の「defaultTimezone」を「UTC」から「Asia/Tokyo」に修正

cakePHPインストールにあたっての注意事項

プラグインのインストールについて

旧cakePHPではzipファイルをアップロードして展開するだけでインストールを行うことができました。それはPHP4でも行うことはできますが、1点注意が必要です。問題なのはインストールではなく「その後」なのです。

※zipファイルなどのダウンロードは下記GitHubから行うことができます。
https://github.com/cakephp/cakephp/tags

ログイン画面などを作成するときは認証用のプラグイン(cakePHP4からAuthentication および Authorizationになりました。)を使うなど、各種プラグインを用いると思いますが、このプラグインもcomposerを使用してインストールすることが推奨されています。
これらプラグインはcakePHP本体と異なり、GitHubからソースを持ってきただけでは使用することができません。
composerを使ったときと同じような設定の追加、変更、ファイル配置を行う必要があります。
手動で行うことができないわけではありませんが、その労力を使うならばサーバーを変更したほうが早いです。

※「どうしてもサーバーを変更できない!」という場合は、composerが使用可能なサーバーで一度試して、そのファイル構成などを元のサーバーに模倣したほうが良いでしょう。

ロリポップは実質使用できない

皆さんそれぞれに好きなレンタルサーバーというのはあると思います。しかしcakePHP4ではロリポップは実質使用することができません。

※ロリポップは私自身好きなレンタルサーバーで10年以上使っていますし、このブログもロリポップです。。。しかし、それでもです。

理由は2つあります。
・プラグインのインストールの項で説明しましたが、composerが必須です。しかしロリポップはメモリなどの制約が厳しいため、composerでのインストール中にエラーになります。
・SSH接続のコンソール(ターミナル)の画面で使用するPHPのバージョンが古いです。このためbakeなど各種コマンドを使用する時にPHPのバージョンが古いというエラーになります。

これらの理由が作成するアプリには該当しない、もしくは自力で解決できる、という場合ロリポップを使用することは可能です。
が、そこまでしてロリポップにこだわる必要は無いと思います。

じゃあどこのレンタルサーバーがいいの?と言われるとさくらレンタルサーバーでしょうかね。

cakePHP4のcomposerインストールで「php: No match.」エラーになった場合の対処方法

前提環境
  • Hosting Server: さくらインターネット
  • PHP: 7.4.9
  • cakePHP: 4.1.1

cakePHP4のcomposerインストール方法

php composer.phar create-project --prefer-dist cakephp/app:4.* my_app_name

マニュアルに記載の上記コマンドを入力すると「php: No match.」のエラーになることがあります。
https://book.cakephp.org/4/ja/installation.html


その場合は以下のようにダブルクォートで囲んでください。

php composer.phar create-project --prefer-dist "cakephp/app:4.*" my_app_name