Improve support for JMS dictionaries (associative arrays) (#1301)

* improve support for JMS dictionaries (associative arrays)

Doc: https://swagger.io/docs/specification/data-models/dictionaries/

* use schema merge instead of reflection
This commit is contained in:
Asmir Mustafic 2018-04-21 16:57:37 +02:00 committed by Guilhem N
parent 1680ba3481
commit c5b1d538f8
3 changed files with 102 additions and 24 deletions

View File

@ -92,33 +92,33 @@ class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareIn
continue; continue;
} }
if ($type = $this->getNestedTypeInArray($item)) { if ($nestedType = $this->getNestedTypeInArray($item)) {
$property->setType('array'); list($type, $isHash) = $nestedType;
$property = $property->getItems(); if ($isHash) {
$property->setType('object');
$typeDef = $this->findPropertyType($type, $groups);
// in the case of a virtual property, set it as free object type
$property->merge(['additionalProperties' => $typeDef ?: []]);
continue;
} else {
$property->setType('array');
$property = $property->getItems();
}
} else { } else {
$type = $item->type['name']; $type = $item->type['name'];
} }
if (in_array($type, ['boolean', 'string', 'array'])) { $typeDef = $this->findPropertyType($type, $groups);
$property->setType($type);
} elseif (in_array($type, ['int', 'integer'])) {
$property->setType('integer');
} elseif (in_array($type, ['double', 'float'])) {
$property->setType('number');
$property->setFormat($type);
} elseif (in_array($type, ['DateTime', 'DateTimeImmutable'])) {
$property->setType('string');
$property->setFormat('date-time');
} else {
// we can use property type also for custom handlers, then we don't have here real class name
if (!class_exists($type)) {
continue;
}
$property->setRef( // virtual property
$this->modelRegistry->register(new Model(new Type(Type::BUILTIN_TYPE_OBJECT, false, $type), $groups)) if (!$typeDef) {
); continue;
} }
$this->registerPropertyType($typeDef, $property);
} }
} }
@ -139,20 +139,74 @@ class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareIn
return false; return false;
} }
/**
* @param string $type
* @param array|null $groups
*
* @return array|null
*/
private function findPropertyType(string $type, array $groups = null)
{
$typeDef = [];
if (in_array($type, ['boolean', 'string', 'array'])) {
$typeDef['type'] = $type;
} elseif (in_array($type, ['int', 'integer'])) {
$typeDef['type'] = 'integer';
} elseif (in_array($type, ['double', 'float'])) {
$typeDef['type'] = 'number';
$typeDef['format'] = $type;
} elseif (in_array($type, ['DateTime', 'DateTimeImmutable'])) {
$typeDef['type'] = 'string';
$typeDef['format'] = 'date-time';
} else {
// we can use property type also for custom handlers, then we don't have here real class name
if (!class_exists($type)) {
return null;
}
$typeDef['$ref'] = $this->modelRegistry->register(
new Model(new Type(Type::BUILTIN_TYPE_OBJECT, false, $type), $groups)
);
}
return $typeDef;
}
private function registerPropertyType(array $typeDef, $property)
{
if (isset($typeDef['$ref'])) {
$property->setRef($typeDef['$ref']);
} else {
if (isset($typeDef['type'])) {
$property->setType($typeDef['type']);
}
if (isset($typeDef['format'])) {
$property->setFormat($typeDef['format']);
}
}
}
/**
* @param PropertyMetadata $item
*
* @return array|null
*/
private function getNestedTypeInArray(PropertyMetadata $item) private function getNestedTypeInArray(PropertyMetadata $item)
{ {
if ('array' !== $item->type['name'] && 'ArrayCollection' !== $item->type['name']) { if ('array' !== $item->type['name'] && 'ArrayCollection' !== $item->type['name']) {
return; return null;
} }
// array<string, MyNamespaceMyObject> // array<string, MyNamespaceMyObject>
if (isset($item->type['params'][1]['name'])) { if (isset($item->type['params'][1]['name'])) {
return $item->type['params'][1]['name']; return [$item->type['params'][1]['name'], true];
} }
// array<MyNamespaceMyObject> // array<MyNamespaceMyObject>
if (isset($item->type['params'][0]['name'])) { if (isset($item->type['params'][0]['name'])) {
return $item->type['params'][0]['name']; return [$item->type['params'][0]['name'], false];
} }
return null;
} }
} }

View File

@ -80,6 +80,17 @@ class JMSUser
*/ */
private $friends; private $friends;
/**
* @Serializer\Type("array<string, Nelmio\ApiDocBundle\Tests\Functional\Entity\User>")
* @Serializer\Expose
*/
private $indexedFriends;
/**
* @Serializer\Type("array<string, DateTime>")
* @Serializer\Expose
*/
private $favoriteDates;
/** /**
* @Serializer\Type("integer") * @Serializer\Type("integer")
* @Serializer\Expose * @Serializer\Expose

View File

@ -54,6 +54,19 @@ class JMSFunctionalTest extends WebTestCase
'$ref' => '#/definitions/User', '$ref' => '#/definitions/User',
], ],
], ],
'indexed_friends' => [
'type' => 'object',
'additionalProperties' => [
'$ref' => '#/definitions/User',
],
],
'favorite_dates' => [
'type' => 'object',
'additionalProperties' => [
'type' => 'string',
'format' => 'date-time',
],
],
'best_friend' => [ 'best_friend' => [
'$ref' => '#/definitions/User', '$ref' => '#/definitions/User',
], ],