【PHP】ピュアPHPでLaravelのValidatorを使う方法

Laravel

この記事のゴール

入力を要する処理ではバリデーションが不可欠です。

LaravelのValidatorは利用方法が簡単で汎用性があり、

機能ごとにValidatorクラスを作成する必要がなく、

コード量が少なく、実装時間が短くて済み、

挙動の変更も容易です。(Laravelフレームワークの中では。)

Laravel - The PHP Framework For Web Artisans
Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing ...

Laravelフレームワークの管理外でこのValidatorを導入して、

その恩恵を美味しく戴いていきます。

※2024/04/28追記:当記事の内容に機能追加してライブラリ化しました。

前提条件

  • PHP8.2以降インストール済
  • Composerインストール済

PHPプロジェクト構成

次のようなフォルダ構成にしていきます。

[プロジェクトトップ]
 ├─ src/
 |  ├─ lang/ [言語設定]
 │  │  ├─ en/ [英語]
 │  │  │  └─ validation.php [メッセージ設定]
 │  │  └─ ja/ [日本語]
 │  │     └─ validation.php [メッセージ設定]
 │  ├─ lib/ [今回作成するバリデーションライブラリ]
 │  │  └─ validation/
 │  │       ├─ ValidatorWrapper.php
 │  │       └─ ValidatorFactory.php
 │  └─ App.php [アプリケーション本体]
 ├─ vendor/ [Composerパッケージ群]
 ├─ composer.json
 └─ composer.lock

PHPプロジェクト作成

まずは、Laravelを使わないピュアPHPのプロジェクトを作成します。

(というかフォルダを作成するだけ。)

mkdir purephp-validation

作成したフォルダ内に移動します。

cd purephp-validation

illuminate/validationインストール

Laravelフレームワークの個別切り出しコンポーネントである「illuminate/validation」をインストールします。

GitHub - illuminate/validation: [READ ONLY] Subtree split of the Illuminate Validation component (see laravel/framework)
Subtree split of the Illuminate Validation component (see laravel/framework) - illuminate/validation
composer require illuminate/validation

Validatorのメッセージ設置

まずはフォルダを作成します。

mkdir -p src/lang/en
mkdir -p src/lib/validation

作成したフォルダ「src/lang/en/」の中に「validation.php」を作成します。

下のリンクのファイルをコピーして設置すると良いでしょう。

framework/src/Illuminate/Translation/lang/en/validation.php at 11.x · laravel/framework
The Laravel Framework. Contribute to laravel/framework development by creating an account on GitHub.

Laravel framework 11.x 公式リポジトリ内のファイルです。

Validatorラッパー設置

「src/lib/validation/」フォルダ内に「ValidatorWrapper.php」を作成します。

<?php

/**
 * This file is a slightly modified version of the following code:
 * - BaseCode: https://github.com/jeffochoa/validator-factory/blob/master/src/ValidatorFactory.php
 * - BaseTag:  https://github.com/jeffochoa/validator-factory/releases/tag/1.0.1
 * - Modified: namespace is renamed from 'JeffOchoa'
 * - Modified: class name is renamed from 'ValicatorFactory'
 * - Modified: path set by the method 'getTranslationsRootPath' adjusted
 * - Modified: types of class properties added
 * - Modified: types of function parameters added
 * - Modified: PHPDoc blocks added.
 */
namespace Purephp\Validation;

use Illuminate\Validation\Factory;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Translation\FileLoader;
use Illuminate\Translation\Translator;

class ValidatorWrapper
{
    public string $lang;
    public string $group;
    public Factory $factory;
    public string $namespace;

    // Translations root directory
    public string $basePath;

    public static Translator $translator;

    /**
     * constructor
     * @param   string  $namespace
     * @param   string  $lang
     * @param   string  $group
     */
    public function __construct(
        string $namespace = 'lang',
        string $lang = 'en',
        string $group = 'validation'
    ) {
        $this->lang = $lang;
        $this->group = $group;
        $this->namespace = $namespace;
        $this->basePath = $this->getTranslationsRootPath();
        $this->factory = new Factory($this->loadTranslator());
    }

    /**
     * Sets tranlations root path
     * @param   string  $path = ''
     * @return  $this
     */
    public function translationsRootPath(string $path = '')
    {
        if (!empty($path)) {
            $this->basePath = $path;
            $this->reloadValidatorFactory();
        }
        return $this;
    }

    /**
     * Reloads ValidatorFactory
     * @return  $this
     */
    private function reloadValidatorFactory()
    {
        $this->factory = new Factory($this->loadTranslator());
        return $this;
    }

    /**
     * Returns translations root path
     * @return  string
     */
    public function getTranslationsRootPath(): string
    {
        return dirname(__FILE__) . '/../../';
    }

    /**
     * Loads and returns Translator
     * @return  Translator
     */
    public function loadTranslator(): Translator
    {
        $loader = new FileLoader(new Filesystem(), $this->basePath . $this->namespace);
        $loader->addNamespace($this->namespace, $this->basePath . $this->namespace);
        $loader->load($this->lang, $this->group, $this->namespace);
        return static::$translator = new Translator($loader, $this->lang);
    }

    /**
     * Method overloading
     * @param   string  $method
     * @param   array<string, string>   $args
     * @return  mixed|false
     * @see https://www.php.net/manual/en/language.oop5.overloading.php#object.call
     */
    public function __call(string $method, array $args)
    {
        return call_user_func_array([$this->factory, $method], $args);
    }
}

このクラスを静的に呼び出せるように、同じフォルダにもう一つ

「ValidatorFactory.php」を作成します。

<?php

namespace Purephp\Validation;

require_once __DIR__ . '/ValidatorWrapper.php';

class ValidatorFactory
{
    private static string $lang = 'en';
    private static string $langPathRoot = __DIR__ . '/../../lang/';

    /**
     * Sets lang or returns current lang
     * @param   string  $lang = ''
     * @return  string|null
     */
    public static function lang(string $lang = '')
    {
        if (strlen($lang) === 0) {
            return self::$lang;
        }
        $path = self::$langPathRoot . $lang . '/validation.php';
        if (!is_readable($path)) {
            throw new \Exception("Cannot read {$path}.");
        }
        return self::$lang = $lang;
    }

    /**
     * Creates Validator
     * @param   array<string, mixed>    $data
     * @param   array<string, string>   $rule
     * @param   array<string, string>   $messages = []
     * @param   array<string, string>   $attributes = []
     * @return  \Illuminate\Validation\Validator
     */
    public static function make(
        array $data,
        array $rule,
        array $messages = []
        array $attributes = []
    ) {
        // @phpstan-ignore-next-line
        return (new ValidatorWrapper(
            lang: self::$lang
        ))
        ->make($data, $rule, $messages, $attributes);
    }
}

アプリケーション作成

アプリケーション本体を作成して、上記のクラスを利用したコードを実装していきます。

「src/」フォルダ内に「App.php」を作成します。

<?php

require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/lib/validation/ValidatorFactory.php';

use Purephp\Validation\ValidatorFactory;

// Input
$user = [
    'name' => 'ho',
    'email' => 'hoge',
    'level' => 0,
];

// Valiation Rules
$rules = [
    'name' => 'required|string|min:3|max:40',
    'email' => 'required|string|email:rfc,dns',
    'level' => 'required|integer|min:1|max:99',
];

// Validation
$validator = ValidatorFactory::make($user, $rules);

// Checking Result
if ($validator->fails()) {
    var_dump($validator->errors());
} else {
    echo "passed.\n";
}

バリデーション実行

作成したアプリケーションを実行して

バリデーションの動作確認をしてみます。

「$validator->fails()」が機能していることが判ります。

戻り値は「Illuminate\Support\MessageBag」です。

バリデーションのエラーメッセージも表示されていますが、

これはprotectedプロパティで、コード内では取得できません。

次のようにすると、メッセージを配列で取得可能です。

$validator->errors()->messages()

では、バリデーションを通過するように入力値を修正します。

// Input
$user = [
    'name' => 'hoge',
    'email' => 'hoge@hoge.com',
    'level' => 1,
];

再度実行してみます。

バリデーションを通過しました。

日本語化設定

バリデーションメッセージを日本語にします。

「src/lang/ja/」フォルダを作成し、その中に下のリンクのファイルを設置すると良いでしょう。

breezejp/stubs/lang/ja/validation.php at main · askdkc/breezejp
Laravel Breeze(+Laravel UIとJetstream)を一瞬で日本語化し、言語切替機能も提供するパッケージです / Laravelの各種バリデーションメッセージも日本語化するのでBreeze無しでも便利✨ - askdk...

Laravel Breeze 日本語化プロジェクトのGithubリポジトリのファイルです。

バリデーション実行の手前に次のコードを挿入します。

// Set lang
ValidatorFactory::lang('ja');

「src/App.php」の完成形は次のような感じです。

<?php

require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/lib/validation/ValidatorFactory.php';

use Purephp\Validation\ValidatorFactory;

// Input
$user = [
    'name' => 'ho',
    'email' => 'hoge',
    'level' => 0,
];

// Valiation Rules
$rules = [
    'name' => 'required|string|min:3|max:40',
    'email' => 'required|string|email:rfc,dns',
    'level' => 'required|integer|min:1|max:99',
];

// Set lang
ValidatorFactory::lang('ja');

// Validation
$validator = ValidatorFactory::make($user, $rules);

// Checking Result
if ($validator->fails()) {
    var_dump($validator->errors());
} else {
    echo "passed.\n";
}

実行してみます。

日本語のメッセージが表示されました。

当記事の内容に機能追加してライブラリ化しました。

こちらの記事をご覧ください。

参考サイト

▼ (by Jeff Ochoa) @ Medium

Using the Illuminate\Validation\Validator class outside Laravel
Laravel’s validation component its very handy especially when you need to validate forms on your PHP projects. Building ...

▼ ねうねう技術らくがき日記 @ Hatena Blog

コメント

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