NelmioApiDocBundle/Model/ModelRegistry.php
Asmir Mustafic ab005c4129 Implement alternative naming system via configuration (#1312)
* implement alternative naming system via configuration

* use strict comparison

* test di configs

* rever

* test naming aliases are applied

* set "default" as default area

* test names are passed to generators

* cs formatting

* added extra check for built-int types

* cs

* added documentation about alternative names

* allow to create the same alias in two different areas

* document and test better aliasing strategy

* specify that the last matching rule is used

* Make last matching rule wins

* Fix documentation
2018-06-10 09:56:38 +02:00

164 lines
4.5 KiB
PHP

<?php
/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Model;
use EXSyst\Component\Swagger\Schema;
use EXSyst\Component\Swagger\Swagger;
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface;
use Nelmio\ApiDocBundle\ModelDescriber\ModelDescriberInterface;
use Symfony\Component\PropertyInfo\Type;
final class ModelRegistry
{
private $alternativeNames = [];
private $unregistered = [];
private $models = [];
private $names = [];
private $modelDescribers = [];
private $api;
/**
* @param ModelDescriberInterface[]|iterable $modelDescribers
*
* @internal
*/
public function __construct($modelDescribers, Swagger $api, array $alternativeNames = [])
{
$this->modelDescribers = $modelDescribers;
$this->api = $api;
$this->alternativeNames = array_reverse($alternativeNames); // last rule wins
}
public function register(Model $model): string
{
$hash = $model->getHash();
if (isset($this->names[$hash])) {
return '#/definitions/'.$this->names[$hash];
}
$this->names[$hash] = $name = $this->generateModelName($model);
$this->models[$hash] = $model;
$this->unregistered[] = $hash;
// Reserve the name
$this->api->getDefinitions()->get($name);
return '#/definitions/'.$name;
}
/**
* @internal
*/
public function registerDefinitions()
{
while (count($this->unregistered)) {
$tmp = [];
foreach ($this->unregistered as $hash) {
$tmp[$this->names[$hash]] = $this->models[$hash];
}
$this->unregistered = [];
foreach ($tmp as $name => $model) {
$schema = null;
foreach ($this->modelDescribers as $modelDescriber) {
if ($modelDescriber instanceof ModelRegistryAwareInterface) {
$modelDescriber->setModelRegistry($this);
}
if ($modelDescriber->supports($model)) {
$schema = new Schema();
$modelDescriber->describe($model, $schema);
break;
}
}
if (null === $schema) {
throw new \LogicException(sprintf('Schema of type "%s" can\'t be generated, no describer supports it.', $this->typeToString($model->getType())));
}
$this->api->getDefinitions()->set($name, $schema);
}
}
}
private function generateModelName(Model $model): string
{
$definitions = $this->api->getDefinitions();
$name = $base = $this->getAlternativeName($model) ?? $this->getTypeShortName($model->getType());
$i = 1;
while ($definitions->has($name)) {
++$i;
$name = $base.$i;
}
return $name;
}
/**
* @param Model $model
*
* @return string|null
*/
private function getAlternativeName(Model $model)
{
$type = $model->getType();
foreach ($this->alternativeNames as $alternativeName => $criteria) {
if (
Type::BUILTIN_TYPE_OBJECT === $type->getBuiltinType() &&
$type->getClassName() === $criteria['type'] &&
$criteria['groups'] == $model->getGroups()
) {
return $alternativeName;
}
}
return null;
}
private function getTypeShortName(Type $type): string
{
if (null !== $type->getCollectionValueType()) {
return $this->getTypeShortName($type->getCollectionValueType()).'[]';
}
if (Type::BUILTIN_TYPE_OBJECT === $type->getBuiltinType()) {
$parts = explode('\\', $type->getClassName());
return end($parts);
}
return $type->getBuiltinType();
}
private function typeToString(Type $type): string
{
if (Type::BUILTIN_TYPE_OBJECT === $type->getBuiltinType()) {
return $type->getClassName();
} elseif ($type->isCollection()) {
if (null !== $type->getCollectionValueType()) {
return $this->typeToString($type->getCollectionValueType()).'[]';
} else {
return 'mixed[]';
}
} else {
return $type->getBuiltinType();
}
}
}