2017-01-14 17:36:56 +01:00
< ? 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\ModelDescriber ;
2018-01-24 19:58:38 +01:00
use Doctrine\Common\Annotations\Reader ;
2017-01-14 17:36:56 +01:00
use EXSyst\Component\Swagger\Schema ;
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface ;
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareTrait ;
use Nelmio\ApiDocBundle\Model\Model ;
2018-01-24 19:58:38 +01:00
use Nelmio\ApiDocBundle\ModelDescriber\Annotations\AnnotationsReader ;
2017-01-14 17:36:56 +01:00
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface ;
use Symfony\Component\PropertyInfo\Type ;
class ObjectModelDescriber implements ModelDescriberInterface , ModelRegistryAwareInterface
{
use ModelRegistryAwareTrait ;
2017-03-15 12:33:05 +01:00
private $propertyInfo ;
2018-03-17 19:23:29 +01:00
private $doctrineReader ;
2017-12-03 20:30:44 +02:00
2018-01-24 15:20:20 +01:00
private $swaggerDefinitionAnnotationReader ;
2017-12-03 20:30:44 +02:00
public function __construct (
PropertyInfoExtractorInterface $propertyInfo ,
2018-01-24 19:58:38 +01:00
Reader $reader
2017-12-17 10:44:07 +01:00
) {
2017-01-14 17:36:56 +01:00
$this -> propertyInfo = $propertyInfo ;
2018-03-17 19:23:29 +01:00
$this -> doctrineReader = $reader ;
2017-01-14 17:36:56 +01:00
}
public function describe ( Model $model , Schema $schema )
{
$schema -> setType ( 'object' );
$properties = $schema -> getProperties ();
$class = $model -> getType () -> getClassName ();
2017-06-13 13:34:26 +02:00
$context = [];
if ( null !== $model -> getGroups ()) {
2018-11-04 12:24:45 +01:00
$context = [ 'serializer_groups' => array_filter ( $model -> getGroups (), 'is_string' )];
2017-06-13 13:34:26 +02:00
}
2018-03-17 19:23:29 +01:00
$annotationsReader = new AnnotationsReader ( $this -> doctrineReader , $this -> modelRegistry );
$annotationsReader -> updateDefinition ( new \ReflectionClass ( $class ), $schema );
2017-06-13 13:34:26 +02:00
$propertyInfoProperties = $this -> propertyInfo -> getProperties ( $class , $context );
2017-03-15 12:33:05 +01:00
if ( null === $propertyInfoProperties ) {
return ;
}
2017-05-31 18:35:02 +02:00
2017-03-15 12:33:05 +01:00
foreach ( $propertyInfoProperties as $propertyName ) {
2018-01-11 12:26:59 +01:00
// read property options from Swagger Property annotation if it exists
if ( property_exists ( $class , $propertyName )) {
2018-02-19 21:41:05 +01:00
$reflectionProperty = new \ReflectionProperty ( $class , $propertyName );
2018-03-17 19:23:29 +01:00
$property = $properties -> get ( $annotationsReader -> getPropertyName ( $reflectionProperty , $propertyName ));
$groups = $model -> getGroups ();
2018-04-05 15:54:51 +03:00
if ( isset ( $groups [ $propertyName ]) && is_array ( $groups [ $propertyName ])) {
$groups = $model -> getGroups ()[ $propertyName ];
2018-03-17 19:23:29 +01:00
}
$annotationsReader -> updateProperty ( $reflectionProperty , $property , $groups );
2018-02-19 21:41:05 +01:00
} else {
$property = $properties -> get ( $propertyName );
2018-01-11 12:26:59 +01:00
}
// If type manually defined
2018-03-17 19:23:29 +01:00
if ( null !== $property -> getType () || null !== $property -> getRef ()) {
2018-01-11 12:26:59 +01:00
continue ;
}
2017-01-14 17:36:56 +01:00
$types = $this -> propertyInfo -> getTypes ( $class , $propertyName );
2018-02-14 13:51:06 +01:00
if ( null === $types || 0 === count ( $types )) {
2017-01-14 17:36:56 +01:00
throw new \LogicException ( sprintf ( 'The PropertyInfo component was not able to guess the type of %s::$%s' , $class , $propertyName ));
}
if ( count ( $types ) > 1 ) {
throw new \LogicException ( sprintf ( 'Property %s::$%s defines more than one type.' , $class , $propertyName ));
}
2017-12-03 20:30:44 +02:00
$type = $types [ 0 ];
2018-07-27 08:30:22 +02:00
if ( $type -> isCollection ()) {
2017-12-03 20:30:44 +02:00
$type = $type -> getCollectionValueType ();
2018-01-11 12:26:59 +01:00
if ( null === $type ) {
throw new \LogicException ( sprintf ( 'Property "%s:%s" is an array, but no indication of the array elements are made. Use e.g. string[] for an array of string.' , $class , $propertyName ));
}
2017-12-03 20:30:44 +02:00
$property -> setType ( 'array' );
$property = $property -> getItems ();
}
2017-12-17 10:44:07 +01:00
if ( Type :: BUILTIN_TYPE_STRING === $type -> getBuiltinType ()) {
2017-12-03 20:30:44 +02:00
$property -> setType ( 'string' );
2017-12-17 10:44:07 +01:00
} elseif ( Type :: BUILTIN_TYPE_BOOL === $type -> getBuiltinType ()) {
2017-12-03 20:30:44 +02:00
$property -> setType ( 'boolean' );
2017-12-17 10:44:07 +01:00
} elseif ( Type :: BUILTIN_TYPE_INT === $type -> getBuiltinType ()) {
2017-12-03 20:30:44 +02:00
$property -> setType ( 'integer' );
2017-12-17 10:44:07 +01:00
} elseif ( Type :: BUILTIN_TYPE_FLOAT === $type -> getBuiltinType ()) {
2017-12-03 20:30:44 +02:00
$property -> setType ( 'number' );
$property -> setFormat ( 'float' );
2017-12-17 10:44:07 +01:00
} elseif ( Type :: BUILTIN_TYPE_OBJECT === $type -> getBuiltinType ()) {
2018-12-10 14:21:09 +01:00
if ( is_a ( $type -> getClassName (), \DateTimeInterface :: class , true )) {
2017-12-03 20:30:44 +02:00
$property -> setType ( 'string' );
$property -> setFormat ( 'date-time' );
} else {
2018-08-30 01:10:36 +02:00
$type = new Type ( $type -> getBuiltinType (), false , $type -> getClassName (), $type -> isCollection (), $type -> getCollectionKeyType (), $type -> getCollectionValueType ()); // ignore nullable field
2017-12-03 20:30:44 +02:00
$property -> setRef (
$this -> modelRegistry -> register ( new Model ( $type , $model -> getGroups ()))
);
}
} else {
2017-12-17 10:44:07 +01:00
throw new \Exception ( sprintf ( 'Unknown type: %s' , $type -> getBuiltinType ()));
2017-12-03 20:30:44 +02:00
}
2017-01-14 17:36:56 +01:00
}
}
2017-03-17 19:37:41 +01:00
public function supports ( Model $model ) : bool
2017-01-14 17:36:56 +01:00
{
return Type :: BUILTIN_TYPE_OBJECT === $model -> getType () -> getBuiltinType ();
}
}