LoginSignup
4
2

More than 3 years have passed since last update.

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

Last updated at Posted at 2017-12-15

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

DI基盤を使いたいニーズに対応するためにDIインジェクタを実装に利用したモジュールです。紹介記事はこちら

関連リンク

4
2
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
2