Validate and map data from request and map data to response
api:
plugins:
Apitte\Core\DI\Plugin\CoreMappingPlugin:
Ensure you have also decorator plugin registered, mapping is implemented by decorators.
Validate request parameters and convert them to correct php datatype.
Do you remember UsersController example? Imagine that you need add new endpoint to get an user by its numeric ID. You will probably want validate that ID and cast it to integer. That is what request parameters are used for.
namespace App\Api\V1\Controllers;
use Apitte\Core\Annotation\Controller\Method;
use Apitte\Core\Annotation\Controller\Path;
use Apitte\Core\Annotation\Controller\RequestParameters;
use Apitte\Core\Annotation\Controller\RequestParameter;
use Apitte\Core\Http\ApiRequest;
use Apitte\Core\Http\ApiResponse;
/**
* @Path("/users")
*/
class UsersController extends BaseV1Controller
{
/**
* @Path("/{id}")
* @Method("GET")
* @RequestParameters({
* @RequestParameter(name="id", type="int", description="My favourite user ID")
* })
*/
public function detail(ApiRequest $request): ApiResponse
{
/** @var int $id Perfectly valid integer */
$id = $request->getParameter('id');
// Return response with error or user
}
}
@RequestParameter()
have few available options.
name="nameOfParameter"
- same as name of parameter in path, query...type="string|int|float|bool|datetime"
- data type, see data typesdescription={string}
- description of parameter, used in openapi schemain="path|query|header|cookie"
- whether parameter is located in url path, url query is a http header or cookie, default is pathrequired=true|false
- required by default, but you could make parameter optionaldeprecated=true|false
- used in openapi schemaallowEmpty=true|false
- make parameter nullable
-
string
- Simply returns given value.
-
int
- Converts value to int.
- Could overflow to float if value is bigger than PHP could handle.
-
float
- Converts value to float.
- Accepts values which have decimals divided by comma
,
or dot.
-
bool
- Converts
'true'
totrue
- and
'false'
tofalse
- Converts
-
datetime
- Converts value to DateTimeImmutable.
-
You can define custom data types
-
Each of the data types could return null if request parameter is allowed to be empty
-
If conversion is not possible (because data type is invalid) then API returns HTTP 400
You could override each datatype with your own implementation (e.g. if you need handle bigger numbers then PHP could handle in native integer and float)
api:
plugins:
Apitte\Core\DI\Plugin\CoreMappingPlugin:
types:
string: Apitte\Core\Mapping\Parameter\StringTypeMapper
int: Apitte\Core\Mapping\Parameter\IntegerTypeMapper
float: Apitte\Core\Mapping\Parameter\FloatTypeMapper
bool: Apitte\Core\Mapping\Parameter\BooleanTypeMapper
datetime: Apitte\Core\Mapping\Parameter\DateTimeTypeMapper
You can also add your custom data types.
api:
plugins:
Apitte\Core\DI\Plugin\CoreMappingPlugin:
types:
email: MyEmailTypeMapper
use Apitte\Core\Mapping\Parameter\ITypeMapper;
use Apitte\Core\Exception\Runtime\InvalidArgumentTypeException;
class MyEmailTypeMapper implements ITypeMapper
{
public function normalize($value): string
{
if (is_string($value) && filter_var($value, FILTER_VALIDATE_EMAIL)) {
return $value;
}
throw new InvalidArgumentTypeException('email', 'Pass valid email address.');
}
}
Imagine you have a data grid with many filter options. You can describe all options manually or
use value object, entity, for it. And it leads us to @RequestBody
.
We have an entity with described fields.
namespace App\Api\Entity\Request;
use Apitte\Core\Mapping\Request\BasicEntity;
final class UserFilter extends BasicEntity
{
/** @var int */
public $userId;
/** @var string */
public $email;
}
And some endpoint with @RequestBody
annotation. There's a method ApiRequest::getEntity()
, it gets
the entity from request attributes. So simple, right?
/**
* @Path("/filter")
* @Method("GET")
* @RequestBody(entity="App\Api\Entity\Request\UserFilter")
*/
public function filter(ApiRequest $request)
{
/** @var UserFilter $entity */
$entity = $request->getEntity();
}
Validate request entities with validator.
By default no validator is used.
Supports @required annotation - check if attribute is not empty
api:
plugins:
Apitte\Core\DI\Plugin\CoreMappingPlugin:
request:
validator: Apitte\Core\Mapping\Validator\BasicValidator
Requires a doctrine annotation reader, you may use nettrine/annotations
Also install symfony/validator
extensions:
annotations: Nettrine\Annotations\DI\AnnotationsExtension
api:
plugins:
Apitte\Core\DI\Plugin\CoreMappingPlugin:
request:
validator: Apitte\Core\Mapping\Validator\SymfonyValidator()
Using SymfonyValidator your request entity could look like this:
use Apitte\Core\Mapping\Request\BasicEntity;
final class UserFilter extends BasicEntity
{
/**
* @var int
* @Assert\NotNull()
* @Assert\Type(
* type="integer",
* message="The value {{ value }} is not a valid {{ type }}."
* )
*/
public $userId;
}
You can override ConstraintValidatorFactory
on SymfonyValidator
. If you want to use custom validation contstraints with support of Nette DI,
you should also install contributte/validator.
If you want to use translated constraint messages, you can use contributte/translation.
Take a look at example.
services:
symfonyValidator:
factory: Apitte\Core\Mapping\Validator\SymfonyValidator
setup:
- setConstraintValidatorFactory(Contributte\Validator\ContainerConstraintValidatorFactory())
- setTranslator(@Contributte\Translation\Translator)
- setTranslationDomain('validators')
api:
plugins:
Apitte\Core\DI\Plugin\CoreMappingPlugin:
request:
validator: @symfonyValidator