Linio Input is yet another component of the Linio Framework. It aims to abstract HTTP request input handling, allowing a seamless integration with your domain model. The component is responsible for:
- Parsing request body contents
- Validating input data
- Hydrating input data into objects
The recommended way to install Linio Input is through composer.
{
"require": {
"linio/input": "dev-master"
}
}To run the test suite, you need install the dependencies via composer, then run PHPUnit.
$ composer install
$ phpunit
The library is very easy to use: first, you have to create your input handler class. The input handlers are responsible for specifying which data you're expecting to receive from requests. Let's create one:
<?phpnamespaceLinio\Api\Handler;
useLinio\Component\Input\InputHandler;
class RegistrationHandler extends InputHandler
{
publicfunctiondefine()
{
$this->add('referrer', 'string');
$this->add('registration_date', 'datetime');
$user = $this->add('user', 'Linio\Model\User');
$user->add('name', 'string');
$user->add('email', 'string');
$user->add('age', 'integer');
}
}Now, in your controller, you just need to bind data to the handler:
<?phpnamespaceLinio\Api\Controller;
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
class RegistrationController
{
publicfunctionregisterAction(Request$request): Response
{
$input = newRegistrationHandler();
$input->bind($request->request->all());
if (!$input->isValid()) {
returnnewResponse($input->getErrorsAsString());
}
$data = $input->getData();
$data['referrer']; // string$data['registration_date']; // \DateTime$data['user']; // Linio\Model\UserreturnnewResponse(['message' => 'Valid!']);
}
}When you are defining the fields for your input handler, there are a few types
available: string, int, bool, datetime, etc. Those are predefined types
provided by the library, but you can also create your own. This magic is
handled by Linio\Component\Input\TypeHandler. The TypeHandler allows you to
add new types, which are extensions of the BaseNode class.
<?phpclass GuidNode extends BaseNode
{
publicfunction__construct()
{
$this->addConstraint(newLinio\Component\Input\Constraint\GuidValue());
}
}
$typeHandler = newLinio\Component\Input\TypeHandler();
$typeHandler->addType('guid', GuidNode::class);
$input = newRegistrationHandler();
$input->setTypeHandler($typeHandler);In this example, we have created a new guid type, which has a built-in constraint
to validate contents. You can use custom types to do all sorts of things: add
predefined constraint chains, transformers, instantiators and also customize how
values are generated.
Linio Input allows you to apply constraints to your fields. This can be done
by providing a third argument for the add() method in your input handlers:
<?phpuseLinio\Component\Input\Constraint\Pattern;
class RegistrationHandler extends InputHandler
{
publicfunctiondefine()
{
$this->add('referrer', 'string', ['required' => true]);
$this->add('registration_date', 'datetime');
$user = $this->add('user', 'Linio\Model\User');
$user->add('name', 'string');
$user->add('email', 'string', ['constraints' => [newPattern('/^\S+@\S+\.\S+$/')]]);
$user->add('age', 'integer');
}
}The library includes several constraints by default:
- Enum
- GuidValue
- NotNull
- Pattern
- StringSize
Linio Input allows you to create data transformers, responsible for converting simple input data, like timestamps and unique IDs, into something meaningful, like a datetime object or the full entity (by performing a query).
<?phpnamespaceLinio\Api\Handler\Transformer;
useDoctrine\Common\Persistence\ObjectRepository;
useLinio\Component\Input\Transformer\TransformerInterface;
class IdTransformer implements TransformerInterface
{
/** * @var ObjectRepository */protected$repository;
publicfunctiontransform($value)
{
try {
$entity = $this->repository->find($value);
} catch (\Exception$e) {
returnnull;
}
return$entity;
}
publicfunctionsetRepository(ObjectRepository$repository)
{
$this->repository = $repository;
}
}Data transformers can be added on a per-field basis during the definition of your input handler:
<?phpuseLinio\Api\Handler\Transformer\IdTransformer;
class RegistrationHandler extends InputHandler
{
/** * @var IdTransformer */protected$idTransformer;
publicfunctiondefine()
{
$this->add('store_id', 'string', ['transformer' => $this->idTransformer]);
}
publicfunctionsetIdTransformer(IdTransformer$idTransformer)
{
$this->idTransformer = $idTransformer;
}
}Linio Input allows you to use different object instantiators on a per-field
basis. This can be done by providing a third argument for the add() method
in your input handlers:
<?phpuseLinio\Component\Input\Instantiator\ConstructInstantiator;
useLinio\Component\Input\Instantiator\ReflectionInstantiator;
class RegistrationHandler extends InputHandler
{
publicfunctiondefine()
{
$this->add('foobar', 'My\Foo\Class', ['instantiator' => newConstructInstantiator()]);
$this->add('barfoo', 'My\Bar\Class', ['instantiator' => newReflectionInstantiator()]);
}
}The library includes several instantiators by default:
- ConstructInstantiator
- PropertyInstantiator
- SetInstantiator
- ReflectionInstantiator
By default, the SetInstantiator is used by Object and Collection nodes.
Linio Input supports portable, reusable InputHandlers via nesting. This is accomplished
by including the handler to the options parameter when adding fields.
Suppose your application deals with mailing addresses:
<?phpclass OrderHandler extends InputHandler
{
publicfunctiondefine()
{
$address = $this->add('shipping_address', Address::class);
$address->add('street', 'string');
$address->add('city', 'string');
$address->add('state', 'string');
$address->add('zip_code', 'integer');
}
}Rather than duplicating this everywhere you need to handle an address, you can extract the address into its own InputHandler and re-use it throughout your application.
<?phpclass AddressHandler extends InputHandler
{
publicfunctiondefine()
{
$address->add('street', 'string');
$address->add('city', 'string');
$address->add('state', 'string');
$address->add('zip_code', 'integer');
}
}
class OrderHandler extends InputHander
{
publicfunctiondefine()
{
$this->add('shipping_address', Address::Class, ['handler' => newAddressHandler()]);
}
}
class RegistrationHandler extends InputHander
{
publicfunctiondefine()
{
$this->add('home_address', Address::Class, ['handler' => newAddressHandler()]);
}
}
