NelmioApiDocBundle/Parser/ValidationParser.php

354 lines
12 KiB
PHP
Raw Permalink Normal View History

<?php
/*
* This file is part of the NelmioApiDocBundle.
*
* (c) Nelmio <hello@nelm.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Parser;
Unified data types [actualType and subType] This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410. This PR aims to provide a uniform way of declaring data-types of parameters for parsers and handlers to follow. In turn, this would allow formatters to determine data-types in a cleaner and less volatile manner. (See use-case that can be improved with this PR: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103) This is possible by the addition two properties to each property item in `response`, and `parameters` fields in each API endpoint produced by the `ApiDocExtractor`: * `actualType` Contains a value from one of the `DataTypes` class constants. * `subType` Can contain either `null`, or any other `DataTypes` class constant. This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein `subType` would specify the type of the collection items. It is also relevant when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an identifier of the model (the FQCN or anything the parser would wish to specify) Examples: ```php array( 'id' => array( 'dataType' => 'integer', 'actualType' => DataTypes::INTEGER, 'subType' => null, ), 'profile' => array( 'dataType' => 'object (Profile)', 'actualType' => DataTypes::MODEL, 'subType' => 'Foo\Entity\Profile', 'children' => array( 'name' => array( 'dataType' => 'string', 'actualType' => DataTypes::STRING, 'subType' => null, ), 'birthDate' => array( 'dataType' => 'date', 'actualType' => DataTypes::DATE, 'subType' => null, ), ) ), 'languages' => array( 'dataType' => 'array of strings', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::STRING, ), 'roles' => array( 'dataType' => 'array of choices', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::ENUM, ), 'groups' => array( 'dataType' => 'array of objects (Group)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Group', ), 'profileRevisions' => array( 'dataType' => 'array of objects (Profile)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Profile', ), 'address' => array( 'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)', 'actualType' => DataTypes::MODEL, 'subType' => 'a_type_a_custom_JMS_serializer_handler_handles', ), ); ``` When a formatter omits the `dataType` property or leaves it blank, it is inferred within `ApiDocExtractor` before everything is passed to formatters.
2014-06-17 17:05:00 -07:00
use Nelmio\ApiDocBundle\DataTypes;
2024-10-01 15:54:04 +03:00
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\Type;
2013-11-06 15:25:10 +01:00
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
2015-11-30 19:19:17 +01:00
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
use Symfony\Component\Validator\MetadataFactoryInterface as LegacyMetadataFactoryInterface;
/**
* Uses the Symfony Validation component to extract information about API objects.
*/
class ValidationParser implements ParserInterface, PostParserInterface
{
/**
2024-10-01 15:54:04 +03:00
* @var LegacyMetadataFactoryInterface
*/
protected $factory;
2024-10-01 15:54:04 +03:00
protected $typeMap = [
'integer' => DataTypes::INTEGER,
'int' => DataTypes::INTEGER,
'scalar' => DataTypes::STRING,
'numeric' => DataTypes::INTEGER,
'boolean' => DataTypes::BOOLEAN,
'string' => DataTypes::STRING,
'float' => DataTypes::FLOAT,
'double' => DataTypes::FLOAT,
'long' => DataTypes::INTEGER,
'object' => DataTypes::MODEL,
'array' => DataTypes::COLLECTION,
Unified data types [actualType and subType] This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410. This PR aims to provide a uniform way of declaring data-types of parameters for parsers and handlers to follow. In turn, this would allow formatters to determine data-types in a cleaner and less volatile manner. (See use-case that can be improved with this PR: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103) This is possible by the addition two properties to each property item in `response`, and `parameters` fields in each API endpoint produced by the `ApiDocExtractor`: * `actualType` Contains a value from one of the `DataTypes` class constants. * `subType` Can contain either `null`, or any other `DataTypes` class constant. This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein `subType` would specify the type of the collection items. It is also relevant when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an identifier of the model (the FQCN or anything the parser would wish to specify) Examples: ```php array( 'id' => array( 'dataType' => 'integer', 'actualType' => DataTypes::INTEGER, 'subType' => null, ), 'profile' => array( 'dataType' => 'object (Profile)', 'actualType' => DataTypes::MODEL, 'subType' => 'Foo\Entity\Profile', 'children' => array( 'name' => array( 'dataType' => 'string', 'actualType' => DataTypes::STRING, 'subType' => null, ), 'birthDate' => array( 'dataType' => 'date', 'actualType' => DataTypes::DATE, 'subType' => null, ), ) ), 'languages' => array( 'dataType' => 'array of strings', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::STRING, ), 'roles' => array( 'dataType' => 'array of choices', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::ENUM, ), 'groups' => array( 'dataType' => 'array of objects (Group)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Group', ), 'profileRevisions' => array( 'dataType' => 'array of objects (Profile)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Profile', ), 'address' => array( 'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)', 'actualType' => DataTypes::MODEL, 'subType' => 'a_type_a_custom_JMS_serializer_handler_handles', ), ); ``` When a formatter omits the `dataType` property or leaves it blank, it is inferred within `ApiDocExtractor` before everything is passed to formatters.
2014-06-17 17:05:00 -07:00
'DateTime' => DataTypes::DATETIME,
2024-10-01 15:54:04 +03:00
];
Unified data types [actualType and subType] This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410. This PR aims to provide a uniform way of declaring data-types of parameters for parsers and handlers to follow. In turn, this would allow formatters to determine data-types in a cleaner and less volatile manner. (See use-case that can be improved with this PR: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103) This is possible by the addition two properties to each property item in `response`, and `parameters` fields in each API endpoint produced by the `ApiDocExtractor`: * `actualType` Contains a value from one of the `DataTypes` class constants. * `subType` Can contain either `null`, or any other `DataTypes` class constant. This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein `subType` would specify the type of the collection items. It is also relevant when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an identifier of the model (the FQCN or anything the parser would wish to specify) Examples: ```php array( 'id' => array( 'dataType' => 'integer', 'actualType' => DataTypes::INTEGER, 'subType' => null, ), 'profile' => array( 'dataType' => 'object (Profile)', 'actualType' => DataTypes::MODEL, 'subType' => 'Foo\Entity\Profile', 'children' => array( 'name' => array( 'dataType' => 'string', 'actualType' => DataTypes::STRING, 'subType' => null, ), 'birthDate' => array( 'dataType' => 'date', 'actualType' => DataTypes::DATE, 'subType' => null, ), ) ), 'languages' => array( 'dataType' => 'array of strings', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::STRING, ), 'roles' => array( 'dataType' => 'array of choices', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::ENUM, ), 'groups' => array( 'dataType' => 'array of objects (Group)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Group', ), 'profileRevisions' => array( 'dataType' => 'array of objects (Profile)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Profile', ), 'address' => array( 'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)', 'actualType' => DataTypes::MODEL, 'subType' => 'a_type_a_custom_JMS_serializer_handler_handles', ), ); ``` When a formatter omits the `dataType` property or leaves it blank, it is inferred within `ApiDocExtractor` before everything is passed to formatters.
2014-06-17 17:05:00 -07:00
/**
* Requires a validation MetadataFactory.
*
2015-11-30 19:19:17 +01:00
* @param MetadataFactoryInterface|LegacyMetadataFactoryInterface $factory
*/
2015-11-30 19:19:17 +01:00
public function __construct($factory)
{
2015-11-30 19:19:17 +01:00
if (!($factory instanceof MetadataFactoryInterface) && !($factory instanceof LegacyMetadataFactoryInterface)) {
throw new \InvalidArgumentException('Argument 1 of %s constructor must be either an instance of Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface or Symfony\Component\Validator\MetadataFactoryInterface.');
}
$this->factory = $factory;
}
public function supports(array $input)
{
$className = $input['class'];
return $this->factory->hasMetadataFor($className);
}
public function parse(array $input)
{
$className = $input['class'];
2013-12-11 01:59:59 +01:00
2024-10-01 15:54:04 +03:00
$groups = [];
if (isset($input['groups']) && $input['groups']) {
$groups = $input['groups'];
2019-04-24 19:08:53 +03:00
}
2024-10-01 15:54:04 +03:00
$parsed = $this->doParse($className, [], $groups);
if (!isset($input['name']) || empty($input['name'])) {
return $parsed;
}
2015-10-22 14:42:59 +02:00
if ($className && class_exists($className)) {
$parts = explode('\\', $className);
$dataType = sprintf('object (%s)', end($parts));
} else {
$dataType = sprintf('object (%s)', $className);
}
2024-10-01 15:54:04 +03:00
return [
$input['name'] => [
'dataType' => $dataType,
'actualType' => DataTypes::MODEL,
'class' => $className,
'subType' => $dataType,
'required' => null,
'readonly' => null,
'children' => $parsed,
'default' => null,
2024-10-01 15:54:04 +03:00
],
];
2013-11-07 14:11:09 +01:00
}
2013-11-07 14:11:09 +01:00
/**
* Recursively parse constraints.
*
* @return array
*/
2024-10-01 15:54:04 +03:00
protected function doParse($className, array $visited, array $groups = [])
2013-11-07 14:11:09 +01:00
{
2024-10-01 15:54:04 +03:00
$params = [];
$classdata = $this->factory->getMetadataFor($className);
$properties = $classdata->getConstrainedProperties();
$refl = $classdata->getReflectionClass();
$defaults = $refl->getDefaultProperties();
2013-11-14 10:28:42 +01:00
foreach ($properties as $property) {
2024-10-01 15:54:04 +03:00
$vparams = [];
2024-10-01 15:54:04 +03:00
$vparams['default'] = $defaults[$property] ?? null;
$pds = $classdata->getPropertyMetadata($property);
2013-11-14 10:28:42 +01:00
foreach ($pds as $propdata) {
$constraints = $propdata->getConstraints();
2013-11-14 10:28:42 +01:00
foreach ($constraints as $constraint) {
2019-04-24 19:08:53 +03:00
$vparams = $this->parseConstraint($constraint, $vparams, $className, $visited, $groups);
}
}
2013-11-14 10:28:42 +01:00
if (isset($vparams['format'])) {
2024-10-01 15:54:04 +03:00
$vparams['format'] = implode(', ', array_unique($vparams['format']));
}
2024-10-01 15:54:04 +03:00
foreach (['dataType', 'readonly', 'required', 'subType'] as $reqprop) {
2013-11-14 10:28:42 +01:00
if (!isset($vparams[$reqprop])) {
$vparams[$reqprop] = null;
}
}
2013-11-07 14:11:09 +01:00
// check for nested classes with All constraint
if (isset($vparams['class']) && !in_array($vparams['class'], $visited) && null !== $this->factory->getMetadataFor($vparams['class'])) {
$visited[] = $vparams['class'];
2019-04-24 19:08:53 +03:00
$vparams['children'] = $this->doParse($vparams['class'], $visited, $groups);
2013-11-07 14:11:09 +01:00
}
2024-10-01 15:54:04 +03:00
$vparams['actualType'] = $vparams['actualType'] ?? DataTypes::STRING;
2014-08-04 09:42:48 -07:00
$params[$property] = $vparams;
}
return $params;
}
public function postParse(array $input, array $parameters)
{
2019-04-24 19:08:53 +03:00
$groups = [];
2024-10-01 15:54:04 +03:00
if (isset($input['groups']) && $input['groups']) {
$groups = $input['groups'];
2019-04-24 19:08:53 +03:00
}
2013-11-14 10:28:42 +01:00
foreach ($parameters as $param => $data) {
if (isset($data['class']) && isset($data['children'])) {
2024-10-01 15:54:04 +03:00
$input = ['class' => $data['class'], 'groups' => $groups];
$parameters[$param]['children'] = array_merge(
$parameters[$param]['children'], $this->postParse($input, $parameters[$param]['children'])
);
$parameters[$param]['children'] = array_merge(
$parameters[$param]['children'], $this->parse($input, $parameters[$param]['children'])
);
}
}
return $parameters;
}
/**
* Create a valid documentation parameter based on an individual validation Constraint.
* Currently supports:
* - NotBlank/NotNull
* - Type
* - Email
* - Url
* - Ip
* - Length (min and max)
* - Choice (single and multiple, min and max)
* - Regex (match and non-match)
*
2024-10-01 15:54:04 +03:00
* @param Constraint $constraint The constraint metadata object.
* @param array $vparams The existing validation parameters.
* @param array $groups Validation groups.
*
* @return mixed The parsed list of validation parameters.
*/
2019-04-24 19:08:53 +03:00
protected function parseConstraint(
Constraint $constraint,
$vparams,
$className,
2024-10-01 15:54:04 +03:00
&$visited = [],
array $groups = [],
) {
$class = substr($constraint::class, strlen('Symfony\\Component\\Validator\\Constraints\\'));
Unified data types [actualType and subType] This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410. This PR aims to provide a uniform way of declaring data-types of parameters for parsers and handlers to follow. In turn, this would allow formatters to determine data-types in a cleaner and less volatile manner. (See use-case that can be improved with this PR: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103) This is possible by the addition two properties to each property item in `response`, and `parameters` fields in each API endpoint produced by the `ApiDocExtractor`: * `actualType` Contains a value from one of the `DataTypes` class constants. * `subType` Can contain either `null`, or any other `DataTypes` class constant. This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein `subType` would specify the type of the collection items. It is also relevant when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an identifier of the model (the FQCN or anything the parser would wish to specify) Examples: ```php array( 'id' => array( 'dataType' => 'integer', 'actualType' => DataTypes::INTEGER, 'subType' => null, ), 'profile' => array( 'dataType' => 'object (Profile)', 'actualType' => DataTypes::MODEL, 'subType' => 'Foo\Entity\Profile', 'children' => array( 'name' => array( 'dataType' => 'string', 'actualType' => DataTypes::STRING, 'subType' => null, ), 'birthDate' => array( 'dataType' => 'date', 'actualType' => DataTypes::DATE, 'subType' => null, ), ) ), 'languages' => array( 'dataType' => 'array of strings', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::STRING, ), 'roles' => array( 'dataType' => 'array of choices', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::ENUM, ), 'groups' => array( 'dataType' => 'array of objects (Group)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Group', ), 'profileRevisions' => array( 'dataType' => 'array of objects (Profile)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Profile', ), 'address' => array( 'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)', 'actualType' => DataTypes::MODEL, 'subType' => 'a_type_a_custom_JMS_serializer_handler_handles', ), ); ``` When a formatter omits the `dataType` property or leaves it blank, it is inferred within `ApiDocExtractor` before everything is passed to formatters.
2014-06-17 17:05:00 -07:00
$vparams['actualType'] = DataTypes::STRING;
$vparams['subType'] = null;
2019-04-24 19:08:53 +03:00
$vparams['groups'] = $constraint->groups;
if ($groups) {
$containGroup = false;
foreach ($groups as $group) {
if (in_array($group, $vparams['groups'])) {
$containGroup = true;
}
}
if (!$containGroup) {
return $vparams;
}
}
Unified data types [actualType and subType] This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410. This PR aims to provide a uniform way of declaring data-types of parameters for parsers and handlers to follow. In turn, this would allow formatters to determine data-types in a cleaner and less volatile manner. (See use-case that can be improved with this PR: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103) This is possible by the addition two properties to each property item in `response`, and `parameters` fields in each API endpoint produced by the `ApiDocExtractor`: * `actualType` Contains a value from one of the `DataTypes` class constants. * `subType` Can contain either `null`, or any other `DataTypes` class constant. This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein `subType` would specify the type of the collection items. It is also relevant when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an identifier of the model (the FQCN or anything the parser would wish to specify) Examples: ```php array( 'id' => array( 'dataType' => 'integer', 'actualType' => DataTypes::INTEGER, 'subType' => null, ), 'profile' => array( 'dataType' => 'object (Profile)', 'actualType' => DataTypes::MODEL, 'subType' => 'Foo\Entity\Profile', 'children' => array( 'name' => array( 'dataType' => 'string', 'actualType' => DataTypes::STRING, 'subType' => null, ), 'birthDate' => array( 'dataType' => 'date', 'actualType' => DataTypes::DATE, 'subType' => null, ), ) ), 'languages' => array( 'dataType' => 'array of strings', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::STRING, ), 'roles' => array( 'dataType' => 'array of choices', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::ENUM, ), 'groups' => array( 'dataType' => 'array of objects (Group)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Group', ), 'profileRevisions' => array( 'dataType' => 'array of objects (Profile)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Profile', ), 'address' => array( 'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)', 'actualType' => DataTypes::MODEL, 'subType' => 'a_type_a_custom_JMS_serializer_handler_handles', ), ); ``` When a formatter omits the `dataType` property or leaves it blank, it is inferred within `ApiDocExtractor` before everything is passed to formatters.
2014-06-17 17:05:00 -07:00
2013-11-14 10:28:42 +01:00
switch ($class) {
case 'NotBlank':
$vparams['format'][] = '{not blank}';
2024-10-01 15:54:04 +03:00
// no break
case 'NotNull':
$vparams['required'] = true;
break;
case 'Type':
Unified data types [actualType and subType] This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410. This PR aims to provide a uniform way of declaring data-types of parameters for parsers and handlers to follow. In turn, this would allow formatters to determine data-types in a cleaner and less volatile manner. (See use-case that can be improved with this PR: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103) This is possible by the addition two properties to each property item in `response`, and `parameters` fields in each API endpoint produced by the `ApiDocExtractor`: * `actualType` Contains a value from one of the `DataTypes` class constants. * `subType` Can contain either `null`, or any other `DataTypes` class constant. This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein `subType` would specify the type of the collection items. It is also relevant when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an identifier of the model (the FQCN or anything the parser would wish to specify) Examples: ```php array( 'id' => array( 'dataType' => 'integer', 'actualType' => DataTypes::INTEGER, 'subType' => null, ), 'profile' => array( 'dataType' => 'object (Profile)', 'actualType' => DataTypes::MODEL, 'subType' => 'Foo\Entity\Profile', 'children' => array( 'name' => array( 'dataType' => 'string', 'actualType' => DataTypes::STRING, 'subType' => null, ), 'birthDate' => array( 'dataType' => 'date', 'actualType' => DataTypes::DATE, 'subType' => null, ), ) ), 'languages' => array( 'dataType' => 'array of strings', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::STRING, ), 'roles' => array( 'dataType' => 'array of choices', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::ENUM, ), 'groups' => array( 'dataType' => 'array of objects (Group)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Group', ), 'profileRevisions' => array( 'dataType' => 'array of objects (Profile)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Profile', ), 'address' => array( 'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)', 'actualType' => DataTypes::MODEL, 'subType' => 'a_type_a_custom_JMS_serializer_handler_handles', ), ); ``` When a formatter omits the `dataType` property or leaves it blank, it is inferred within `ApiDocExtractor` before everything is passed to formatters.
2014-06-17 17:05:00 -07:00
if (isset($this->typeMap[$constraint->type])) {
$vparams['actualType'] = $this->typeMap[$constraint->type];
}
$vparams['dataType'] = $constraint->type;
break;
case 'Email':
$vparams['format'][] = '{email address}';
break;
case 'Url':
$vparams['format'][] = '{url}';
break;
case 'Ip':
$vparams['format'][] = '{ip address}';
break;
case 'Date':
$vparams['format'][] = '{Date YYYY-MM-DD}';
Unified data types [actualType and subType] This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410. This PR aims to provide a uniform way of declaring data-types of parameters for parsers and handlers to follow. In turn, this would allow formatters to determine data-types in a cleaner and less volatile manner. (See use-case that can be improved with this PR: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103) This is possible by the addition two properties to each property item in `response`, and `parameters` fields in each API endpoint produced by the `ApiDocExtractor`: * `actualType` Contains a value from one of the `DataTypes` class constants. * `subType` Can contain either `null`, or any other `DataTypes` class constant. This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein `subType` would specify the type of the collection items. It is also relevant when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an identifier of the model (the FQCN or anything the parser would wish to specify) Examples: ```php array( 'id' => array( 'dataType' => 'integer', 'actualType' => DataTypes::INTEGER, 'subType' => null, ), 'profile' => array( 'dataType' => 'object (Profile)', 'actualType' => DataTypes::MODEL, 'subType' => 'Foo\Entity\Profile', 'children' => array( 'name' => array( 'dataType' => 'string', 'actualType' => DataTypes::STRING, 'subType' => null, ), 'birthDate' => array( 'dataType' => 'date', 'actualType' => DataTypes::DATE, 'subType' => null, ), ) ), 'languages' => array( 'dataType' => 'array of strings', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::STRING, ), 'roles' => array( 'dataType' => 'array of choices', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::ENUM, ), 'groups' => array( 'dataType' => 'array of objects (Group)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Group', ), 'profileRevisions' => array( 'dataType' => 'array of objects (Profile)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Profile', ), 'address' => array( 'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)', 'actualType' => DataTypes::MODEL, 'subType' => 'a_type_a_custom_JMS_serializer_handler_handles', ), ); ``` When a formatter omits the `dataType` property or leaves it blank, it is inferred within `ApiDocExtractor` before everything is passed to formatters.
2014-06-17 17:05:00 -07:00
$vparams['actualType'] = DataTypes::DATE;
break;
case 'DateTime':
$vparams['format'][] = '{DateTime YYYY-MM-DD HH:MM:SS}';
Unified data types [actualType and subType] This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410. This PR aims to provide a uniform way of declaring data-types of parameters for parsers and handlers to follow. In turn, this would allow formatters to determine data-types in a cleaner and less volatile manner. (See use-case that can be improved with this PR: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103) This is possible by the addition two properties to each property item in `response`, and `parameters` fields in each API endpoint produced by the `ApiDocExtractor`: * `actualType` Contains a value from one of the `DataTypes` class constants. * `subType` Can contain either `null`, or any other `DataTypes` class constant. This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein `subType` would specify the type of the collection items. It is also relevant when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an identifier of the model (the FQCN or anything the parser would wish to specify) Examples: ```php array( 'id' => array( 'dataType' => 'integer', 'actualType' => DataTypes::INTEGER, 'subType' => null, ), 'profile' => array( 'dataType' => 'object (Profile)', 'actualType' => DataTypes::MODEL, 'subType' => 'Foo\Entity\Profile', 'children' => array( 'name' => array( 'dataType' => 'string', 'actualType' => DataTypes::STRING, 'subType' => null, ), 'birthDate' => array( 'dataType' => 'date', 'actualType' => DataTypes::DATE, 'subType' => null, ), ) ), 'languages' => array( 'dataType' => 'array of strings', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::STRING, ), 'roles' => array( 'dataType' => 'array of choices', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::ENUM, ), 'groups' => array( 'dataType' => 'array of objects (Group)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Group', ), 'profileRevisions' => array( 'dataType' => 'array of objects (Profile)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Profile', ), 'address' => array( 'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)', 'actualType' => DataTypes::MODEL, 'subType' => 'a_type_a_custom_JMS_serializer_handler_handles', ), ); ``` When a formatter omits the `dataType` property or leaves it blank, it is inferred within `ApiDocExtractor` before everything is passed to formatters.
2014-06-17 17:05:00 -07:00
$vparams['actualType'] = DataTypes::DATETIME;
break;
case 'Time':
$vparams['format'][] = '{Time HH:MM:SS}';
Unified data types [actualType and subType] This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410. This PR aims to provide a uniform way of declaring data-types of parameters for parsers and handlers to follow. In turn, this would allow formatters to determine data-types in a cleaner and less volatile manner. (See use-case that can be improved with this PR: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103) This is possible by the addition two properties to each property item in `response`, and `parameters` fields in each API endpoint produced by the `ApiDocExtractor`: * `actualType` Contains a value from one of the `DataTypes` class constants. * `subType` Can contain either `null`, or any other `DataTypes` class constant. This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein `subType` would specify the type of the collection items. It is also relevant when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an identifier of the model (the FQCN or anything the parser would wish to specify) Examples: ```php array( 'id' => array( 'dataType' => 'integer', 'actualType' => DataTypes::INTEGER, 'subType' => null, ), 'profile' => array( 'dataType' => 'object (Profile)', 'actualType' => DataTypes::MODEL, 'subType' => 'Foo\Entity\Profile', 'children' => array( 'name' => array( 'dataType' => 'string', 'actualType' => DataTypes::STRING, 'subType' => null, ), 'birthDate' => array( 'dataType' => 'date', 'actualType' => DataTypes::DATE, 'subType' => null, ), ) ), 'languages' => array( 'dataType' => 'array of strings', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::STRING, ), 'roles' => array( 'dataType' => 'array of choices', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::ENUM, ), 'groups' => array( 'dataType' => 'array of objects (Group)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Group', ), 'profileRevisions' => array( 'dataType' => 'array of objects (Profile)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Profile', ), 'address' => array( 'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)', 'actualType' => DataTypes::MODEL, 'subType' => 'a_type_a_custom_JMS_serializer_handler_handles', ), ); ``` When a formatter omits the `dataType` property or leaves it blank, it is inferred within `ApiDocExtractor` before everything is passed to formatters.
2014-06-17 17:05:00 -07:00
$vparams['actualType'] = DataTypes::TIME;
break;
case 'Range':
2024-10-01 15:54:04 +03:00
$messages = [];
if (isset($constraint->min)) {
$messages[] = ">={$constraint->min}";
}
if (isset($constraint->max)) {
$messages[] = "<={$constraint->max}";
}
2024-10-01 15:54:04 +03:00
$vparams['format'][] = '{range: {' . implode(', ', $messages) . '}}';
break;
case 'Length':
2024-10-01 15:54:04 +03:00
$messages = [];
2013-11-14 10:28:42 +01:00
if (isset($constraint->min)) {
$messages[] = "min: {$constraint->min}";
}
2013-11-14 10:28:42 +01:00
if (isset($constraint->max)) {
$messages[] = "max: {$constraint->max}";
}
2024-10-01 15:54:04 +03:00
$vparams['format'][] = '{length: {' . implode(', ', $messages) . '}}';
break;
case 'Choice':
2013-11-06 15:25:10 +01:00
$choices = $this->getChoices($constraint, $className);
sort($choices);
2024-10-01 15:54:04 +03:00
$format = '[' . implode('|', $choices) . ']';
2013-11-14 10:28:42 +01:00
if ($constraint->multiple) {
Unified data types [actualType and subType] This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410. This PR aims to provide a uniform way of declaring data-types of parameters for parsers and handlers to follow. In turn, this would allow formatters to determine data-types in a cleaner and less volatile manner. (See use-case that can be improved with this PR: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103) This is possible by the addition two properties to each property item in `response`, and `parameters` fields in each API endpoint produced by the `ApiDocExtractor`: * `actualType` Contains a value from one of the `DataTypes` class constants. * `subType` Can contain either `null`, or any other `DataTypes` class constant. This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein `subType` would specify the type of the collection items. It is also relevant when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an identifier of the model (the FQCN or anything the parser would wish to specify) Examples: ```php array( 'id' => array( 'dataType' => 'integer', 'actualType' => DataTypes::INTEGER, 'subType' => null, ), 'profile' => array( 'dataType' => 'object (Profile)', 'actualType' => DataTypes::MODEL, 'subType' => 'Foo\Entity\Profile', 'children' => array( 'name' => array( 'dataType' => 'string', 'actualType' => DataTypes::STRING, 'subType' => null, ), 'birthDate' => array( 'dataType' => 'date', 'actualType' => DataTypes::DATE, 'subType' => null, ), ) ), 'languages' => array( 'dataType' => 'array of strings', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::STRING, ), 'roles' => array( 'dataType' => 'array of choices', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::ENUM, ), 'groups' => array( 'dataType' => 'array of objects (Group)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Group', ), 'profileRevisions' => array( 'dataType' => 'array of objects (Profile)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Profile', ), 'address' => array( 'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)', 'actualType' => DataTypes::MODEL, 'subType' => 'a_type_a_custom_JMS_serializer_handler_handles', ), ); ``` When a formatter omits the `dataType` property or leaves it blank, it is inferred within `ApiDocExtractor` before everything is passed to formatters.
2014-06-17 17:05:00 -07:00
$vparams['actualType'] = DataTypes::COLLECTION;
$vparams['subType'] = DataTypes::ENUM;
2024-10-01 15:54:04 +03:00
$messages = [];
2013-11-14 10:28:42 +01:00
if (isset($constraint->min)) {
$messages[] = "min: {$constraint->min} ";
}
2013-11-14 10:28:42 +01:00
if (isset($constraint->max)) {
$messages[] = "max: {$constraint->max} ";
}
2024-10-01 15:54:04 +03:00
$vparams['format'][] = '{' . implode('', $messages) . 'choice of ' . $format . '}';
} else {
Unified data types [actualType and subType] This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410. This PR aims to provide a uniform way of declaring data-types of parameters for parsers and handlers to follow. In turn, this would allow formatters to determine data-types in a cleaner and less volatile manner. (See use-case that can be improved with this PR: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103) This is possible by the addition two properties to each property item in `response`, and `parameters` fields in each API endpoint produced by the `ApiDocExtractor`: * `actualType` Contains a value from one of the `DataTypes` class constants. * `subType` Can contain either `null`, or any other `DataTypes` class constant. This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein `subType` would specify the type of the collection items. It is also relevant when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an identifier of the model (the FQCN or anything the parser would wish to specify) Examples: ```php array( 'id' => array( 'dataType' => 'integer', 'actualType' => DataTypes::INTEGER, 'subType' => null, ), 'profile' => array( 'dataType' => 'object (Profile)', 'actualType' => DataTypes::MODEL, 'subType' => 'Foo\Entity\Profile', 'children' => array( 'name' => array( 'dataType' => 'string', 'actualType' => DataTypes::STRING, 'subType' => null, ), 'birthDate' => array( 'dataType' => 'date', 'actualType' => DataTypes::DATE, 'subType' => null, ), ) ), 'languages' => array( 'dataType' => 'array of strings', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::STRING, ), 'roles' => array( 'dataType' => 'array of choices', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::ENUM, ), 'groups' => array( 'dataType' => 'array of objects (Group)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Group', ), 'profileRevisions' => array( 'dataType' => 'array of objects (Profile)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Profile', ), 'address' => array( 'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)', 'actualType' => DataTypes::MODEL, 'subType' => 'a_type_a_custom_JMS_serializer_handler_handles', ), ); ``` When a formatter omits the `dataType` property or leaves it blank, it is inferred within `ApiDocExtractor` before everything is passed to formatters.
2014-06-17 17:05:00 -07:00
$vparams['actualType'] = DataTypes::ENUM;
$vparams['format'][] = $format;
}
break;
case 'Regex':
2013-11-14 10:28:42 +01:00
if ($constraint->match) {
$vparams['format'][] = '{match: ' . $constraint->pattern . '}';
} else {
$vparams['format'][] = '{not match: ' . $constraint->pattern . '}';
}
break;
2013-11-07 14:11:09 +01:00
case 'All':
foreach ($constraint->constraints as $childConstraint) {
if ($childConstraint instanceof Type) {
$nestedType = $childConstraint->type;
2024-10-01 15:54:04 +03:00
$exp = explode('\\', $nestedType);
if (!$nestedType || !class_exists($nestedType)) {
2024-10-01 15:54:04 +03:00
$nestedType = substr($className, 0, strrpos($className, '\\') + 1) . $nestedType;
2013-11-07 14:11:09 +01:00
if (!class_exists($nestedType)) {
continue;
}
}
2024-10-01 15:54:04 +03:00
$vparams['dataType'] = sprintf('array of objects (%s)', end($exp));
Unified data types [actualType and subType] This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410. This PR aims to provide a uniform way of declaring data-types of parameters for parsers and handlers to follow. In turn, this would allow formatters to determine data-types in a cleaner and less volatile manner. (See use-case that can be improved with this PR: https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103) This is possible by the addition two properties to each property item in `response`, and `parameters` fields in each API endpoint produced by the `ApiDocExtractor`: * `actualType` Contains a value from one of the `DataTypes` class constants. * `subType` Can contain either `null`, or any other `DataTypes` class constant. This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein `subType` would specify the type of the collection items. It is also relevant when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an identifier of the model (the FQCN or anything the parser would wish to specify) Examples: ```php array( 'id' => array( 'dataType' => 'integer', 'actualType' => DataTypes::INTEGER, 'subType' => null, ), 'profile' => array( 'dataType' => 'object (Profile)', 'actualType' => DataTypes::MODEL, 'subType' => 'Foo\Entity\Profile', 'children' => array( 'name' => array( 'dataType' => 'string', 'actualType' => DataTypes::STRING, 'subType' => null, ), 'birthDate' => array( 'dataType' => 'date', 'actualType' => DataTypes::DATE, 'subType' => null, ), ) ), 'languages' => array( 'dataType' => 'array of strings', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::STRING, ), 'roles' => array( 'dataType' => 'array of choices', 'actualType' => DataTypes::COLLECTION, 'subType' => DataTypes::ENUM, ), 'groups' => array( 'dataType' => 'array of objects (Group)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Group', ), 'profileRevisions' => array( 'dataType' => 'array of objects (Profile)', 'actualType' => DataTypes::COLLECTION, 'subType' => 'Foo\Entity\Profile', ), 'address' => array( 'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)', 'actualType' => DataTypes::MODEL, 'subType' => 'a_type_a_custom_JMS_serializer_handler_handles', ), ); ``` When a formatter omits the `dataType` property or leaves it blank, it is inferred within `ApiDocExtractor` before everything is passed to formatters.
2014-06-17 17:05:00 -07:00
$vparams['actualType'] = DataTypes::COLLECTION;
2024-10-01 15:54:04 +03:00
$vparams['subType'] = $nestedType;
$vparams['class'] = $nestedType;
2013-11-07 14:11:09 +01:00
if (!in_array($nestedType, $visited)) {
$visited[] = $nestedType;
$vparams['children'] = $this->doParse($nestedType, $visited);
}
}
}
break;
}
return $vparams;
}
2013-11-06 15:25:10 +01:00
/**
* Return Choice constraint choices.
*
* @return array
2024-10-01 15:54:04 +03:00
*
* @throws ConstraintDefinitionException
2013-11-06 15:25:10 +01:00
*/
2013-11-14 10:28:42 +01:00
protected function getChoices(Constraint $constraint, $className)
2013-11-06 15:25:10 +01:00
{
if ($constraint->callback) {
2024-10-01 15:54:04 +03:00
if (is_callable([$className, $constraint->callback])) {
$choices = call_user_func([$className, $constraint->callback]);
2013-11-06 15:25:10 +01:00
} elseif (is_callable($constraint->callback)) {
$choices = call_user_func($constraint->callback);
} else {
throw new ConstraintDefinitionException('The Choice constraint expects a valid callback');
}
} else {
$choices = $constraint->choices;
}
return $choices;
}
}