Contributte Apitte
# Setup
api:
plugins:
Apitte\Core\DI\Plugin\CoreMappingPlugin:
2
3
Ensure you have also decorator plugin registered, mapping is implemented by decorators.
# RequestParameters
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
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Options
@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
# Data types
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
# Overriding datatypes
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
2
3
4
5
6
7
8
9
# Custom datatypes
You can also add your custom data types.
api:
plugins:
Apitte\Core\DI\Plugin\CoreMappingPlugin:
types:
email: MyEmailTypeMapper
2
3
4
5
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.');
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# RequestBody
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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
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();
}
2
3
4
5
6
7
8
9
10
# Validators
Validate request entities with validator.
By default no validator is used.
# BasicValidator
Supports @required annotation - check if attribute is not empty
api:
plugins:
Apitte\Core\DI\Plugin\CoreMappingPlugin:
request:
validator: Apitte\Core\Mapping\Validator\BasicValidator
2
3
4
5
# SymfonyValidator
Requires a doctrine annotation reader, you may use nettrine/annotations (opens new window)
Also install symfony/validator (opens new window)
extensions:
annotations: Nettrine\Annotations\DI\AnnotationsExtension
api:
plugins:
Apitte\Core\DI\Plugin\CoreMappingPlugin:
request:
validator: Apitte\Core\Mapping\Validator\SymfonyValidator()
2
3
4
5
6
7
8
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;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
You can override ConstraintValidatorFactory
on SymfonyValidator
. If you want to use custom validation contstraints (opens new window) with support of Nette DI,
you should also install contributte/validator (opens new window).
If you want to use translated constraint messages, you can use contributte/translation (opens new window).
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
2
3
4
5
6
7
8
9
10
11
12