LoginSignup
4
1

More than 5 years have passed since last update.

Laravel/LumenでValidatorライブラリを作る

Last updated at Posted at 2018-12-07

はじめに

アプリケーションが大きくなってくると、複数のリポジトリにまたがるライブラリを作りたくなってきます。
それをcomposer installで入れられたらもうダブルメンテなんて気にしなくて済みますよね。
ビジネスロジックに依存したバリデータは重複するものが多いので、わりとライブラリに切り出しやすかったりします。

Laravel/Lumenにはカスタムバリデータを簡単に作る機構があるのですが、それを独立したライブラリに切り出すのはちょっとコツが必要だったのでご紹介します。

(コツはここから

カスタムバリデータを定義する(どこにでも載ってる話)

まずライブラリのソース構成はこんな感じです。Composerのパッケージ定義等は割愛します。

src/
 ├ Providers/
 │   └ ValidatorServiceProvider.php
 └ Validators/
     └ NumberValidator.php

次にカスタムバリデータクラスを作成します。
1. Illuminate\Validation\Validatorを継承する
2. validateXXXという命名規則でバリデーションメソッドを定義する

use Illuminate\Validation\Validator;

class NumberValidator extends Validator
{
    public function validateNaturalNumber(string $attribute, $value, array $parameters, Validator $validator): bool
    {
        if ($validator->validateInteger($attribute, $value)) {
            return $value > 0;
        }
        return false;
    }
}

そしてバリデータをServiceProviderに登録します。

use Illuminate\Support\Facades\Validator;
use Illuminate\Support\ServiceProvider;
use Relux\Validators\NumberValidator;

class ValidatorServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Validator::resolver(function ($translator, $data, $rules, $messages) {
            return new NumberValidator($translator, $data, $rules, $messages);
        });
    }
}

使うときはメソッド名をスネークケースにしたもので呼び出せます。

'required|natural_number'

ここまではどこにでも載っている話。

バリデーションメッセージをどこに定義するか

通常Laravel/Lumenのアプリケーションでバリデーションメッセージを定義する場合、下記のように言語ファイルを配置することでフレームワークが言語ファイルを読みに行ってくれます。

app/
 └ resources/
    └ lang/
       └ en/
          └ validation.php

ただ今回はComposerライブラリ内なので上記のような言語ファイル配置はできません。
でも同じようなことをライブラリでも実現したいと思い調べていると、こんな記述がありました。
https://laravel.com/docs/5.7/packages#translations

public function boot()
{
    $this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');
}

ServiceProviderでloadTranslationsFromを呼ぶことで言語ファイルを読み出すことができ、

echo trans('courier::messages.welcome');

という風にネームスペース付きで翻訳を呼び出すことができます。

最終的な実装はこうなった

src/
 ├ Providers/
 │   └ ValidatorServiceProvider.php
 ├ Validators/
 │   └ NumberValidator.php
 └ resources/
      └ lang/
         └ en/
            └ validation.php

ディレクトリ構成はLaravelと同じにしています。
validation.phpの中身は、通常の言語ファイルと同じようにただの連想配列。

return [
    'natural_number' => 'The :attribute must be natural number.',
];

ServiceProviderで言語ファイルの読み出しと、バリデータにメッセージをセットする。

class ValidatorServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // 言語ファイルをロード
        $this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'Relux');

        // バリデーションメッセージを取得
        $customMessages = trans('Relux::validation');

        Validator::resolver(function ($translator, $data, $rules, $messages) use ($customMessages) {
            $validator = new NumberValidator($translator, $data, $rules, $messages);

            // バリデータにメッセージ群をセット
            $validator->setCustomMessages($customMessages);
            return $validator;
        });
    }
}

loadTranslationsFromはネームスペース付きで言語ファイルの内容を保持しています。
バリデータはネームスペースをもたせてバリデーションメッセージを取得しに行かないので、setCustomMessagesメソッドでメッセージリストを設定する必要があります。

ここにまだ課題があるので、バリデータがネームスペース付きでバリデーションメッセージを取得しに行くようにカスタマイズできないか考えてみたいです。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1