vendor/sensio/framework-extra-bundle/Request/ParamConverter/DoctrineParamConverter.php line 87

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
  11. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  12. use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
  13. use Symfony\Component\ExpressionLanguage\SyntaxError;
  14. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  15. use Symfony\Component\HttpFoundation\Request;
  16. use Doctrine\Common\Persistence\ManagerRegistry;
  17. use Doctrine\ORM\EntityManagerInterface;
  18. use Doctrine\ORM\NoResultException;
  19. /**
  20.  * DoctrineParamConverter.
  21.  *
  22.  * @author Fabien Potencier <fabien@symfony.com>
  23.  */
  24. class DoctrineParamConverter implements ParamConverterInterface
  25. {
  26.     /**
  27.      * @var ManagerRegistry
  28.      */
  29.     private $registry;
  30.     /**
  31.      * @var ExpressionLanguage
  32.      */
  33.     private $language;
  34.     public function __construct(ManagerRegistry $registry nullExpressionLanguage $expressionLanguage null)
  35.     {
  36.         $this->registry $registry;
  37.         $this->language $expressionLanguage;
  38.     }
  39.     /**
  40.      * {@inheritdoc}
  41.      *
  42.      * @throws \LogicException       When unable to guess how to get a Doctrine instance from the request information
  43.      * @throws NotFoundHttpException When object not found
  44.      */
  45.     public function apply(Request $requestParamConverter $configuration)
  46.     {
  47.         $name $configuration->getName();
  48.         $class $configuration->getClass();
  49.         $options $this->getOptions($configuration);
  50.         if (null === $request->attributes->get($namefalse)) {
  51.             $configuration->setIsOptional(true);
  52.         }
  53.         $errorMessage null;
  54.         if ($expr $options['expr']) {
  55.             $object $this->findViaExpression($class$request$expr$options$configuration);
  56.             if (null === $object) {
  57.                 $errorMessage sprintf('The expression "%s" returned null'$expr);
  58.             }
  59.             // find by identifier?
  60.         } elseif (false === $object $this->find($class$request$options$name)) {
  61.             // find by criteria
  62.             if (false === $object $this->findOneBy($class$request$options)) {
  63.                 if ($configuration->isOptional()) {
  64.                     $object null;
  65.                 } else {
  66.                     throw new \LogicException(sprintf('Unable to guess how to get a Doctrine instance from the request information for parameter "%s".'$name));
  67.                 }
  68.             }
  69.         }
  70.         if (null === $object && false === $configuration->isOptional()) {
  71.             $message sprintf('%s object not found by the @%s annotation.'$class$this->getAnnotationName($configuration));
  72.             if ($errorMessage) {
  73.                 $message .= ' '.$errorMessage;
  74.             }
  75.             throw new NotFoundHttpException($message);
  76.         }
  77.         $request->attributes->set($name$object);
  78.         return true;
  79.     }
  80.     private function find($classRequest $request$options$name)
  81.     {
  82.         if ($options['mapping'] || $options['exclude']) {
  83.             return false;
  84.         }
  85.         $id $this->getIdentifier($request$options$name);
  86.         if (false === $id || null === $id) {
  87.             return false;
  88.         }
  89.         if ($options['repository_method']) {
  90.             $method $options['repository_method'];
  91.         } else {
  92.             $method 'find';
  93.         }
  94.         $om $this->getManager($options['entity_manager'], $class);
  95.         if ($options['evict_cache'] && $om instanceof EntityManagerInterface) {
  96.             $cacheProvider $om->getCache();
  97.             if ($cacheProvider && $cacheProvider->containsEntity($class$id)) {
  98.                 $cacheProvider->evictEntity($class$id);
  99.             }
  100.         }
  101.         try {
  102.             return $om->getRepository($class)->$method($id);
  103.         } catch (NoResultException $e) {
  104.             return;
  105.         }
  106.     }
  107.     private function getIdentifier(Request $request$options$name)
  108.     {
  109.         if (null !== $options['id']) {
  110.             if (!is_array($options['id'])) {
  111.                 $name $options['id'];
  112.             } elseif (is_array($options['id'])) {
  113.                 $id = [];
  114.                 foreach ($options['id'] as $field) {
  115.                     $id[$field] = $request->attributes->get($field);
  116.                 }
  117.                 return $id;
  118.             }
  119.         }
  120.         if ($request->attributes->has($name)) {
  121.             return $request->attributes->get($name);
  122.         }
  123.         if ($request->attributes->has('id') && !$options['id']) {
  124.             return $request->attributes->get('id');
  125.         }
  126.         return false;
  127.     }
  128.     private function findOneBy($classRequest $request$options)
  129.     {
  130.         if (!$options['mapping']) {
  131.             $keys $request->attributes->keys();
  132.             $options['mapping'] = $keys array_combine($keys$keys) : [];
  133.         }
  134.         foreach ($options['exclude'] as $exclude) {
  135.             unset($options['mapping'][$exclude]);
  136.         }
  137.         if (!$options['mapping']) {
  138.             return false;
  139.         }
  140.         // if a specific id has been defined in the options and there is no corresponding attribute
  141.         // return false in order to avoid a fallback to the id which might be of another object
  142.         if ($options['id'] && null === $request->attributes->get($options['id'])) {
  143.             return false;
  144.         }
  145.         $criteria = [];
  146.         $em $this->getManager($options['entity_manager'], $class);
  147.         $metadata $em->getClassMetadata($class);
  148.         $mapMethodSignature $options['repository_method']
  149.             && $options['map_method_signature']
  150.             && true === $options['map_method_signature'];
  151.         foreach ($options['mapping'] as $attribute => $field) {
  152.             if ($metadata->hasField($field)
  153.                 || ($metadata->hasAssociation($field) && $metadata->isSingleValuedAssociation($field))
  154.                 || $mapMethodSignature) {
  155.                 $criteria[$field] = $request->attributes->get($attribute);
  156.             }
  157.         }
  158.         if ($options['strip_null']) {
  159.             $criteria array_filter($criteria, function ($value) {
  160.                 return null !== $value;
  161.             });
  162.         }
  163.         if (!$criteria) {
  164.             return false;
  165.         }
  166.         if ($options['repository_method']) {
  167.             $repositoryMethod $options['repository_method'];
  168.         } else {
  169.             $repositoryMethod 'findOneBy';
  170.         }
  171.         try {
  172.             if ($mapMethodSignature) {
  173.                 return $this->findDataByMapMethodSignature($em$class$repositoryMethod$criteria);
  174.             }
  175.             return $em->getRepository($class)->$repositoryMethod($criteria);
  176.         } catch (NoResultException $e) {
  177.             return;
  178.         }
  179.     }
  180.     private function findDataByMapMethodSignature($em$class$repositoryMethod$criteria)
  181.     {
  182.         $arguments = [];
  183.         $repository $em->getRepository($class);
  184.         $ref = new \ReflectionMethod($repository$repositoryMethod);
  185.         foreach ($ref->getParameters() as $parameter) {
  186.             if (array_key_exists($parameter->name$criteria)) {
  187.                 $arguments[] = $criteria[$parameter->name];
  188.             } elseif ($parameter->isDefaultValueAvailable()) {
  189.                 $arguments[] = $parameter->getDefaultValue();
  190.             } else {
  191.                 throw new \InvalidArgumentException(sprintf('Repository method "%s::%s" requires that you provide a value for the "$%s" argument.'get_class($repository), $repositoryMethod$parameter->name));
  192.             }
  193.         }
  194.         return $ref->invokeArgs($repository$arguments);
  195.     }
  196.     private function findViaExpression($classRequest $request$expression$optionsParamConverter $configuration)
  197.     {
  198.         if (null === $this->language) {
  199.             throw new \LogicException(sprintf('To use the @%s tag with the "expr" option, you need to install the ExpressionLanguage component.'$this->getAnnotationName($configuration)));
  200.         }
  201.         $repository $this->getManager($options['entity_manager'], $class)->getRepository($class);
  202.         $variables array_merge($request->attributes->all(), ['repository' => $repository]);
  203.         try {
  204.             return $this->language->evaluate($expression$variables);
  205.         } catch (NoResultException $e) {
  206.             return;
  207.         } catch (SyntaxError $e) {
  208.             throw new \LogicException(sprintf('Error parsing expression -- %s -- (%s)'$expression$e->getMessage()), 0$e);
  209.         }
  210.     }
  211.     /**
  212.      * {@inheritdoc}
  213.      */
  214.     public function supports(ParamConverter $configuration)
  215.     {
  216.         // if there is no manager, this means that only Doctrine DBAL is configured
  217.         if (null === $this->registry || !count($this->registry->getManagerNames())) {
  218.             return false;
  219.         }
  220.         if (null === $configuration->getClass()) {
  221.             return false;
  222.         }
  223.         $options $this->getOptions($configurationfalse);
  224.         // Doctrine Entity?
  225.         $em $this->getManager($options['entity_manager'], $configuration->getClass());
  226.         if (null === $em) {
  227.             return false;
  228.         }
  229.         return !$em->getMetadataFactory()->isTransient($configuration->getClass());
  230.     }
  231.     private function getOptions(ParamConverter $configuration$strict true)
  232.     {
  233.         $defaultValues = [
  234.             'entity_manager' => null,
  235.             'exclude' => [],
  236.             'mapping' => [],
  237.             'strip_null' => false,
  238.             'expr' => null,
  239.             'id' => null,
  240.             'repository_method' => null,
  241.             'map_method_signature' => false,
  242.             'evict_cache' => false,
  243.         ];
  244.         $passedOptions $configuration->getOptions();
  245.         if (isset($passedOptions['repository_method'])) {
  246.             @trigger_error('The repository_method option of @ParamConverter is deprecated and will be removed in 6.0. Use the expr option or @Entity.'E_USER_DEPRECATED);
  247.         }
  248.         if (isset($passedOptions['map_method_signature'])) {
  249.             @trigger_error('The map_method_signature option of @ParamConverter is deprecated and will be removed in 6.0. Use the expr option or @Entity.'E_USER_DEPRECATED);
  250.         }
  251.         $extraKeys array_diff(array_keys($passedOptions), array_keys($defaultValues));
  252.         if ($extraKeys && $strict) {
  253.             throw new \InvalidArgumentException(sprintf('Invalid option(s) passed to @%s: %s'$this->getAnnotationName($configuration), implode(', '$extraKeys)));
  254.         }
  255.         return array_replace($defaultValues$passedOptions);
  256.     }
  257.     private function getManager($name$class)
  258.     {
  259.         if (null === $name) {
  260.             return $this->registry->getManagerForClass($class);
  261.         }
  262.         return $this->registry->getManager($name);
  263.     }
  264.     private function getAnnotationName(ParamConverter $configuration)
  265.     {
  266.         $r = new \ReflectionClass($configuration);
  267.         return $r->getShortName();
  268.     }
  269. }