vendor/sonata-project/doctrine-orm-admin-bundle/src/Model/ModelManager.php line 75

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of the Sonata Project package.
  5.  *
  6.  * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  7.  *
  8.  * For the full copyright and license information, please view the LICENSE
  9.  * file that was distributed with this source code.
  10.  */
  11. namespace Sonata\DoctrineORMAdminBundle\Model;
  12. use Doctrine\Common\Util\ClassUtils;
  13. use Doctrine\DBAL\DBALException;
  14. use Doctrine\DBAL\LockMode;
  15. use Doctrine\DBAL\Platforms\AbstractPlatform;
  16. use Doctrine\DBAL\Types\Type;
  17. use Doctrine\ORM\EntityManager;
  18. use Doctrine\ORM\OptimisticLockException;
  19. use Doctrine\ORM\Query;
  20. use Doctrine\ORM\QueryBuilder;
  21. use Doctrine\ORM\UnitOfWork;
  22. use Doctrine\Persistence\ManagerRegistry;
  23. use Doctrine\Persistence\Mapping\ClassMetadata;
  24. use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
  25. use Sonata\AdminBundle\Datagrid\DatagridInterface;
  26. use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
  27. use Sonata\AdminBundle\Exception\LockException;
  28. use Sonata\AdminBundle\Exception\ModelManagerException;
  29. use Sonata\AdminBundle\Model\LockInterface;
  30. use Sonata\AdminBundle\Model\ModelManagerInterface;
  31. use Sonata\DoctrineORMAdminBundle\Admin\FieldDescription;
  32. use Sonata\DoctrineORMAdminBundle\Datagrid\OrderByToSelectWalker;
  33. use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery;
  34. use Sonata\Exporter\Source\DoctrineORMQuerySourceIterator;
  35. use Symfony\Component\PropertyAccess\PropertyAccess;
  36. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  37. class ModelManager implements ModelManagerInterfaceLockInterface
  38. {
  39.     public const ID_SEPARATOR '~';
  40.     /**
  41.      * @var ManagerRegistry
  42.      */
  43.     protected $registry;
  44.     /**
  45.      * @var PropertyAccessorInterface
  46.      */
  47.     protected $propertyAccessor;
  48.     /**
  49.      * @var EntityManager[]
  50.      */
  51.     protected $cache = [];
  52.     /**
  53.      * NEXT_MAJOR: Make $propertyAccessor mandatory.
  54.      */
  55.     public function __construct(ManagerRegistry $registry, ?PropertyAccessorInterface $propertyAccessor null)
  56.     {
  57.         $this->registry $registry;
  58.         // NEXT_MAJOR: Remove this block.
  59.         if (null === $propertyAccessor) {
  60.             @trigger_error(sprintf(
  61.                 'Constructing "%s" without passing an instance of "%s" as second argument is deprecated since'
  62.                 .' sonata-project/doctrine-orm-admin-bundle 3.22 and will be mandatory in 4.0.',
  63.                 __CLASS__,
  64.                 PropertyAccessorInterface::class
  65.             ), E_USER_DEPRECATED);
  66.             $propertyAccessor PropertyAccess::createPropertyAccessor();
  67.         }
  68.         $this->propertyAccessor $propertyAccessor;
  69.     }
  70.     /**
  71.      * @param string $class
  72.      *
  73.      * @return ClassMetadata
  74.      */
  75.     public function getMetadata($class)
  76.     {
  77.         return $this->getEntityManager($class)->getMetadataFactory()->getMetadataFor($class);
  78.     }
  79.     /**
  80.      * Returns the model's metadata holding the fully qualified property, and the last
  81.      * property name.
  82.      *
  83.      * @param string $baseClass        The base class of the model holding the fully qualified property
  84.      * @param string $propertyFullName The name of the fully qualified property (dot ('.') separated
  85.      *                                 property string)
  86.      *
  87.      * @return array(
  88.      *                \Doctrine\ORM\Mapping\ClassMetadata $parentMetadata,
  89.      *                string $lastPropertyName,
  90.      *                array $parentAssociationMappings
  91.      *                )
  92.      */
  93.     public function getParentMetadataForProperty($baseClass$propertyFullName)
  94.     {
  95.         $nameElements explode('.'$propertyFullName);
  96.         $lastPropertyName array_pop($nameElements);
  97.         $class $baseClass;
  98.         $parentAssociationMappings = [];
  99.         foreach ($nameElements as $nameElement) {
  100.             $metadata $this->getMetadata($class);
  101.             if (isset($metadata->associationMappings[$nameElement])) {
  102.                 $parentAssociationMappings[] = $metadata->associationMappings[$nameElement];
  103.                 $class $metadata->getAssociationTargetClass($nameElement);
  104.                 continue;
  105.             }
  106.             break;
  107.         }
  108.         $properties = \array_slice($nameElements, \count($parentAssociationMappings));
  109.         $properties[] = $lastPropertyName;
  110.         return [$this->getMetadata($class), implode('.'$properties), $parentAssociationMappings];
  111.     }
  112.     /**
  113.      * @param string $class
  114.      *
  115.      * @return bool
  116.      */
  117.     public function hasMetadata($class)
  118.     {
  119.         return $this->getEntityManager($class)->getMetadataFactory()->hasMetadataFor($class);
  120.     }
  121.     public function getNewFieldDescriptionInstance($class$name, array $options = [])
  122.     {
  123.         if (!\is_string($name)) {
  124.             throw new \RuntimeException('The name argument must be a string');
  125.         }
  126.         if (!isset($options['route']['name'])) {
  127.             $options['route']['name'] = 'edit';
  128.         }
  129.         if (!isset($options['route']['parameters'])) {
  130.             $options['route']['parameters'] = [];
  131.         }
  132.         list($metadata$propertyName$parentAssociationMappings) = $this->getParentMetadataForProperty($class$name);
  133.         $fieldDescription = new FieldDescription();
  134.         $fieldDescription->setName($name);
  135.         $fieldDescription->setOptions($options);
  136.         $fieldDescription->setParentAssociationMappings($parentAssociationMappings);
  137.         if (isset($metadata->associationMappings[$propertyName])) {
  138.             $fieldDescription->setAssociationMapping($metadata->associationMappings[$propertyName]);
  139.         }
  140.         if (isset($metadata->fieldMappings[$propertyName])) {
  141.             $fieldDescription->setFieldMapping($metadata->fieldMappings[$propertyName]);
  142.         }
  143.         return $fieldDescription;
  144.     }
  145.     public function create($object)
  146.     {
  147.         try {
  148.             $entityManager $this->getEntityManager($object);
  149.             $entityManager->persist($object);
  150.             $entityManager->flush();
  151.         } catch (\PDOException $e) {
  152.             throw new ModelManagerException(
  153.                 sprintf('Failed to create object: %s'ClassUtils::getClass($object)),
  154.                 $e->getCode(),
  155.                 $e
  156.             );
  157.         } catch (DBALException $e) {
  158.             throw new ModelManagerException(
  159.                 sprintf('Failed to create object: %s'ClassUtils::getClass($object)),
  160.                 $e->getCode(),
  161.                 $e
  162.             );
  163.         }
  164.     }
  165.     public function update($object)
  166.     {
  167.         try {
  168.             $entityManager $this->getEntityManager($object);
  169.             $entityManager->persist($object);
  170.             $entityManager->flush();
  171.         } catch (\PDOException $e) {
  172.             throw new ModelManagerException(
  173.                 sprintf('Failed to update object: %s'ClassUtils::getClass($object)),
  174.                 $e->getCode(),
  175.                 $e
  176.             );
  177.         } catch (DBALException $e) {
  178.             throw new ModelManagerException(
  179.                 sprintf('Failed to update object: %s'ClassUtils::getClass($object)),
  180.                 $e->getCode(),
  181.                 $e
  182.             );
  183.         }
  184.     }
  185.     public function delete($object)
  186.     {
  187.         try {
  188.             $entityManager $this->getEntityManager($object);
  189.             $entityManager->remove($object);
  190.             $entityManager->flush();
  191.         } catch (\PDOException $e) {
  192.             throw new ModelManagerException(
  193.                 sprintf('Failed to delete object: %s'ClassUtils::getClass($object)),
  194.                 $e->getCode(),
  195.                 $e
  196.             );
  197.         } catch (DBALException $e) {
  198.             throw new ModelManagerException(
  199.                 sprintf('Failed to delete object: %s'ClassUtils::getClass($object)),
  200.                 $e->getCode(),
  201.                 $e
  202.             );
  203.         }
  204.     }
  205.     public function getLockVersion($object)
  206.     {
  207.         $metadata $this->getMetadata(ClassUtils::getClass($object));
  208.         if (!$metadata->isVersioned) {
  209.             return null;
  210.         }
  211.         return $metadata->reflFields[$metadata->versionField]->getValue($object);
  212.     }
  213.     public function lock($object$expectedVersion)
  214.     {
  215.         $metadata $this->getMetadata(ClassUtils::getClass($object));
  216.         if (!$metadata->isVersioned) {
  217.             return;
  218.         }
  219.         try {
  220.             $entityManager $this->getEntityManager($object);
  221.             $entityManager->lock($objectLockMode::OPTIMISTIC$expectedVersion);
  222.         } catch (OptimisticLockException $e) {
  223.             throw new LockException($e->getMessage(), $e->getCode(), $e);
  224.         }
  225.     }
  226.     public function find($class$id)
  227.     {
  228.         if (null === $id) {
  229.             @trigger_error(sprintf(
  230.                 'Passing null as argument 1 for %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.20 and will be not allowed in version 4.0.',
  231.                 __METHOD__
  232.             ), E_USER_DEPRECATED);
  233.             return null;
  234.         }
  235.         $values array_combine($this->getIdentifierFieldNames($class), explode(self::ID_SEPARATOR, (string) $id));
  236.         return $this->getEntityManager($class)->getRepository($class)->find($values);
  237.     }
  238.     public function findBy($class, array $criteria = [])
  239.     {
  240.         return $this->getEntityManager($class)->getRepository($class)->findBy($criteria);
  241.     }
  242.     public function findOneBy($class, array $criteria = [])
  243.     {
  244.         return $this->getEntityManager($class)->getRepository($class)->findOneBy($criteria);
  245.     }
  246.     /**
  247.      * @param string|object $class
  248.      *
  249.      * @return EntityManager
  250.      */
  251.     public function getEntityManager($class)
  252.     {
  253.         if (\is_object($class)) {
  254.             $class = \get_class($class);
  255.         }
  256.         if (!isset($this->cache[$class])) {
  257.             $em $this->registry->getManagerForClass($class);
  258.             if (!$em) {
  259.                 throw new \RuntimeException(sprintf('No entity manager defined for class %s'$class));
  260.             }
  261.             $this->cache[$class] = $em;
  262.         }
  263.         return $this->cache[$class];
  264.     }
  265.     /**
  266.      * NEXT_MAJOR: Remove this method.
  267.      *
  268.      * @deprecated since sonata-project/doctrine-orm-admin-bundle 3.22 and will be removed in version 4.0
  269.      */
  270.     public function getParentFieldDescription($parentAssociationMapping$class)
  271.     {
  272.         @trigger_error(sprintf(
  273.             'Method %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.22 and will be removed in 4.0.',
  274.             __METHOD__
  275.         ), E_USER_DEPRECATED);
  276.         $fieldName $parentAssociationMapping['fieldName'];
  277.         $metadata $this->getMetadata($class);
  278.         $associatingMapping $metadata->associationMappings[$parentAssociationMapping];
  279.         $fieldDescription $this->getNewFieldDescriptionInstance($class$fieldName);
  280.         $fieldDescription->setName($parentAssociationMapping);
  281.         $fieldDescription->setAssociationMapping($associatingMapping);
  282.         return $fieldDescription;
  283.     }
  284.     public function createQuery($class$alias 'o')
  285.     {
  286.         $repository $this->getEntityManager($class)->getRepository($class);
  287.         return new ProxyQuery($repository->createQueryBuilder($alias));
  288.     }
  289.     public function executeQuery($query)
  290.     {
  291.         if ($query instanceof QueryBuilder) {
  292.             return $query->getQuery()->execute();
  293.         }
  294.         return $query->execute();
  295.     }
  296.     /**
  297.      * NEXT_MAJOR: Remove this function.
  298.      *
  299.      * @deprecated since sonata-project/doctrine-orm-admin-bundle 3.18. To be removed in 4.0.
  300.      */
  301.     public function getModelIdentifier($class)
  302.     {
  303.         return $this->getMetadata($class)->identifier;
  304.     }
  305.     public function getIdentifierValues($entity)
  306.     {
  307.         // Fix code has an impact on performance, so disable it ...
  308.         //$entityManager = $this->getEntityManager($entity);
  309.         //if (!$entityManager->getUnitOfWork()->isInIdentityMap($entity)) {
  310.         //    throw new \RuntimeException('Entities passed to the choice field must be managed');
  311.         //}
  312.         $class ClassUtils::getClass($entity);
  313.         $metadata $this->getMetadata($class);
  314.         $platform $this->getEntityManager($class)->getConnection()->getDatabasePlatform();
  315.         $identifiers = [];
  316.         foreach ($metadata->getIdentifierValues($entity) as $name => $value) {
  317.             if (!\is_object($value)) {
  318.                 $identifiers[] = $value;
  319.                 continue;
  320.             }
  321.             $fieldType $metadata->getTypeOfField($name);
  322.             $type $fieldType && Type::hasType($fieldType) ? Type::getType($fieldType) : null;
  323.             if ($type) {
  324.                 $identifiers[] = $this->getValueFromType($value$type$fieldType$platform);
  325.                 continue;
  326.             }
  327.             $identifierMetadata $this->getMetadata(ClassUtils::getClass($value));
  328.             foreach ($identifierMetadata->getIdentifierValues($value) as $value) {
  329.                 $identifiers[] = $value;
  330.             }
  331.         }
  332.         return $identifiers;
  333.     }
  334.     public function getIdentifierFieldNames($class)
  335.     {
  336.         return $this->getMetadata($class)->getIdentifierFieldNames();
  337.     }
  338.     public function getNormalizedIdentifier($entity)
  339.     {
  340.         // NEXT_MAJOR: Remove the following 2 checks and declare "object" as type for argument 1.
  341.         if (null === $entity) {
  342.             @trigger_error(sprintf(
  343.                 'Passing null as argument 1 for %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.20 and will be not allowed in version 4.0.',
  344.                 __METHOD__
  345.             ), E_USER_DEPRECATED);
  346.             return null;
  347.         }
  348.         if (!\is_object($entity)) {
  349.             throw new \RuntimeException('Invalid argument, object or null required');
  350.         }
  351.         if (\in_array($this->getEntityManager($entity)->getUnitOfWork()->getEntityState($entity), [
  352.             UnitOfWork::STATE_NEW,
  353.             UnitOfWork::STATE_REMOVED,
  354.         ], true)) {
  355.             // NEXT_MAJOR: Uncomment the following exception, remove the deprecation and the return statement inside this conditional block.
  356.             // throw new \InvalidArgumentException(sprintf(
  357.             //    'Can not get the normalized identifier for %s since it is in state %u.',
  358.             //    ClassUtils::getClass($entity),
  359.             //    $this->getEntityManager($entity)->getUnitOfWork()->getEntityState($entity)
  360.             // ));
  361.             @trigger_error(sprintf(
  362.                 'Passing an object which is in state %u (new) or %u (removed) as argument 1 for %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.20'
  363.                 .'and will be not allowed in version 4.0.',
  364.                 UnitOfWork::STATE_NEW,
  365.                 UnitOfWork::STATE_REMOVED,
  366.                 __METHOD__
  367.             ), E_USER_DEPRECATED);
  368.             return null;
  369.         }
  370.         $values $this->getIdentifierValues($entity);
  371.         if (=== \count($values)) {
  372.             return null;
  373.         }
  374.         return implode(self::ID_SEPARATOR$values);
  375.     }
  376.     /**
  377.      * {@inheritdoc}
  378.      *
  379.      * The ORM implementation does nothing special but you still should use
  380.      * this method when using the id in a URL to allow for future improvements.
  381.      */
  382.     public function getUrlSafeIdentifier($entity)
  383.     {
  384.         // NEXT_MAJOR: Remove the following check and declare "object" as type for argument 1.
  385.         if (!\is_object($entity)) {
  386.             @trigger_error(sprintf(
  387.                 'Passing other type than object for argument 1 for %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.20 and will be not allowed in version 4.0.',
  388.                 __METHOD__
  389.             ), E_USER_DEPRECATED);
  390.             return null;
  391.         }
  392.         return $this->getNormalizedIdentifier($entity);
  393.     }
  394.     public function addIdentifiersToQuery($classProxyQueryInterface $queryProxy, array $idx)
  395.     {
  396.         $fieldNames $this->getIdentifierFieldNames($class);
  397.         $qb $queryProxy->getQueryBuilder();
  398.         $prefix uniqid();
  399.         $sqls = [];
  400.         foreach ($idx as $pos => $id) {
  401.             $ids explode(self::ID_SEPARATOR$id);
  402.             $ands = [];
  403.             foreach ($fieldNames as $posName => $name) {
  404.                 $parameterName sprintf('field_%s_%s_%d'$prefix$name$pos);
  405.                 $ands[] = sprintf('%s.%s = :%s'current($qb->getRootAliases()), $name$parameterName);
  406.                 $qb->setParameter($parameterName$ids[$posName]);
  407.             }
  408.             $sqls[] = implode(' AND '$ands);
  409.         }
  410.         $qb->andWhere(sprintf('( %s )'implode(' OR '$sqls)));
  411.     }
  412.     public function batchDelete($classProxyQueryInterface $queryProxy)
  413.     {
  414.         $queryProxy->select('DISTINCT '.current($queryProxy->getRootAliases()));
  415.         try {
  416.             $entityManager $this->getEntityManager($class);
  417.             $i 0;
  418.             foreach ($queryProxy->getQuery()->iterate() as $pos => $object) {
  419.                 $entityManager->remove($object[0]);
  420.                 if (=== (++$i 20)) {
  421.                     $entityManager->flush();
  422.                     $entityManager->clear();
  423.                 }
  424.             }
  425.             $entityManager->flush();
  426.             $entityManager->clear();
  427.         } catch (\PDOException DBALException $e) {
  428.             throw new ModelManagerException(''0$e);
  429.         }
  430.     }
  431.     public function getDataSourceIterator(DatagridInterface $datagrid, array $fields$firstResult null$maxResult null)
  432.     {
  433.         $datagrid->buildPager();
  434.         $query $datagrid->getQuery();
  435.         $query->select('DISTINCT '.current($query->getRootAliases()));
  436.         $query->setFirstResult($firstResult);
  437.         $query->setMaxResults($maxResult);
  438.         if ($query instanceof ProxyQueryInterface) {
  439.             $sortBy $query->getSortBy();
  440.             if (!empty($sortBy)) {
  441.                 $query->addOrderBy($sortBy$query->getSortOrder());
  442.                 $query $query->getQuery();
  443.                 $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, [OrderByToSelectWalker::class]);
  444.             } else {
  445.                 $query $query->getQuery();
  446.             }
  447.         }
  448.         return new DoctrineORMQuerySourceIterator($query$fields);
  449.     }
  450.     public function getExportFields($class)
  451.     {
  452.         $metadata $this->getEntityManager($class)->getClassMetadata($class);
  453.         return $metadata->getFieldNames();
  454.     }
  455.     public function getModelInstance($class)
  456.     {
  457.         $r = new \ReflectionClass($class);
  458.         if ($r->isAbstract()) {
  459.             throw new \RuntimeException(sprintf('Cannot initialize abstract class: %s'$class));
  460.         }
  461.         $constructor $r->getConstructor();
  462.         if (null !== $constructor && (!$constructor->isPublic() || $constructor->getNumberOfRequiredParameters() > 0)) {
  463.             return $r->newInstanceWithoutConstructor();
  464.         }
  465.         return new $class();
  466.     }
  467.     /**
  468.      * NEXT_MAJOR: Remove this method.
  469.      *
  470.      * @deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.
  471.      */
  472.     public function getSortParameters(FieldDescriptionInterface $fieldDescriptionDatagridInterface $datagrid)
  473.     {
  474.         @trigger_error(sprintf(
  475.             'Method %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.',
  476.             __METHOD__
  477.         ), E_USER_DEPRECATED);
  478.         $values $datagrid->getValues();
  479.         if ($this->isFieldAlreadySorted($fieldDescription$datagrid)) {
  480.             if ('ASC' === $values['_sort_order']) {
  481.                 $values['_sort_order'] = 'DESC';
  482.             } else {
  483.                 $values['_sort_order'] = 'ASC';
  484.             }
  485.         } else {
  486.             $values['_sort_order'] = 'ASC';
  487.         }
  488.         $values['_sort_by'] = \is_string($fieldDescription->getOption('sortable')) ? $fieldDescription->getOption('sortable') : $fieldDescription->getName();
  489.         return ['filter' => $values];
  490.     }
  491.     /**
  492.      * NEXT_MAJOR: Remove this method.
  493.      *
  494.      * @deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.
  495.      */
  496.     public function getPaginationParameters(DatagridInterface $datagrid$page)
  497.     {
  498.         @trigger_error(sprintf(
  499.             'Method %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.',
  500.             __METHOD__
  501.         ), E_USER_DEPRECATED);
  502.         $values $datagrid->getValues();
  503.         if (isset($values['_sort_by']) && $values['_sort_by'] instanceof FieldDescriptionInterface) {
  504.             $values['_sort_by'] = $values['_sort_by']->getName();
  505.         }
  506.         $values['_page'] = $page;
  507.         return ['filter' => $values];
  508.     }
  509.     public function getDefaultSortValues($class)
  510.     {
  511.         return [
  512.             '_sort_order' => 'ASC',
  513.             '_sort_by' => implode(','$this->getModelIdentifier($class)),
  514.             '_page' => 1,
  515.             '_per_page' => 25,
  516.         ];
  517.     }
  518.     public function getDefaultPerPageOptions(string $class): array
  519.     {
  520.         return [102550100250];
  521.     }
  522.     public function modelTransform($class$instance)
  523.     {
  524.         return $instance;
  525.     }
  526.     public function modelReverseTransform($class, array $array = [])
  527.     {
  528.         $instance $this->getModelInstance($class);
  529.         $metadata $this->getMetadata($class);
  530.         foreach ($array as $name => $value) {
  531.             $property $this->getFieldName($metadata$name);
  532.             $this->propertyAccessor->setValue($instance$property$value);
  533.         }
  534.         return $instance;
  535.     }
  536.     /**
  537.      * NEXT_MAJOR: Remove this method.
  538.      *
  539.      * @deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.
  540.      */
  541.     public function getModelCollectionInstance($class)
  542.     {
  543.         @trigger_error(sprintf(
  544.             'Method %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.',
  545.             __METHOD__
  546.         ), E_USER_DEPRECATED);
  547.         return new \Doctrine\Common\Collections\ArrayCollection();
  548.     }
  549.     /**
  550.      * NEXT_MAJOR: Remove this method.
  551.      *
  552.      * @deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.
  553.      */
  554.     public function collectionClear(&$collection)
  555.     {
  556.         @trigger_error(sprintf(
  557.             'Method %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.',
  558.             __METHOD__
  559.         ), E_USER_DEPRECATED);
  560.         return $collection->clear();
  561.     }
  562.     /**
  563.      * NEXT_MAJOR: Remove this method.
  564.      *
  565.      * @deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.
  566.      */
  567.     public function collectionHasElement(&$collection, &$element)
  568.     {
  569.         @trigger_error(sprintf(
  570.             'Method %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.',
  571.             __METHOD__
  572.         ), E_USER_DEPRECATED);
  573.         return $collection->contains($element);
  574.     }
  575.     /**
  576.      * NEXT_MAJOR: Remove this method.
  577.      *
  578.      * @deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.
  579.      */
  580.     public function collectionAddElement(&$collection, &$element)
  581.     {
  582.         @trigger_error(sprintf(
  583.             'Method %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.',
  584.             __METHOD__
  585.         ), E_USER_DEPRECATED);
  586.         return $collection->add($element);
  587.     }
  588.     /**
  589.      * NEXT_MAJOR: Remove this method.
  590.      *
  591.      * @deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.
  592.      */
  593.     public function collectionRemoveElement(&$collection, &$element)
  594.     {
  595.         @trigger_error(sprintf(
  596.             'Method %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.23 and will be removed in version 4.0.',
  597.             __METHOD__
  598.         ), E_USER_DEPRECATED);
  599.         return $collection->removeElement($element);
  600.     }
  601.     /**
  602.      * NEXT_MAJOR: Remove this method.
  603.      *
  604.      * @param string $property
  605.      *
  606.      * @return mixed
  607.      */
  608.     protected function camelize($property)
  609.     {
  610.         @trigger_error(sprintf(
  611.             'Method %s() is deprecated since sonata-project/doctrine-orm-admin-bundle 3.22 and will be removed in version 4.0.',
  612.             __METHOD__
  613.         ), E_USER_DEPRECATED);
  614.         return str_replace(' '''ucwords(str_replace('_'' '$property)));
  615.     }
  616.     private function getFieldName(ClassMetadata $metadatastring $name): string
  617.     {
  618.         if (\array_key_exists($name$metadata->fieldMappings)) {
  619.             return $metadata->fieldMappings[$name]['fieldName'];
  620.         }
  621.         if (\array_key_exists($name$metadata->associationMappings)) {
  622.             return $metadata->associationMappings[$name]['fieldName'];
  623.         }
  624.         return $name;
  625.     }
  626.     private function isFieldAlreadySorted(FieldDescriptionInterface $fieldDescriptionDatagridInterface $datagrid): bool
  627.     {
  628.         $values $datagrid->getValues();
  629.         if (!isset($values['_sort_by']) || !$values['_sort_by'] instanceof FieldDescriptionInterface) {
  630.             return false;
  631.         }
  632.         return $values['_sort_by']->getName() === $fieldDescription->getName()
  633.             || $values['_sort_by']->getName() === $fieldDescription->getOption('sortable');
  634.     }
  635.     /**
  636.      * @param mixed $value
  637.      */
  638.     private function getValueFromType($valueType $typestring $fieldTypeAbstractPlatform $platform): string
  639.     {
  640.         if ($platform->hasDoctrineTypeMappingFor($fieldType) &&
  641.             'binary' === $platform->getDoctrineTypeMapping($fieldType)
  642.         ) {
  643.             return (string) $type->convertToPHPValue($value$platform);
  644.         }
  645.         // some libraries may have `toString()` implementation
  646.         if (\is_callable([$value'toString'])) {
  647.             return $value->toString();
  648.         }
  649.         // final fallback to magic `__toString()` which may throw an exception in 7.4
  650.         if (method_exists($value'__toString')) {
  651.             return $value->__toString();
  652.         }
  653.         return (string) $type->convertToDatabaseValue($value$platform);
  654.     }
  655. }