フォーム

Aura.InputAura.Filterを使用したWebフォーム機能は、関連する機能が単一のクラスに集約され、テストや変更が容易です。1つのクラスでWebフォームとバリデーションの両方の用途に使用できます。

インストール

Aura.Inputを使用したフォーム処理を追加するために、composerでray/web-form-moduleをインストールします:

composer require ray/web-form-module

アプリケーションモジュールsrc/Module/AppModule.phpAuraInputModuleをインストールします:

use BEAR\Package\AbstractAppModule;
use Ray\WebFormModule\WebFormModule;

class AppModule extends AbstractAppModule
{
    protected function configure()
    {
        // ...
        $this->install(new AuraInputModule);
    }
}

Webフォーム

フォーム要素の登録やルールを定めたフォームクラスを作成して、@FormValidationアノテーションを使用して特定のメソッドと束縛します。メソッドは送信されたデータがバリデーションOKのときのみ実行されます。

use Ray\WebFormModule\AbstractForm;
use Ray\WebFormModule\SetAntiCsrfTrait;

class MyForm extends AbstractForm
{
    /**
     * {@inheritdoc}
     */
    public function init()
    {
        // フォームフィールドの設定
        $this->setField('name', 'text')
             ->setAttribs([
                 'id' => 'name'
             ]);

        // バリデーションルールとエラーメッセージの設定
        $this->filter->validate('name')->is('alnum');
        $this->filter->useFieldMessage('name', '名前は英数字のみ使用できます。');
    }
}

フォームクラスのinit()メソッドでフォームのinput要素を登録し、バリデーションのフィルターやサニタイズのルールを適用します。

バリデーションルールについては以下を参照してください:

メソッドの引数を連想配列にしたものをバリデーションします。入力を変更したい場合はSubmitInterfaceインターフェイスのsubmit()メソッドを実装して入力する値を返します。

@FormValidationアノテーション

フォームのバリデーションを行うメソッドを@FormValidationでアノテートすると、実行前にformプロパティのフォームオブジェクトでバリデーションが行われます。バリデーションに失敗するとメソッド名にValidationFailedサフィックスをつけたメソッドが呼ばれます:

use Ray\Di\Di\Inject;
use Ray\Di\Di\Named;
use Ray\WebFormModule\Annotation\FormValidation;
use Ray\WebFormModule\FormInterface;

class MyController
{
    /**
     * @var FormInterface
     */
    protected $form;

    /**
     * @Inject
     * @Named("contact_form")
     */
    public function setForm(FormInterface $form)
    {
        $this->form = $form;
    }

    /**
     * @FormValidation
     * // または
     * @FormValidation(form="form", onFailure="onPostValidationFailed")
     */
    public function onPost($name, $age)
    {
        // バリデーション成功時の処理
    }

    public function onPostValidationFailed($name, $age)
    {
        // バリデーション失敗時の処理
    }
}

@FormValidationアノテーションのformonValidationFailedプロパティを変更して、formプロパティの名前やメソッドの名前を明示的に指定することもできます。onPostValidationFailedにはサブミットされた値が渡されます。

ビュー

フォームのinput要素やエラーメッセージを取得するには要素名を指定します:

$form->input('name');  // 出力例:<input id="name" type="text" name="name" size="20" maxlength="20" />
$form->error('name');  // 出力例:名前は英数字のみ使用できます。

Twigテンプレートを使用する場合も同様です:

{{ form.input('name') }}
{{ form.error('name') }}

CSRF

CSRF(クロスサイトリクエストフォージェリ)対策を行うためには、フォームにCSRFオブジェクトをセットします:

use Ray\WebFormModule\SetAntiCsrfTrait;

class MyForm extends AbstractAuraForm
{
    use SetAntiCsrfTrait;
}

セキュリティレベルを高めるには、ユーザーの認証を含んだカスタムCsrfクラスを作成してフォームクラスにセットします。詳しくはAura.InputのApplying CSRF Protectionsをご覧ください。

@InputValidation

@FormValidationの代わりに@InputValidationとアノテートすると、バリデーションが失敗したときにRay\WebFormModule\Exception\ValidationExceptionが投げられます。この場合はHTML表現は使用されません。Web APIに便利です。

キャッチした例外のerrorプロパティをechoするとapplication/vnd.error+jsonメディアタイプの表現が出力されます:

http_response_code(400);
echo $e->error;

// 出力例:
// {
//     "message": "Validation failed",
//     "path": "/path/to/error",
//     "validation_messages": {
//         "name": [
//             "名前は英数字のみ使用できます。"
//         ]
//     }
// }

@VndErrorアノテーションでvnd.error+jsonに必要な情報を追加できます:

/**
 * @FormValidation(form="contactForm")
 * @VndError(
 *   message="foo validation failed",
 *   logref="a1000",
 *   path="/path/to/error",
 *   href={"_self"="/path/to/error", "help"="/path/to/help"}
 * )
 */
public function onPost()

Vnd Error

Ray\WebFormModule\FormVndErrorModuleをインストールすると、@FormValidationでアノテートしたメソッドも@InputValidationとアノテートしたメソッドと同じように例外を投げるようになります。作成したPageリソースをAPIとして使用することができます:

use BEAR\Package\AbstractAppModule;
use Ray\WebFormModule\FormVndErrorModule;

class FooModule extends AbstractModule
{
    protected function configure()
    {
        $this->install(new AuraInputModule);
        $this->override(new FormVndErrorModule);
    }
}

デモ

MyVendor.ContactFormアプリケーションでフォームのデモを実行して試すことができます。確認付きのフォームページや、複数のフォームを1ページに設置したときの例などが用意されています。