* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Form\ChoiceList; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Types\ConversionException; use Doctrine\DBAL\Types\Type; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\Exception\TransformationFailedException; /** * Loads entities using a {@link QueryBuilder} instance. * * @author Benjamin Eberlei * @author Bernhard Schussek */ class ORMQueryBuilderLoader implements EntityLoaderInterface { /** * Contains the query builder that builds the query for fetching the * entities. * * This property should only be accessed through queryBuilder. * * @var QueryBuilder */ private $queryBuilder; public function __construct(QueryBuilder $queryBuilder) { $this->queryBuilder = $queryBuilder; } /** * {@inheritdoc} */ public function getEntities() { return $this->queryBuilder->getQuery()->execute(); } /** * {@inheritdoc} */ public function getEntitiesByIds(string $identifier, array $values) { if (null !== $this->queryBuilder->getMaxResults() || null !== $this->queryBuilder->getFirstResult()) { // an offset or a limit would apply on results including the where clause with submitted id values // that could make invalid choices valid $choices = []; $metadata = $this->queryBuilder->getEntityManager()->getClassMetadata(current($this->queryBuilder->getRootEntities())); foreach ($this->getEntities() as $entity) { if (\in_array((string) current($metadata->getIdentifierValues($entity)), $values, true)) { $choices[] = $entity; } } return $choices; } $qb = clone $this->queryBuilder; $alias = current($qb->getRootAliases()); $parameter = 'ORMQueryBuilderLoader_getEntitiesByIds_'.$identifier; $parameter = str_replace('.', '_', $parameter); $where = $qb->expr()->in($alias.'.'.$identifier, ':'.$parameter); // Guess type $entity = current($qb->getRootEntities()); $metadata = $qb->getEntityManager()->getClassMetadata($entity); if (\in_array($type = $metadata->getTypeOfField($identifier), ['integer', 'bigint', 'smallint'])) { $parameterType = Connection::PARAM_INT_ARRAY; // Filter out non-integer values (e.g. ""). If we don't, some // databases such as PostgreSQL fail. $values = array_values(array_filter($values, function ($v) { return (string) $v === (string) (int) $v || ctype_digit($v); })); } elseif (\in_array($type, ['ulid', 'uuid', 'guid'])) { $parameterType = Connection::PARAM_STR_ARRAY; // Like above, but we just filter out empty strings. $values = array_values(array_filter($values, function ($v) { return '' !== (string) $v; })); // Convert values into right type if (Type::hasType($type)) { $doctrineType = Type::getType($type); $platform = $qb->getEntityManager()->getConnection()->getDatabasePlatform(); foreach ($values as &$value) { try { $value = $doctrineType->convertToDatabaseValue($value, $platform); } catch (ConversionException $e) { throw new TransformationFailedException(sprintf('Failed to transform "%s" into "%s".', $value, $type), 0, $e); } } unset($value); } } else { $parameterType = Connection::PARAM_STR_ARRAY; } if (!$values) { return []; } return $qb->andWhere($where) ->getQuery() ->setParameter($parameter, $values, $parameterType) ->getResult(); } }