diff --git a/manuals/1.0/en/form.md b/manuals/1.0/en/form.md index e67680ec..fd76fb4c 100644 --- a/manuals/1.0/en/form.md +++ b/manuals/1.0/en/form.md @@ -7,18 +7,17 @@ permalink: /manuals/1.0/en/form.html # Form -Each related function of Web Forms using [Aura.Input](https://github.com/auraphp/Aura.Input) and [Aura.Filter](https://github.com/auraphp/Aura.Filter) is aggregated to a single class so that it is easy to test and change. -We can use a corresponding class for the use of Web Forms and validation. +Web form handling powered by [Aura.Input](https://github.com/auraphp/Aura.Input) and [Aura.Filter](https://github.com/auraphp/Aura.Filter) aggregates related functionality into a single class, making it easy to test and modify. A single form class can serve both rendering and validation. ## Install -Install `ray/web-form-module` via composer to add form using Aura.Input +Install `ray/web-form-module` with composer: ```bash composer require ray/web-form-module ``` -Install `AuraInputModule` in our application module `src/Module/AppModule.php` +Install `WebFormModule` in your application module at `src/Module/AppModule.php`: ```php use BEAR\Package\AbstractAppModule; @@ -29,15 +28,14 @@ class AppModule extends AbstractAppModule protected function configure() { // ... - $this->install(new AuraInputModule); + $this->install(new WebFormModule()); } } ``` -## Web Form +## Form Class -Create **a form class** that defines the registration and the rules of form elements, then bind it to a method using `#[FormValidation]` attribute. -The method runs only when the sent data is validated. +Create a **form class** that defines input elements and validation rules, then bind it to a method with the `#[FormValidation]` attribute. The method only runs when validation succeeds. ```php use Ray\WebFormModule\AbstractForm; @@ -45,107 +43,129 @@ use Ray\WebFormModule\SetAntiCsrfTrait; class MyForm extends AbstractForm { - /** - * {@inheritdoc} - */ + use SetAntiCsrfTrait; + public function init() { - // set input fields + // register input fields $this->setField('name', 'text') - ->setAttribs([ - 'id' => 'name' - ]); - // set rules and user defined error message + ->setAttribs(['id' => 'name']); + + // set validation rules and user-defined error messages $this->filter->validate('name')->is('alnum'); $this->filter->useFieldMessage('name', 'Name must be alphabetic only.'); } } ``` -We can register the input elements in the `init()` method of the form class and apply the rules of validation and sanitation. -Please refer to [Rules To Validate Fields](https://github.com/auraphp/Aura.Filter/blob/2.x/docs/validate.md) of Aura.Filter with respect to validation rules, and [Rules To Sanitize Fields](https://github.com/auraphp/Aura.Filter/blob/2.x/docs/sanitize.md) with respect to sanitize rules. +Register input elements inside `init()` and apply validation and sanitization rules. See: + +- [Rules To Validate Fields](https://github.com/auraphp/Aura.Filter/blob/2.x/docs/validate.md) +- [Rules To Sanitize Fields](https://github.com/auraphp/Aura.Filter/blob/2.x/docs/sanitize.md) -We validate an associative array of the argument of the method. -If we want to change the input, we can set the values by implementing `submit()` method of `SubmitInterface` interface. +The associative array of method arguments is validated. To change the input, implement `SubmitInterface::submit()` and return the values to use. ## #[FormValidation] Attribute -Annotate the method that we want to validate with the `#[FormValidation]`, so that the validation is done in the form object specified by the `form` property before execution. -When validation fails, the method with the `ValidationFailed` suffix is called. +A method annotated with `#[FormValidation]` is validated before execution against the form object referenced by the `form` property. When validation fails, the method whose name is suffixed with `ValidationFailed` is invoked instead: ```php -use Ray\Di\Di\Inject; +use BEAR\Resource\ResourceObject; use Ray\Di\Di\Named; use Ray\WebFormModule\Annotation\FormValidation; use Ray\WebFormModule\FormInterface; -class MyController +class MyPage extends ResourceObject { - protected FormInterface $form; - - #[Inject] - #[Named('contact_form')] - public function setForm(FormInterface $form) - { - $this->form = $form; + public function __construct( + #[Named('contact_form')] private FormInterface $contactForm, + ) { } - #[FormValidation] - // or - // #[FormValidation(form: 'form', onFailure: 'onPostValidationFailed')] - public function onPost($name, $age) + #[FormValidation(form: 'contactForm')] + public function onPost(string $name, int $age): static { // validation success + return $this; } - public function onPostValidationFailed($name, $age) + public function onPostValidationFailed(string $name, int $age): static { - // validation failed + // validation failure + return $this; } } ``` -We can explicitly specify the name and the method by changing the `form` property of `#[FormValidation]` attribute or the `onValidationFailed` property. +Use the `form` property of `#[FormValidation]` to point to a different form property, and `onFailure` to specify a custom failure-handler method name: -The submit parameters will be passed to the `onPostValidationFailed` method. +```php +#[FormValidation(form: 'contactForm', onFailure: 'badRequestAction')] +public function onPost(string $name, int $age): static +{ + return $this; +} +``` + +The submitted arguments are forwarded to the failure-handler method. ## View -Specify the element name to get the `input` elements and error messages +Pass the element name to render the `input` markup or to fetch the error message: ```php - $form->input('name'); // - $form->error('name'); // "Please enter a double-byte characters or letters in the name." or blank +$form->input('name'); // e.g. +$form->error('name'); // e.g. "Name must be alphabetic only." ``` -The same applies to Twig template +The same works in Twig templates: -```php +```twig {% raw %}{{ form.input('name') }} {{ form.error('name') }}{% endraw %} ``` +If the form class implements `ToStringInterface`, the whole form can be rendered by casting it to string: + +```php +echo $form; // render the entire form HTML +``` + ## CSRF Protections -We can add a CSRF(Cross site request forgeries) object to the form to apply CSRF protections. +CSRF protection is **opt-in**. A form that uses `SetAntiCsrfTrait` is wired with an `AntiCsrfInterface`, but the token is only verified on methods annotated with `#[CsrfProtection]`. Methods without `#[CsrfProtection]` perform no CSRF check even if the form has an `AntiCsrf` object set. ```php +use BEAR\Resource\ResourceObject; +use Ray\WebFormModule\AbstractForm; +use Ray\WebFormModule\Annotation\CsrfProtection; +use Ray\WebFormModule\Annotation\FormValidation; use Ray\WebFormModule\SetAntiCsrfTrait; class MyForm extends AbstractForm { use SetAntiCsrfTrait; +} + +class MyPage extends ResourceObject +{ + #[FormValidation(form: 'contactForm')] + #[CsrfProtection] + public function onPost(string $name, int $age): static + { + // executed only when the CSRF token is valid + return $this; + } +} ``` -In order to increase the security level, add a custom CSRF class that contains the user authentication to the form class. -Please refer to the [Applying CSRF Protections](https://github.com/auraphp/Aura.Input#applying-csrf-protections) of Aura.Input for more information. +To raise the security level, provide a custom CSRF class that incorporates user authentication and set it on the form. See [Applying CSRF Protections](https://github.com/auraphp/Aura.Input#applying-csrf-protections) in Aura.Input for details. -## #[InputValidation] attribute +## #[InputValidation] -If we annotate the method with `#[InputValidation]` instead of `#[FormValidation]`, the exception `Ray\WebFormModule\Exception\ValidationException` is thrown when validation fails. -For convenience, HTML representation is not used in this case. +If a method is annotated with `#[InputValidation]` instead of `#[FormValidation]`, a `Ray\WebFormModule\Exception\ValidationException` is thrown when validation fails. No HTML representation is used, which is convenient for Web APIs. -When we `echo` the `error` property of the caught exception, we can see the representation of the media type [application/vnd.error+json](https://github.com/blongden/vnd.error). +`echo`ing the `error` property of the caught exception outputs an [application/vnd.error+json](https://github.com/blongden/vnd.error) representation: ```php http_response_code(400); @@ -156,13 +176,13 @@ echo $e->error; // "path": "/path/to/error", // "validation_messages": { // "name": [ -// "Please enter a double-byte characters or letters in the name." +// "Name must be alphabetic only." // ] // } // } ``` -We can add the necessary information to `vnd.error+json` using `#[VndError]` attribute. +Use the `#[VndError]` attribute to enrich the `vnd.error+json` payload: ```php #[FormValidation(form: 'contactForm')] @@ -172,27 +192,35 @@ We can add the necessary information to `vnd.error+json` using `#[VndError]` att path: '/path/to/error', href: ['_self' => '/path/to/error', 'help' => '/path/to/help'] )] -public function onPost() +public function onPost(): static +{ + return $this; +} ``` ## FormVndErrorModule -If we install `Ray\WebFormModule\FormVndErrorModule`, the method annotated with `#[FormValidation]` -will throw an exception in the same way as the method annotated with `#[InputValidation]`. -We can use the page resources as API. +Install `Ray\WebFormModule\FormVndErrorModule` to make `#[FormValidation]` methods throw the same exception as `#[InputValidation]` methods. Page resources can then be reused as API endpoints: ```php +use Ray\Di\AbstractModule; +use Ray\WebFormModule\WebFormModule; +use Ray\WebFormModule\FormVndErrorModule; + class FooModule extends AbstractModule { protected function configure() { - $this->install(new AuraInputModule); - $this->override(new FormVndErrorModule); + $this->install(new WebFormModule()); + $this->override(new FormVndErrorModule()); } } ``` +## Migration from 0.x + +Version 1.0 drops Doctrine Annotations in favour of native PHP 8 Attributes and turns CSRF protection into opt-in via `#[CsrfProtection]`, among other breaking changes. See the [Ray.WebFormModule README](https://github.com/ray-di/Ray.WebFormModule#migration-from-0x) and the [CHANGELOG](https://github.com/ray-di/Ray.WebFormModule/blob/1.x/CHANGELOG.md) for the upgrade steps. + ## Demo -Try the demo app [MyVendor.ContactForm](https://github.com/bearsunday/MyVendor.ContactForm) to get an idea on how forms such as -a confirmation form and multiple forms in a single page work. +Try the demo app [MyVendor.ContactForm](https://github.com/bearsunday/MyVendor.ContactForm) to see how a confirmation form and multiple forms on a single page work. diff --git a/manuals/1.0/ja/form.md b/manuals/1.0/ja/form.md index e98f87c4..f09c9d55 100644 --- a/manuals/1.0/ja/form.md +++ b/manuals/1.0/ja/form.md @@ -7,17 +7,17 @@ permalink: /manuals/1.0/ja/form.html # フォーム -[Aura.Input](https://github.com/auraphp/Aura.Input)と[Aura.Filter](https://github.com/auraphp/Aura.Filter)を使用したWebフォーム機能は、関連する機能が単一のクラスに集約され、テストや変更が容易です。1つのクラスでWebフォームとバリデーションの両方の用途に使用できます。 +[Aura.Input](https://github.com/auraphp/Aura.Input)と[Aura.Filter](https://github.com/auraphp/Aura.Filter)を使ったWebフォーム機能は、関連する処理を単一のクラスに集約するため、テストや変更が容易です。1つのフォームクラスをWebフォームの表示とバリデーションの両方に使用できます。 ## インストール -Aura.Inputを使用したフォーム処理を追加するために、composerで`ray/web-form-module`をインストールします: +composerで`ray/web-form-module`をインストールします: ```bash composer require ray/web-form-module ``` -アプリケーションモジュール`src/Module/AppModule.php`で`AuraInputModule`をインストールします: +アプリケーションモジュール`src/Module/AppModule.php`で`WebFormModule`をインストールします: ```php use BEAR\Package\AbstractAppModule; @@ -28,14 +28,14 @@ class AppModule extends AbstractAppModule protected function configure() { // ... - $this->install(new WebFormModule); + $this->install(new WebFormModule()); } } ``` -## Webフォーム +## フォームクラス -フォーム要素の登録やルールを定めた**フォームクラス**を作成して、`#[FormValidation]`アトリビュートを使用して特定のメソッドと束縛します。メソッドは送信されたデータがバリデーションOKのときのみ実行されます。 +フォーム要素の登録とバリデーションルールを定義する**フォームクラス**を作成し、`#[FormValidation]`アトリビュートで特定のメソッドに束縛します。バリデーションが成功したときだけ、そのメソッドが実行されます。 ```php use Ray\WebFormModule\AbstractForm; @@ -43,16 +43,13 @@ use Ray\WebFormModule\SetAntiCsrfTrait; class MyForm extends AbstractForm { - /** - * {@inheritdoc} - */ + use SetAntiCsrfTrait; + public function init() { - // フォームフィールドの設定 + // フォームフィールドの登録 $this->setField('name', 'text') - ->setAttribs([ - 'id' => 'name' - ]); + ->setAttribs(['id' => 'name']); // バリデーションルールとエラーメッセージの設定 $this->filter->validate('name')->is('alnum'); @@ -61,88 +58,114 @@ class MyForm extends AbstractForm } ``` -フォームクラスの`init()`メソッドでフォームのinput要素を登録し、バリデーションのフィルターやサニタイズのルールを適用します。 +`init()`メソッドでフォームの入力要素を登録し、バリデーションフィルターやサニタイズルールを適用します。詳しいルールは以下を参照してください: -バリデーションルールについては以下を参照してください: - [Rules To Validate Fields](https://github.com/auraphp/Aura.Filter/blob/2.x/docs/validate.md) - [Rules To Sanitize Fields](https://github.com/auraphp/Aura.Filter/blob/2.x/docs/sanitize.md) -メソッドの引数を連想配列にしたものをバリデーションします。入力を変更したい場合は`SubmitInterface`インターフェイスの`submit()`メソッドを実装して入力する値を返します。 +メソッドの引数を連想配列にしたものをバリデーションします。入力値を加工したい場合は`SubmitInterface::submit()`を実装して値を返します。 ## #[FormValidation]アトリビュート -フォームのバリデーションを行うメソッドを`#[FormValidation]`でアノテートすると、実行前に`form`プロパティのフォームオブジェクトでバリデーションが行われます。バリデーションに失敗するとメソッド名に`ValidationFailed`サフィックスをつけたメソッドが呼ばれます: +`#[FormValidation]`アトリビュートを付けたメソッドは、実行前に`form`プロパティのフォームオブジェクトでバリデーションされます。バリデーションが失敗すると、メソッド名に`ValidationFailed`サフィックスを付けたメソッドが呼び出されます: ```php -use Ray\Di\Di\Inject; +use BEAR\Resource\ResourceObject; use Ray\Di\Di\Named; use Ray\WebFormModule\Annotation\FormValidation; use Ray\WebFormModule\FormInterface; -class MyController +class MyPage extends ResourceObject { - protected FormInterface $form; - - #[Inject] - #[Named('contact_form')] - public function setForm(FormInterface $form) - { - $this->form = $form; + public function __construct( + #[Named('contact_form')] private FormInterface $contactForm, + ) { } - #[FormValidation] - // または - // #[FormValidation(form: 'form', onFailure: 'onPostValidationFailed')] - public function onPost($name, $age) + #[FormValidation(form: 'contactForm')] + public function onPost(string $name, int $age): static { // バリデーション成功時の処理 + return $this; } - public function onPostValidationFailed($name, $age) + public function onPostValidationFailed(string $name, int $age): static { // バリデーション失敗時の処理 + return $this; } } ``` -`#[FormValidation]`アトリビュートの`form`と`onValidationFailed`プロパティを変更して、`form`プロパティの名前やメソッドの名前を明示的に指定することもできます。`onPostValidationFailed`にはサブミットされた値が渡されます。 +`#[FormValidation]`の`form`プロパティでフォームプロパティ名を、`onFailure`プロパティで失敗時に呼び出すメソッド名を明示できます: + +```php +#[FormValidation(form: 'contactForm', onFailure: 'badRequestAction')] +public function onPost(string $name, int $age): static +{ + return $this; +} +``` + +失敗時メソッドにはサブミットされた引数がそのまま渡されます。 ## ビュー フォームの`input`要素やエラーメッセージを取得するには要素名を指定します: ```php -$form->input('name'); // 出力例: -$form->error('name'); // 出力例:名前は英数字のみ使用できます。 +$form->input('name'); // 例: +$form->error('name'); // 例:名前は英数字のみ使用できます。 ``` -Twigテンプレートを使用する場合も同様です: +Twigテンプレートでも同様です: ```twig {% raw %}{{ form.input('name') }} {{ form.error('name') }}{% endraw %} ``` +フォームクラスが`ToStringInterface`を実装していれば、フォーム全体を文字列として出力できます: + +```php +echo $form; // フォーム全体のHTMLを描画 +``` + ## CSRF -CSRF(クロスサイトリクエストフォージェリ)対策を行うためには、フォームにCSRFオブジェクトをセットします: +CSRF(クロスサイトリクエストフォージェリ)保護はopt-inです。フォームに`SetAntiCsrfTrait`を使うと`AntiCsrfInterface`が組み込まれますが、トークンの検証は`#[CsrfProtection]`アトリビュートを付けたメソッドでのみ実行されます。アトリビュートが無いメソッドでは、フォームが`AntiCsrf`オブジェクトを持っていてもCSRF検証は行われません。 ```php +use BEAR\Resource\ResourceObject; +use Ray\WebFormModule\AbstractForm; +use Ray\WebFormModule\Annotation\CsrfProtection; +use Ray\WebFormModule\Annotation\FormValidation; use Ray\WebFormModule\SetAntiCsrfTrait; class MyForm extends AbstractForm { use SetAntiCsrfTrait; } + +class MyPage extends ResourceObject +{ + #[FormValidation(form: 'contactForm')] + #[CsrfProtection] + public function onPost(string $name, int $age): static + { + // CSRFトークンが正しい場合のみ実行される + return $this; + } +} ``` -セキュリティレベルを高めるには、ユーザーの認証を含んだカスタムCsrfクラスを作成してフォームクラスにセットします。詳しくはAura.Inputの[Applying CSRF Protections](https://github.com/auraphp/Aura.Input#applying-csrf-protections)をご覧ください。 +セキュリティレベルを高めるには、ユーザー認証を組み込んだカスタムCsrfクラスを作成してフォームクラスにセットします。詳しくはAura.Inputの[Applying CSRF Protections](https://github.com/auraphp/Aura.Input#applying-csrf-protections)を参照してください。 ## #[InputValidation] -`#[FormValidation]`の代わりに`#[InputValidation]`とアノテートすると、バリデーションが失敗したときに`Ray\WebFormModule\Exception\ValidationException`が投げられます。この場合はHTML表現は使用されません。Web APIに便利です。 +`#[FormValidation]`の代わりに`#[InputValidation]`を使うと、バリデーション失敗時に`Ray\WebFormModule\Exception\ValidationException`が投げられます。HTML表現を使用しないのでWeb APIに便利です。 -キャッチした例外の`error`プロパティを`echo`すると[application/vnd.error+json](https://github.com/blongden/vnd.error)メディアタイプの表現が出力されます: +キャッチした例外の`error`プロパティを`echo`すると、[application/vnd.error+json](https://github.com/blongden/vnd.error)メディアタイプの表現が出力されます: ```php http_response_code(400); @@ -160,7 +183,7 @@ echo $e->error; // } ``` -`#[VndError]`アトリビュートで`vnd.error+json`に必要な情報を追加できます: +`#[VndError]`アトリビュートで`vnd.error+json`に追加情報を付与できます: ```php #[FormValidation(form: 'contactForm')] @@ -170,27 +193,35 @@ echo $e->error; path: '/path/to/error', href: ['_self' => '/path/to/error', 'help' => '/path/to/help'] )] -public function onPost() +public function onPost(): static +{ + return $this; +} ``` -## Vnd Error +## FormVndErrorModule -`Ray\WebFormModule\FormVndErrorModule`をインストールすると、`#[FormValidation]`でアノテートしたメソッドも`#[InputValidation]`とアノテートしたメソッドと同じように例外を投げるようになります。作成したPageリソースをAPIとして使用することができます: +`Ray\WebFormModule\FormVndErrorModule`をインストールすると、`#[FormValidation]`を付けたメソッドも`#[InputValidation]`と同様に例外を投げるようになります。Pageリソースをそのまま API として利用できます: ```php -use BEAR\Package\AbstractAppModule; +use Ray\Di\AbstractModule; +use Ray\WebFormModule\WebFormModule; use Ray\WebFormModule\FormVndErrorModule; class FooModule extends AbstractModule { protected function configure() { - $this->install(new WebFormModule); - $this->override(new FormVndErrorModule); + $this->install(new WebFormModule()); + $this->override(new FormVndErrorModule()); } } ``` +## 0.x からの移行 + +1.0 では Doctrine Annotations から PHP 8 Attributes へ移行し、CSRF 保護が `#[CsrfProtection]` による opt-in に変わるなどの破壊的変更があります。移行手順は[Ray.WebFormModule README](https://github.com/ray-di/Ray.WebFormModule#migration-from-0x)と[CHANGELOG](https://github.com/ray-di/Ray.WebFormModule/blob/1.x/CHANGELOG.md)を参照してください。 + ## デモ -[MyVendor.ContactForm](https://github.com/bearsunday/MyVendor.ContactForm)アプリケーションでフォームのデモを実行して試すことができます。確認付きのフォームページや、複数のフォームを1ページに設置したときの例などが用意されています。 +[MyVendor.ContactForm](https://github.com/bearsunday/MyVendor.ContactForm)で、確認画面付きフォームや複数フォームを1ページに設置した例などを試すことができます。