BEAR.Sunday のフォーム で Aura.Filter(v2.x)
のカスタムルールを使ってバリデーションを行う例を紹介します。
Ray.WebFormModule のフォームバリデーション
Ray.WebFormModule では入力フィールドの検証に Aura.Filter (v2.x) が利用されます。具体的には下図のように SubjectFilter
クラスを使います。
- フォーム抽象クラス(※バリデーションに関わる箇所の抜粋)
SubjectFilter は個々のフィールドに対するルール定義を通して要素の集合に対する検証を行います。要素の集合とは配列またはオブジェクトで、これをサブジェクトと呼びます。
フォームでカスタムバリデーションを行いたい場合は SubjectFilter にその設定を加える必要があります。設定できる箇所はフォームクラスのセッタにて定義されている FilterFactory
クラスです。
カスタムバリデーションの実装
下記の例で考えます。
「(商品の注文操作時に)商品の在庫があるかどうか」
の検証をするとします。
- 注文業務の仕様(商品在庫あり)を満たすかどうかのチェック
- 複数のフィールドにまたがった検証(
商品ID
と数量
の2項目を参照)
サンプルコードの Github はこちら kumamidori/ExampleSpecFormです。
業務の仕様
// 商品在庫有り仕様
class InStockItemSpecification implements SpecificationInterface
{
public function isSatisfiedBy($order)
{
// 実際にはここでDB検索を呼び出して $order の在庫があるかをチェック
return false;
}
}
AuraFilter の設定
- バリデータ(業務の仕様を使う)
class ValidateInStockItem implements ValidateInterface
{
/**
* @Inject
*/
private $spec;
public function __construct(InStockItemSpecification $spec)
{
$this->spec = $spec;
}
public function __invoke($subject, $field)
{
// 実際にはここでフォーム要素群($subject)からバリデーションに必要な値セット($order)へと変換
$order = $subject;
return $this->spec->isSatisfiedBy($order);
}
}
-
ロケータファクトリ (Locator Factories)
を設定(プロバイダー)
カスタムルールのためのファクトリ設定。 FilterFactory
コンストラクタからロケータに渡される。
class FilterFactoryProvider implements ProviderInterface
{
private $validateFactories;
/**
* @Inject
*/
public function __construct(
ValidateInStockItem $validateInStockItem
)
{
$this->validateFactories = [
'app.in-stock-item' => function () use ($validateInStockItem) {
return $validateInStockItem;
},
];
}
public function get()
{
return (new FilterFactory($this->validateFactories));
}
}
※ ここで設定するルール名がグローバルである点(名前空間のようなグルーピングが必要か)が気になりました。ここではライブラリ同梱のルール名と区別できる規約として app.
プレフィックスを付けました。
※設定箇所を プロバイダー(Provider)
のコンストラクタにしたので、設定の知識がわかりづらい課題があります(これについては別記事で別途改善を考えました)。
フォーム側
作ったルールを使うだけです。
class ItemSelectForm extends AbstractForm
{
use InStockItemFilterInject;
/**
* {@inheritdoc}
*/
public function init()
{
$this->setField('item_id');
$this->setField('quantity');
$this->filter->validate('quantity')->is('app.in-stock-item');
$this->filter->useFieldMessage('item_id', '選択された商品は在庫切れとなっております。');
}
}
モジュールでの束縛
- FilterFactory をプロバイダーに束縛
$this->bind(FilterFactory::class)->toProvider(FilterFactoryProvider::class);
$this->bind(FormInterface::class)->annotatedWith('app.itemSelectForm')->to(ItemSelectForm::class);
$this->bind(InStockItemSpecification::class);
$this->bind(ValidateInStockItem::class);
補足
-
ここではカスタムルール機能を利用しましたが、ユーザインターフェイスのバリデーションであれば Aura.Filter マニュアル記載の標準機能のみでまかなえるケースも多いです。必須とオプションの区別も標準機能でできます。
-
上記は一例です。BEARではフォームライブラリを自由に選ぶこともできます(参考記事:
BEAR.SundayとZF2 Formを使ったフルスタックスタイルフォームの作り方 by zingooo )
関連モジュール
こちらは設定ファイルのみでカスタムバリデーション設定を行うモジュールです。制約があって、設定からユーザサイドでオブジェクト生成を行うものなので、DI基盤を使いたい場合には利用することができません。
DI基盤を使いたいニーズに対応するためにDIインジェクタを実装に利用したモジュールです。紹介記事はこちら。