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 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 ;
2020-05-28 13:19:11 +02:00
use Nelmio\ApiDocBundle\OpenApiPhp\Util ;
2019-12-13 21:40:42 +01:00
use Nelmio\ApiDocBundle\PropertyDescriber\PropertyDescriberInterface ;
2020-05-28 13:19:11 +02:00
use OpenApi\Annotations as OA ;
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 ;
2019-12-13 21:40:42 +01:00
/** @var PropertyInfoExtractorInterface */
2017-03-15 12:33:05 +01:00
private $propertyInfo ;
2019-12-13 21:40:42 +01:00
/** @var Reader */
2018-03-17 19:23:29 +01:00
private $doctrineReader ;
2019-12-13 21:40:42 +01:00
/** @var PropertyDescriberInterface[] */
private $propertyDescribers ;
2020-05-28 13:19:11 +02:00
/** @var string[] */
private $mediaTypes ;
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 ,
2019-12-13 21:40:42 +01:00
Reader $reader ,
2020-05-28 13:19:11 +02:00
$propertyDescribers ,
array $mediaTypes
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 ;
2019-12-13 22:20:13 +01:00
$this -> propertyDescribers = $propertyDescribers ;
2020-05-28 13:19:11 +02:00
$this -> mediaTypes = $mediaTypes ;
2017-01-14 17:36:56 +01:00
}
2020-05-28 13:19:11 +02:00
public function describe ( Model $model , OA\Schema $schema )
2017-01-14 17:36:56 +01:00
{
2020-05-28 13:19:11 +02:00
$schema -> type = 'object' ;
2017-01-14 17:36:56 +01:00
$class = $model -> getType () -> getClassName ();
2020-05-28 13:19:11 +02:00
$schema -> _context -> class = $class ;
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
2020-05-28 13:19:11 +02:00
$annotationsReader = new AnnotationsReader ( $this -> doctrineReader , $this -> modelRegistry , $this -> mediaTypes );
2018-03-17 19:23:29 +01:00
$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 ) {
2020-05-28 13:19:11 +02:00
// read property options from OpenApi Property annotation if it exists
2018-01-11 12:26:59 +01:00
if ( property_exists ( $class , $propertyName )) {
2018-02-19 21:41:05 +01:00
$reflectionProperty = new \ReflectionProperty ( $class , $propertyName );
2020-05-28 13:19:11 +02:00
$property = Util :: getProperty ( $schema , $annotationsReader -> getPropertyName ( $reflectionProperty , $propertyName ));
2018-03-17 19:23:29 +01:00
$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 {
2020-05-28 13:19:11 +02:00
$property = Util :: getProperty ( $schema , $propertyName );
2018-01-11 12:26:59 +01:00
}
// If type manually defined
2020-05-28 13:19:11 +02:00
if ( OA\UNDEFINED !== $property -> type || OA\UNDEFINED !== $property -> ref ) {
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 )) {
2019-04-12 09:35:49 +02:00
throw new \LogicException ( sprintf ( 'The PropertyInfo component was not able to guess the type of %s::$%s. You may need to add a `@var` annotation or use `@SWG\Property(type="")` to make its type explicit.' , $class , $propertyName ));
2017-01-14 17:36:56 +01:00
}
if ( count ( $types ) > 1 ) {
2019-04-12 09:35:49 +02:00
throw new \LogicException ( sprintf ( 'Property %s::$%s defines more than one type. You can specify the one that should be documented using `@SWG\Property(type="")`.' , $class , $propertyName ));
2017-01-14 17:36:56 +01:00
}
2017-12-03 20:30:44 +02:00
$type = $types [ 0 ];
2019-12-13 22:45:32 +01:00
$this -> describeProperty ( $type , $model , $property , $propertyName );
2019-12-13 22:20:13 +01:00
}
}
2018-01-11 12:26:59 +01:00
2020-05-28 13:19:11 +02:00
private function describeProperty ( Type $type , Model $model , OA\Schema $property , string $propertyName )
2019-12-13 22:20:13 +01:00
{
foreach ( $this -> propertyDescribers as $propertyDescriber ) {
2020-02-18 21:08:48 +01:00
if ( $propertyDescriber instanceof ModelRegistryAwareInterface ) {
$propertyDescriber -> setModelRegistry ( $this -> modelRegistry );
}
2019-12-13 22:20:13 +01:00
if ( $propertyDescriber -> supports ( $type )) {
$propertyDescriber -> describe ( $type , $property , $model -> getGroups ());
2019-12-13 21:40:42 +01:00
2019-12-13 22:20:13 +01:00
return ;
}
2017-01-14 17:36:56 +01:00
}
2019-12-13 22:20:13 +01:00
2020-05-28 13:19:11 +02:00
throw new \Exception ( sprintf ( 'Type "%s" is not supported in %s::$%s. You may use the `@OA\Property(type="")` annotation to specify it manually.' , $type -> getBuiltinType (), $model -> getType () -> getClassName (), $propertyName ));
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
{
2019-04-16 12:13:33 +02:00
return Type :: BUILTIN_TYPE_OBJECT === $model -> getType () -> getBuiltinType () && class_exists ( $model -> getType () -> getClassName ());
2017-01-14 17:36:56 +01:00
}
}