はじめに
アプリケーションが大きくなってくると、複数のリポジトリにまたがるライブラリを作りたくなってきます。
それをcomposer install
で入れられたらもうダブルメンテなんて気にしなくて済みますよね。
ビジネスロジックに依存したバリデータは重複するものが多いので、わりとライブラリに切り出しやすかったりします。
Laravel/Lumenにはカスタムバリデータを簡単に作る機構があるのですが、それを独立したライブラリに切り出すのはちょっとコツが必要だったのでご紹介します。
(コツはここから)
カスタムバリデータを定義する(どこにでも載ってる話)
まずライブラリのソース構成はこんな感じです。Composerのパッケージ定義等は割愛します。
src/
├ Providers/
│ └ ValidatorServiceProvider.php
└ Validators/
└ NumberValidator.php
次にカスタムバリデータクラスを作成します。
-
Illuminate\Validation\Validator
を継承する -
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
メソッドでメッセージリストを設定する必要があります。
ここにまだ課題があるので、バリデータがネームスペース付きでバリデーションメッセージを取得しに行くようにカスタマイズできないか考えてみたいです。