diff --git a/docs/book/index.md b/docs/book/index.md deleted file mode 120000 index fe8400541..000000000 --- a/docs/book/index.md +++ /dev/null @@ -1 +0,0 @@ -../../README.md \ No newline at end of file diff --git a/docs/book/index.md b/docs/book/index.md new file mode 100644 index 000000000..b8c87cfac --- /dev/null +++ b/docs/book/index.md @@ -0,0 +1,21 @@ +## Installation + +### Using Composer + +```bash +$ composer require laminas/laminas-form +``` + +## Learn + + diff --git a/docs/book/v3/application-integration/form-element-manager-mvc.md b/docs/book/v3/application-integration/form-element-manager-mvc.md new file mode 100644 index 000000000..fc57331eb --- /dev/null +++ b/docs/book/v3/application-integration/form-element-manager-mvc.md @@ -0,0 +1,283 @@ +# Usage of Form Element Manager in a laminas-mvc Application + +INFO: +The following examples show the usage of the form element manager in a laminas-mvc based application. +All **the basics of the form element manager** [can be found in a separate section](../form-element-manager.md). + +## Using the Form Element Manager in a Controller + +### Create Controller + +[Create a controller class](https://docs.laminas.dev/laminas-mvc/quick-start/#create-a-controller) and inject the form element manager via the constructor, e.g. `module/Album/Controller/AlbumController.php`: + +```php +namespace Album\Controller; + +use Laminas\Form\FormElementManager; +use Laminas\Mvc\Controller\AbstractActionController; + +final class AlbumController extends AbstractActionController +{ + public function __construct( + private readonly FormElementManager $formElementManager + ) {} +} +``` + +### Register Controller + +In a laminas-mvc based application, the form element manager is registered in the application during the [installation of laminas-form](../installation.md#installation-for-mezzio-and-laminas-mvc-application). +This allows fetching the form element manager from the application service container. +When using the [reflection factory of laminas-servicemanager](https://docs.laminas.dev/laminas-servicemanager/reflection-abstract-factory/), the form element manager can be automatically injected into the controller. + +To [register the controller](https://docs.laminas.dev/laminas-mvc/quick-start/#create-a-route) in the application, extend the configuration of the module. +Add the following lines to the module configuration file, e.g. `module/Album/config/module.config.php`: + +

+namespace Album;
+
+use Laminas\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory;
+
+return [
+    'controllers' => [
+        'factories' => [
+            // Add this line
+            Controller\AlbumController::class => ReflectionBasedAbstractFactory::class,
+        ],
+    ],
+    // …
+];
+
+ +## Fetch a Form without Registration + +The following example creates a form class: + +```php +final class AlbumForm extends Laminas\Form\Form +{ + public function init(): void + { + // … + } +} +``` + +Extend the controller and fetch the form: + +

+namespace Album\Controller;
+
+use Album\Form\AlbumForm;
+use Laminas\Form\FormElementManager;
+use Laminas\Mvc\Controller\AbstractActionController;
+
+use function assert;
+
+final class AlbumController extends AbstractActionController
+{
+    public function __construct(
+        public readonly FormElementManager $formElementManager
+    ) {}
+    
+    public function addAction()
+    {
+        $form = $this->formElementManager->get(AlbumForm::class);
+        assert($form instanceof AlbumForm);
+        
+        // …
+    }
+}
+
+ +## Register and Fetch a Form With a Factory + +If a form needs some preparation for creation then a factory can be used. + +The following example creates a factory class for the form: + +```php +final class AlbumFormFactory +{ + public function __invoke(Psr\Container\ContainerInterface): AlbumForm + { + $form = new AlbumForm(); + $form->setName('album'); + + return $form; + } +} +``` + +Register the form on the form element manager via the configuration key `form_elements` in the module configuration, e.g. `module/Album/config/module.config.php`: + +

+namespace Album;
+
+return [
+    'form_elements' => [
+        'factories' => [
+            // Add this line
+            Form\AlbumForm::class => Form\AlbumFormFactory::class,
+        ],
+    ],
+    // …
+];
+
+ +To retrieve the form and the form name in a controller: + +```php +namespace Album\Controller; + +use Album\Form\AlbumForm; +use Laminas\Form\FormElementManager; +use Laminas\Mvc\Controller\AbstractActionController; + +use function assert; + +final class AlbumController extends AbstractActionController +{ + // … + + public function addAction() + { + $form = $this->formElementManager->get(AlbumForm::class); + assert($form instanceof AlbumForm); + + echo $form->getName(); // album + + // … + } +} +``` + +Now the custom factory will be used to create the form instance. + +## Using a Custom Element without Registration + +The form element manager [allows fetching custom elements without prior registration](../form-element-manager.md#usage-of-the-form-element-manager-in-a-form) with the manager. + +The following example creates a custom element: + +```php +final class ExampleElement extends Laminas\Form\Element +{ + // … +} +``` + +Create a form and add the custom element: + +```php +final class ExampleForm extends Laminas\Form\Form +{ + public function init(): void + { + $this->add([ + 'type' => ExampleElement::class, + 'name' => 'example', + 'options' => [ + 'label' => 'Example element' + ], + ]); + + // … + } +} +``` + +Fetch the form and the element in a controller: + +```php +namespace Album\Controller; + +use Album\Form\AlbumForm; +use Laminas\Form\FormElementManager; +use Laminas\Mvc\Controller\AbstractActionController; + +use function assert; + +final class AlbumController extends AbstractActionController +{ + // … + + public function addAction() + { + $form = $this->formElementManager->get(AlbumForm::class); + assert($form instanceof AlbumForm); + + echo $form->get('example')->getLabel(); // Example element + + // … + } +} +``` + +## Register and Use a Custom Element via a Factory + +If a custom element needs some preparation for creation then a factory can be used. + +The following example creates a factory class for the element of the previous example: + +```php +final class ExampleElementFactory +{ + public function __invoke(Psr\Container\ContainerInterface): ExampleElement + { + $element = new ExampleElement(); + $element->setOption('example_param', 'value'); + + return $element; + } +} +``` + +Register the element on the form element manager via the configuration key `form_elements` in the module configuration, e.g. `module/Album/config/module.config.php`: + +

+namespace Album;
+
+return [
+    'form_elements' => [
+        'factories' => [
+            // Add this line
+            Form\ExampleElement::class => Form\ExampleElementFactory::class,
+        ],
+    ],
+    // …
+];
+
+ +Now the custom factory will be used to create the element instance. + +## Configuring the Form Element Manager + +The [configuration of the form element manager follows the exact same pattern](https://docs.laminas.dev/laminas-servicemanager/configuring-the-service-manager/) as for a normal service manager of laminas-servicemanager. + +In a laminas-mvc based application this means using the application or module configuration, such as `config/autload/global.php` or `module/Album/config/module.config.php`, and the configuration key `form_elements`: + +```php +return [ + 'form_elements' => [ + 'factories' => [ + Album\Form\ExampleElement::class => Album\Form\ExampleElementFactory::class, + ], + 'aliases' => [ + 'example' => Album\Form\ExampleElement::class, + ], + 'abstract_factories' => [], + 'delegators' => [], + // … + ] +]; +``` + +The factory `Laminas\Form\FormElementManagerFactory` uses the configuration, searches for the configuration key `form_elements` and creates the form element manager using the discovered configuration. + +## Learn More + +- [The basics of the form element manager](../form-element-manager.md) +- [Creating custom elements](../advanced.md#creating-custom-elements) +- [Handling dependencies](../advanced.md#handling-dependencies) +- [Configuring the service manager](https://docs.laminas.dev/laminas-servicemanager/configuring-the-service-manager/) diff --git a/docs/book/v3/form-element-manager.md b/docs/book/v3/form-element-manager.md new file mode 100644 index 000000000..0aee00bcb --- /dev/null +++ b/docs/book/v3/form-element-manager.md @@ -0,0 +1,318 @@ +# Form Element Manager + +The form element manager is a *specialized* dependency injection container to obtain objects which implements the `Laminas\Form\ElementInterface` interface. + +The manager is the central object of laminas-form to create and retrieve all types of form elements, fieldsets and forms. + +It handles all included form elements of laminas-form, user-defined elements and also whole forms. + +The manager is based on the [plugin manager of laminas-servicemanager](https://docs.laminas.dev/laminas-servicemanager/plugin-managers/). + +## Benefits + +The following benefits are provided by the manager: + +- Handles all forms, fieldsets and elements, including all custom ones. +- Create forms, fieldsets and elements with all dependencies like validators and filters. +- Can create forms, fieldsets and elements with name and options. +- Can provide elements, fieldsets and forms without prior registration. +- Fetches hydrators and input filters from the application's service container and adds them to a form or a fieldset. +- Allows overriding existing elements or extending them. + +> INFO: **Stand-Alone Usage** +> The following examples show the basics of the form element manager using stand-alone usage. +> +> The configuration and usage in **laminas-mvc** or **Mezzio** based application [can be found in separate sections](#learn-more). + +## Create a Form Element Manager + +To create an instance, the form element manager requires a [PSR-11 dependency container](https://www.php-fig.org/psr/psr-11/). +The following examples use the PSR-11 container implementation provided by [laminas-servicemanager](https://docs.laminas.dev/laminas-servicemanager/): + +```php +$formElementManager = new Laminas\Form\FormElementManager( + new Laminas\ServiceManager\ServiceManager() +); +``` + +## Fetch a Standard Element + +To get a supplied form element, use the class name of the element: + +```php +$element = $formElementManager->get(Laminas\Form\Element\Select::class); +``` + +## Fetch and Configure a Standard Element + +The form element manager uses the factory `Laminas\Form\ElementFactory` to create all elements, fieldsets, and forms. +This factory allows the configuration of an element during fetching: + +```php +$element = $formElementManager->get( + Laminas\Form\Element\Select::class, + [ + 'name' => 'rating', + 'options' => [ + 'label' => 'Rating', + 'value_options' => [1, 2, 3, 4, 5], + ], + ], + ] +); +``` + +The name for the element and the options array are provided as parameters to the associated class constructor on instantiation: + +```php +public function __construct($name = null, iterable $options = []) {} +``` + +Retrieving the name and the set options: + +```php +echo $element->getName(); // rating +echo $element->getLabel(); // Rating + +$valueOptions = $element->getValueOptions(); // [1, 2, 3, 4, 5] +``` + +## Fetch a Custom Element without Registration + +The form element manager allows fetching custom elements without prior registration with the manager. + +The following example creates a custom element: + +```php +final class ExampleElement extends Laminas\Form\Element +{ + // … +} +``` + +The form element manager can create these custom elements by the related class name: + +```php +$element = $formElementManager->get(ExampleElement::class); +``` + +The manager uses the factory `Laminas\Form\ElementFactory` to instantiate the element, and will pass the name and options array just like in the prior example: + +```php +$element = $formElementManager->get( + ExampleElement::class, + [ + 'name' => '…', + 'options' => [ + // … + ], + ] +); +``` + +## Register and Fetch a Custom Element with a Factory + +If a custom element needs some preparation for creation then a factory can be used. + +The following example creates a factory class for the element: + +```php +final class ExampleElementFactory +{ + public function __invoke(Psr\Container\ContainerInterface): ExampleElement + { + $element = new ExampleElement(); + $element->setOption('example_param', 'value'); + + return $element; + } +} +``` + +Register the custom element on the form element manager by using a factory class: + +```php +$formElementManager->setFactory( + ExampleElement::class, + ExampleElementFactory::class +); +``` + +Get the custom element and the option: + +```php +$element = $formElementManager->get(ExampleElement::class); + +echo $form->getOption('example_param'); // value +``` + +Now the custom factory will be used to create the element instance. + +## Fetch a Form without Registration + +The form element manager also handles whole forms. + +The following example creates a form class: + +```php +final class ExampleForm extends Laminas\Form\Form +{ + public function init(): void + { + // … + } +} +``` + +The form element manager can create the form by the related class name: + +```php +$form = $formElementManager->get(ExampleForm::class); +``` + +If no separate factory is required, then the form element manager will be instantiating the form class directly, by using the standard factory for elements (`Laminas\Form\ElementFactory`). + +## Register and Fetch a Form With a Factory + +If a form needs some preparation for creation then a factory can be used. + +The following example creates a class as factory for the form: + +```php +final class ExampleFormFactory +{ + public function __invoke(Psr\Container\ContainerInterface): ExampleForm + { + $form = new ExampleForm(); + $form->setName('example'); + + return $form; + } +} +``` + +Register the form on the form element manager by using a factory class: + +```php +$formElementManager->setFactory( + ExampleForm::class, + ExampleFormFactory::class +); +``` + +Get the form and the name: + +```php +$form = $formElementManager->get(ExampleForm::class); + +echo $form->getName(); // example +``` + +Now the custom factory will be used to instantiate the form. + +## Set a Paramater to a Form on Instantiation + +The options of a form can be used to set custom parameters to a form. + +- It can be hooked into the initialization via the `init()` method. +- The form element factory calls the `init()` method after creating an instance of the class. +- Since the name and options are processed in the constructor, it can access them within `init()` to perform further customization of the instance. + +Create a class for the form and get a custom option: + +```php +final class ExampleForm extends Laminas\Form\Form +{ + public function init(): void + { + /** @var mixed|null $exampleParam */ + $exampleParam = $this->getOption('example_param'); + + // … + } +} +``` + +The standard factory `Laminas\Form\ElementFactory` for form elements and forms is used which provides the name and/or the options to the object as arguments to the constructor: + +```php +$form = $formElementManager->get( + ExampleForm::class, + [ + 'options' => [ + 'example_param' => 'value', + ], + ] +); + +echo $form->getOption('example_param'); // value +``` + +## Usage of the Form Element Manager in a Form + +If an element is added to a form via the `add` method and the definition of the element is provided via an array then the form factory `Laminas\Form\Factory` will be used to create this element. +The form factory uses the form element manager to fetch the element. +The following example uses a custom element in a form: + +```php +final class ExampleForm extends Laminas\Form\Form +{ + public function init(): void + { + $this->add([ + 'type' => ExampleElement::class, + 'name' => 'example', + 'options' => [ + 'label' => 'Example element' + ], + ]); + + // … + } +} +``` + +Fetch the form and the element: + +```php +$form = $formElementManager->get(ExampleForm::class); + +echo $form->get('example')->getLabel(); // Example element +``` + +The form element manager will provide the form with the custom element which is created by the form element manager, like before: with or without explicit registration of the element. + +## Configuring the Form Element Manager + +The manager is based on the [plugin manager of laminas-servicemanager](https://docs.laminas.dev/laminas-servicemanager/plugin-managers/) and the [configuration follows the exact same pattern](https://docs.laminas.dev/laminas-servicemanager/configuring-the-service-manager/) as for a normal service manager of laminas-servicemanager: + +```php +$formElementManager = new Laminas\Form\FormElementManager( + new Laminas\ServiceManager\ServiceManager(), + [ + 'factories' => [ + Album\Form\ExampleElement::class => Album\Form\ExampleElementFactory::class, + ], + 'aliases' => [ + 'example' => Album\Form\ExampleElement::class, + ], + 'abstract_factories' => [], + 'delegators' => [], + // … + ] +); +``` + +## Why Use the Form Element Manager? + +- It allows overwriting or extending elements without changing form specifications. +- It allows decoration of elements, like adding a database adapter without passing the adapter through all layers of a form. +- It manages instantiation and initialization of elements, fieldsets and forms, including all dependencies and configuration options. +- It allows usage and configuration of custom elements without prior registration of the elements. +- It handles all form object types: elements, fieldsets and the form itself. + +## Learn More + +- [Usage of Form Element Manager in a laminas-mvc Application](application-integration/form-element-manager-mvc.md) +- [The `init` method](advanced.md#initialization) +- [Configuring the service manager](https://docs.laminas.dev/laminas-servicemanager/configuring-the-service-manager/) diff --git a/mkdocs.yml b/mkdocs.yml index f6b92ab1c..4f7b9bd61 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -11,6 +11,7 @@ nav: - "Creation via Factory": v3/form-creation/creation-via-factory.md - "Factory-Backed Form Extension": v3/form-creation/factory-backed-form-extension.md - "Using PHP8 Attributes or DocBlock Annotations": v3/form-creation/attributes-or-annotations.md + - "Form Element Manager": v3/form-element-manager.md - Elements: - Introduction: v3/element/intro.md - Element: v3/element/element.md @@ -91,6 +92,7 @@ nav: - "Advanced Use Cases": v3/advanced.md - "Application Integration": - "Usage in a laminas-mvc application": v3/application-integration/usage-in-a-laminas-mvc-application.md + - "Usage of Form Element Manager in a laminas-mvc Application": v3/application-integration/form-element-manager-mvc.md - Migration: - "Migration from Version 2 to 3": v3/migration/v2-to-v3.md - v2: @@ -189,6 +191,7 @@ extra: installation: config_provider_class: 'Laminas\Form\ConfigProvider' module_class: 'Laminas\Form\Module' + show_special_homepage: true plugins: - search - redirects: