/** * Adds the where clause according to the chosen null management. * * @param QueryBuilder $queryBuilder * @param QueryNameGeneratorInterface $queryNameGenerator * @param string $alias * @param string $field * @param string $operator * @param string $value * @param string|null $nullManagement */ protected function addWhere(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $field, string $operator, string $value, string $nullManagement = null) { $valueParameter = $queryNameGenerator->generateParameterName($field); $baseWhere = sprintf('%s.%s %s :%s', $alias, $field, self::PARAMETER_BEFORE === $operator ? '<=' : '>=', $valueParameter); if (null === $nullManagement || self::EXCLUDE_NULL === $nullManagement) { $queryBuilder->andWhere($baseWhere); } elseif (self::PARAMETER_BEFORE === $operator && self::INCLUDE_NULL_BEFORE === $nullManagement || self::PARAMETER_AFTER === $operator && self::INCLUDE_NULL_AFTER === $nullManagement) { $queryBuilder->andWhere($queryBuilder->expr()->orX($baseWhere, $queryBuilder->expr()->isNull(sprintf('%s.%s', $alias, $field)))); } else { $queryBuilder->andWhere($queryBuilder->expr()->andX($baseWhere, $queryBuilder->expr()->isNotNull(sprintf('%s.%s', $alias, $field)))); } $queryBuilder->setParameter($valueParameter, new \DateTime($value)); }
/** * Adds the where clause according to the operator. * * @param QueryBuilder $queryBuilder * @param QueryNameGeneratorInterface $queryNameGenerator * @param string $alias * @param string $field * @param string $operator * @param string $value */ protected function addWhere(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, $alias, $field, $operator, $value) { $valueParameter = $queryNameGenerator->generateParameterName($field); switch ($operator) { case self::PARAMETER_BETWEEN: $rangeValue = explode('..', $value); if (2 !== count($rangeValue)) { $this->logger->notice('Invalid filter ignored', ['exception' => new InvalidArgumentException(sprintf('Invalid format for "[%s]", expected "<min>..<max>"', $operator))]); return; } if (!is_numeric($rangeValue[0]) || !is_numeric($rangeValue[1])) { $this->logger->notice('Invalid filter ignored', ['exception' => new InvalidArgumentException(sprintf('Invalid values for "[%s]" range, expected numbers', $operator))]); return; } $queryBuilder->andWhere(sprintf('%1$s.%2$s BETWEEN :%3$s_1 AND :%3$s_2', $alias, $field, $valueParameter))->setParameter(sprintf('%s_1', $valueParameter), $rangeValue[0])->setParameter(sprintf('%s_2', $valueParameter), $rangeValue[1]); break; case self::PARAMETER_GREATER_THAN: if (!is_numeric($value)) { $this->logger->notice('Invalid filter ignored', ['exception' => new InvalidArgumentException(sprintf('Invalid value for "[%s]", expected number', $operator))]); return; } $queryBuilder->andWhere(sprintf('%s.%s > :%s', $alias, $field, $valueParameter))->setParameter($valueParameter, $value); break; case self::PARAMETER_GREATER_THAN_OR_EQUAL: if (!is_numeric($value)) { $this->logger->notice('Invalid filter ignored', ['exception' => new InvalidArgumentException(sprintf('Invalid value for "[%s]", expected number', $operator))]); return; } $queryBuilder->andWhere(sprintf('%s.%s >= :%s', $alias, $field, $valueParameter))->setParameter($valueParameter, $value); break; case self::PARAMETER_LESS_THAN: if (!is_numeric($value)) { $this->logger->notice('Invalid filter ignored', ['exception' => new InvalidArgumentException(sprintf('Invalid value for "[%s]", expected number', $operator))]); return; } $queryBuilder->andWhere(sprintf('%s.%s < :%s', $alias, $field, $valueParameter))->setParameter($valueParameter, $value); break; case self::PARAMETER_LESS_THAN_OR_EQUAL: if (!is_numeric($value)) { $this->logger->notice('Invalid filter ignored', ['exception' => new InvalidArgumentException(sprintf('Invalid value for "[%s]", expected number', $operator))]); return; } $queryBuilder->andWhere(sprintf('%s.%s <= :%s', $alias, $field, $valueParameter))->setParameter($valueParameter, $value); break; } }
/** * Joins relations to eager load. * * @param QueryBuilder $queryBuilder * @param QueryNameGeneratorInterface $queryNameGenerator * @param string $resourceClass * @param bool $forceEager * @param string $parentAlias * @param array $propertyMetadataOptions * @param bool $wasLeftJoin if the relation containing the new one had a left join, we have to force the new one to left join too * @param int $joinCount the number of joins * * @throws RuntimeException when the max number of joins has been reached */ private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, bool $forceEager, string $parentAlias, array $propertyMetadataOptions = [], bool $wasLeftJoin = false, int &$joinCount = 0) { if ($joinCount > $this->maxJoins) { throw new RuntimeException('The total number of joined relations has exceeded the specified maximum. Raise the limit if necessary.'); } $entityManager = $queryBuilder->getEntityManager(); $classMetadata = $entityManager->getClassMetadata($resourceClass); foreach ($classMetadata->associationMappings as $association => $mapping) { try { $propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $association, $propertyMetadataOptions); } catch (PropertyNotFoundException $propertyNotFoundException) { //skip properties not found continue; } catch (ResourceClassNotFoundException $resourceClassNotFoundException) { //skip associations that are not resource classes continue; } if (false === $forceEager && ClassMetadataInfo::FETCH_EAGER !== $mapping['fetch']) { continue; } if (false === $propertyMetadata->isReadableLink() || false === $propertyMetadata->isReadable()) { continue; } $isNullable = $mapping['joinColumns'][0]['nullable'] ?? true; if (false !== $wasLeftJoin || true === $isNullable) { $method = 'leftJoin'; } else { $method = 'innerJoin'; } $associationAlias = $queryNameGenerator->generateJoinAlias($association); $queryBuilder->{$method}(sprintf('%s.%s', $parentAlias, $association), $associationAlias); ++$joinCount; try { $this->addSelect($queryBuilder, $mapping['targetEntity'], $associationAlias, $propertyMetadataOptions); } catch (ResourceClassNotFoundException $resourceClassNotFoundException) { continue; } $this->joinRelations($queryBuilder, $queryNameGenerator, $mapping['targetEntity'], $forceEager, $associationAlias, $propertyMetadataOptions, $method === 'leftJoin', $joinCount); } }
/** * Adds a join to the queryBuilder if none exists. * * @param QueryBuilder $queryBuilder * @param QueryNameGeneratorInterface $queryNameGenerator * @param string $alias * @param string $association the association field * * @return string the new association alias */ protected function addJoinOnce(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $association) : string { $join = $this->getExistingJoin($queryBuilder, $alias, $association); if (null === $join) { $associationAlias = $queryNameGenerator->generateJoinAlias($association); $queryBuilder->join(sprintf('%s.%s', $alias, $association), $associationAlias); } else { $associationAlias = $join->getAlias(); } return $associationAlias; }
/** * Adds where clause according to the strategy. * * @param string $strategy * @param QueryBuilder $queryBuilder * @param QueryNameGeneratorInterface $queryNameGenerator * @param string $alias * @param string $field * @param string $value * @param bool $caseSensitive * * @throws InvalidArgumentException If strategy does not exist */ protected function addWhereByStrategy(string $strategy, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $alias, string $field, string $value, bool $caseSensitive) { $wrapCase = $this->createWrapCase($caseSensitive); $valueParameter = $queryNameGenerator->generateParameterName($field); switch ($strategy) { case null: case self::STRATEGY_EXACT: $queryBuilder->andWhere(sprintf($wrapCase('%s.%s') . ' = ' . $wrapCase(':%s'), $alias, $field, $valueParameter))->setParameter($valueParameter, $value); break; case self::STRATEGY_PARTIAL: $queryBuilder->andWhere(sprintf($wrapCase('%s.%s') . ' LIKE ' . $wrapCase('CONCAT(\'%%\', :%s, \'%%\')'), $alias, $field, $valueParameter))->setParameter($valueParameter, $value); break; case self::STRATEGY_START: $queryBuilder->andWhere(sprintf($wrapCase('%s.%s') . ' LIKE ' . $wrapCase('CONCAT(:%s, \'%%\')'), $alias, $field, $valueParameter))->setParameter($valueParameter, $value); break; case self::STRATEGY_END: $queryBuilder->andWhere(sprintf($wrapCase('%s.%s') . ' LIKE ' . $wrapCase('CONCAT(\'%%\', :%s)'), $alias, $field, $valueParameter))->setParameter($valueParameter, $value); break; case self::STRATEGY_WORD_START: $queryBuilder->andWhere(sprintf($wrapCase('%1$s.%2$s') . ' LIKE ' . $wrapCase('CONCAT(:%3$s, \'%%\')') . ' OR ' . $wrapCase('%1$s.%2$s') . ' LIKE ' . $wrapCase('CONCAT(\'%% \', :%3$s, \'%%\')'), $alias, $field, $valueParameter))->setParameter($valueParameter, $value); break; default: throw new InvalidArgumentException(sprintf('strategy %s does not exist.', $strategy)); } }