[Practical BEAR.Sunday] Ray.WebFormModule(Aura.Filter v2)カスタムバリデーションの作成

BEAR.Sunday のフォームAura.Filter(v2.x) のカスタムルールを使ってバリデーションを行う例を紹介します。

Ray.WebFormModule のフォームバリデーション

Ray.WebFormModule では入力フィールドの検証に Aura.Filter (v2.x) が利用されます。具体的には下図のように SubjectFilter クラスを使います。

  • フォーム抽象クラス(※バリデーションに関わる箇所の抜粋)

Untitled Diagram (6) (3).png

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基盤を使いたい場合には利用することができません。

関連リンク

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.