-
-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #117 from gsteel/v3/prepare-docs
Prepare docs for version 3
- Loading branch information
Showing
21 changed files
with
1,188 additions
and
13 deletions.
There are no files selected for viewing
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
114 changes: 114 additions & 0 deletions
114
docs/book/v3/application-integration/usage-in-a-laminas-mvc-application.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# Usage in a laminas-mvc Application | ||
|
||
The following example shows _one_ potential use case of laminas-inputfilter within a laminas-mvc based application. | ||
The example uses a module, a controller and the input filter plugin manager. | ||
|
||
The example is based on the [tutorial application](https://docs.laminas.dev/tutorials/getting-started/overview/) which builds an album inventory system. | ||
|
||
Before starting, make sure laminas-inputfilter is [installed and configured](../installation.md). | ||
|
||
## Create Input Filter | ||
|
||
[Create an input filter as separate class](../intro.md) using the `init` method, e.g. `module/Album/src/InputFilter/QueryInputFilter.php`: | ||
|
||
```php | ||
namespace Album\InputFilter; | ||
|
||
use Laminas\Filter\ToInt; | ||
use Laminas\I18n\Validator\IsInt; | ||
use Laminas\InputFilter\InputFilter; | ||
|
||
final class QueryInputFilter extends InputFilter | ||
{ | ||
public function init(): void | ||
{ | ||
// Page | ||
$this->add( | ||
[ | ||
'name' => 'page', | ||
'allow_empty' => true, | ||
'validators' => [ | ||
[ | ||
'name' => IsInt::class, | ||
], | ||
], | ||
'filters' => [ | ||
[ | ||
'name' => ToInt::class, | ||
], | ||
], | ||
'fallback_value' => 1, | ||
] | ||
); | ||
|
||
// … | ||
} | ||
} | ||
``` | ||
|
||
## Create Controller | ||
|
||
[Create a controller class](https://docs.laminas.dev/laminas-mvc/quick-start/#create-a-controller) and inject the input filter plugin manager via the constructor, e.g. `module/Album/Controller/AlbumController.php`: | ||
|
||
```php | ||
namespace Album\Controller; | ||
|
||
use Album\InputFilter\QueryInputFilter; | ||
use Laminas\InputFilter\InputFilterPluginManager; | ||
use Laminas\Mvc\Controller\AbstractActionController; | ||
|
||
use function assert; | ||
|
||
final class AlbumController extends AbstractActionController | ||
{ | ||
public function __construct( | ||
public readonly InputFilterPluginManager $inputFilterPluginManager | ||
) {} | ||
|
||
public function indexAction() | ||
{ | ||
$inputFilter = $this->inputFilterPluginManager->get(QueryInputFilter::class); | ||
assert($inputFilter instanceof QueryInputFilter); | ||
|
||
$inputFilter->setData($this->getRequest()->getQuery()); | ||
$inputFilter->isValid(); | ||
$filteredParams = $inputFilter->getValues(); | ||
|
||
// … | ||
} | ||
} | ||
``` | ||
|
||
> INFO: **Instantiating the Input Filter** | ||
> | ||
> The input filter plugin manager (`Laminas\InputFilter\InputFilterPluginManager`) is used instead of directly instantiating the input filter to ensure to get the filter and validator plugin managers injected. | ||
> This allows usage of any filters and validators registered with their respective plugin managers. | ||
> | ||
> Additionally, the input filter plugin manager calls the `init` method _after_ instantiating the input filter, ensuring all dependencies are fully injected first. | ||
## Register Input Filter and Controller | ||
|
||
If no separate factory is required for the input filter, then the input filter plugin manager will be instantiating the input filter class without prior registration. Otherwise, the input filter must be registered. | ||
|
||
To [register the controller](https://docs.laminas.dev/laminas-mvc/quick-start/#create-a-route) for 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`: | ||
|
||
<!-- markdownlint-disable MD033 --> | ||
<pre class="language-php" data-line="8-9"><code> | ||
namespace Album; | ||
|
||
use Laminas\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory; | ||
|
||
return [ | ||
'controllers' => [ | ||
'factories' => [ | ||
// Add this line | ||
Controller\AlbumController::class => ReflectionBasedAbstractFactory::class, | ||
], | ||
], | ||
// … | ||
]; | ||
</code></pre> | ||
<!-- markdownlint-enable MD033 --> | ||
|
||
The example uses the [reflection factory from laminas-servicemanager](https://docs.laminas.dev/laminas-servicemanager/reflection-abstract-factory/) to resolve the constructor dependencies for the controller class. |
178 changes: 178 additions & 0 deletions
178
docs/book/v3/application-integration/usage-in-a-mezzio-application.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
# Usage in a mezzio Application | ||
|
||
The following example shows _one_ potential use case of laminas-inputfilter within | ||
a mezzio based application. The example uses a module, config provider | ||
configuration, laminas-servicemanager as dependency injection container, the | ||
laminas-inputfilter plugin manager and a request handler. | ||
|
||
Before starting, make sure laminas-inputfilter is [installed and configured](../installation.md). | ||
|
||
## Create Input Filter | ||
|
||
Create an input filter as separate class, e.g. | ||
`src/Album/InputFilter/QueryInputFilter.php`: | ||
|
||
```php | ||
namespace Album\InputFilter; | ||
|
||
use Laminas\Filter\ToInt; | ||
use Laminas\I18n\Validator\IsInt; | ||
use Laminas\InputFilter\InputFilter; | ||
|
||
class QueryInputFilter extends InputFilter | ||
{ | ||
public function init() | ||
{ | ||
// Page | ||
$this->add( | ||
[ | ||
'name' => 'page', | ||
'allow_empty' => true, | ||
'validators' => [ | ||
[ | ||
'name' => IsInt::class, | ||
], | ||
], | ||
'filters' => [ | ||
[ | ||
'name' => ToInt::class, | ||
], | ||
], | ||
'fallback_value' => 1, | ||
] | ||
); | ||
|
||
// … | ||
} | ||
} | ||
``` | ||
|
||
## Using Input Filter | ||
|
||
### Create Handler | ||
|
||
Using the input filter in a request handler, e.g. | ||
`src/Album/Handler/ListHandler.php`: | ||
|
||
```php | ||
namespace Album\Handler; | ||
|
||
use Album\InputFilter\QueryInputFilter; | ||
use Psr\Http\Message\ServerRequestInterface; | ||
use Psr\Http\Server\RequestHandlerInterface; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Laminas\InputFilter\InputFilterInterface; | ||
use Mezzio\Template\TemplateRendererInterface; | ||
|
||
class ListHandler implements RequestHandlerInterface | ||
{ | ||
/** @var InputFilterInterface */ | ||
private $inputFilter; | ||
|
||
/** @var TemplateRendererInterface */ | ||
private $renderer; | ||
|
||
public function __construct( | ||
InputFilterInterface $inputFilter, | ||
TemplateRendererInterface $renderer | ||
) { | ||
$this->inputFilter = $inputFilter; | ||
$this->renderer = $renderer; | ||
} | ||
|
||
public function handle(ServerRequestInterface $request) : ResponseInterface | ||
{ | ||
$this->inputFilter->setData($request->getQueryParams()); | ||
$this->inputFilter->isValid(); | ||
$filteredParams = $this->inputFilter->getValues(); | ||
|
||
// … | ||
|
||
return new HtmlResponse($this->renderer->render( | ||
'album::list', | ||
[] | ||
)); | ||
} | ||
} | ||
``` | ||
|
||
### Create Factory for Handler | ||
|
||
Fetch the `QueryInputFilter` from the input filter plugin manager in a factory, | ||
e.g. `src/Album/Handler/ListHandlerFactory.php`: | ||
|
||
```php | ||
namespace Album\Handler; | ||
|
||
use Album\InputFilter\QueryInputFilter; | ||
use Psr\Container\ContainerInterface; | ||
use Laminas\InputFilter\InputFilterPluginManager; | ||
|
||
class ListHandlerFactory | ||
{ | ||
public function __invoke(ContainerInterface $container) | ||
{ | ||
/** @var InputFilterPluginManager $pluginManager */ | ||
$pluginManager = $container->get(InputFilterPluginManager::class); | ||
$inputFilter = $pluginManager->get(QueryInputFilter::class); | ||
|
||
return new ListHandler( | ||
$inputFilter, | ||
$container->get(TemplateRendererInterface::class) | ||
); | ||
} | ||
} | ||
``` | ||
|
||
> ### Instantiating the InputFilter | ||
> | ||
> The `InputFilterPluginManager` is used instead of directly instantiating the | ||
> input filter to ensure to get the filter and validator plugin managers | ||
> injected. This allows usage of any filters and validators registered with | ||
> their respective plugin managers. | ||
> | ||
> Additionally the `InputFilterPluginManager` calls the `init` method _after_ | ||
> instantiating the input filter, ensuring all dependencies are fully injected | ||
> first. | ||
## Register Input Filter and Handler | ||
|
||
Extend the configuration provider of the module to register the input filter and | ||
the request handler, e.g. `src/Album/ConfigProvider.php`: | ||
|
||
```php | ||
namespace Album; | ||
|
||
class ConfigProvider | ||
{ | ||
public function __invoke() : array | ||
{ | ||
return [ | ||
'dependencies' => $this->getDependencies(), | ||
'input_filters' => $this->getInputFilters(), // <-- Add this line | ||
]; | ||
} | ||
|
||
public function getDependencies() : array | ||
{ | ||
return [ | ||
'factories' => [ | ||
Handler\ListHandler::class => Handler\ListHandlerFactory::class, // <-- Add this line | ||
// … | ||
], | ||
]; | ||
} | ||
|
||
// Add the following method | ||
public function getInputFilters() : array | ||
{ | ||
return [ | ||
'factories' => [ | ||
InputFilter\QueryInputFilter::class => InvokableFactory::class, | ||
], | ||
]; | ||
} | ||
|
||
// … | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Using Laminas InputFilter with Static Analysis Tools | ||
|
||
It can be tedious to assert that the array returned by a given input filter contains the keys and value types that you would expect before using those values in your domain. | ||
If you use static analysis tools such as Psalm or PHPStan, `InputFilterInterface` defines a generic template that can be used to refine the types you receive from the `getValues()` method. | ||
|
||
```php | ||
<?php | ||
|
||
namespace My; | ||
|
||
use Laminas\Filter\ToInt; | ||
use Laminas\Filter\ToNull; | ||
use Laminas\I18n\Validator\IsInt; | ||
use Laminas\InputFilter\InputFilter;use Laminas\Validator\GreaterThan; | ||
|
||
/** | ||
* @psalm-type ValidPayload = array{ | ||
* anInteger: int<1, max>, | ||
* } | ||
* @extends InputFilter<ValidPayload> | ||
*/ | ||
final class SomeInputFilter extends InputFilter | ||
{ | ||
public function init(): void | ||
{ | ||
$this->add([ | ||
'name' => 'anInteger', | ||
'required' => true, | ||
'filters' => [ | ||
['name' => ToNull::class], | ||
['name' => ToInt::class], | ||
], | ||
'validators' => [ | ||
['name' => NotEmpty::class], | ||
['name' => IsInt::class], | ||
[ | ||
'name' => GreaterThan::class, | ||
'options' => [ | ||
'min' => 1, | ||
], | ||
], | ||
], | ||
]); | ||
} | ||
} | ||
``` | ||
|
||
With the above input filter specification, one can guarantee that, if the input payload is deemed valid, then you will receive an array with the expected shape from `InputFilter::getValues()`, therefore, your static analysis tooling will not complain when you pass that value directly to something that expects a `positive-int`, for example: | ||
|
||
```php | ||
/** | ||
* @param positive-int $value | ||
* @return positive-int | ||
*/ | ||
function addTo5(int $value): int | ||
{ | ||
return $value + 5; | ||
} | ||
|
||
$filter = new SomeInputFilter(); | ||
$filter->setData(['anInteger' => '123']); | ||
assert($filter->isValid()); | ||
|
||
$result = addTo5($filter->getValues()['anInteger']); | ||
``` | ||
|
||
## Further reading | ||
|
||
- [Psalm documentation on array shapes](https://psalm.dev/docs/annotating_code/type_syntax/array_types/#array-shapes) | ||
- [Psalm documentation on type aliases](https://psalm.dev/docs/annotating_code/type_syntax/utility_types/#type-aliases) | ||
- [PHPStan documentation on array shapes](https://phpstan.org/writing-php-code/phpdoc-types#array-shapes) | ||
- [PHPStan documentation on type aliases](https://phpstan.org/writing-php-code/phpdoc-types#local-type-aliases) |
Oops, something went wrong.