131 $this->dataMapper = $this->objectManager->get(DataMapper::class);
171 $this->tablePropertyMap = [];
172 $this->tableAliasMap = [];
173 $this->unionTableAliasCache = [];
174 $this->tableName =
'';
177 $this->queryBuilder = clone $query->
getStatement()->getStatement();
187 if (!empty($wherePredicates)) {
188 $this->queryBuilder->andWhere($wherePredicates);
206 $className = $source->getNodeTypeName();
207 $tableName = $this->dataMapper->getDataMap($className)->getTableName();
210 $this->queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
220 ->select($tableAlias .
'.*')
224 } elseif ($source instanceof Qom\JoinInterface) {
225 $leftSource = $source->getLeft();
226 $leftTableName = $leftSource->getSelectorName();
228 $this->queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
229 ->getQueryBuilderForTable($leftTableName);
232 ->select($leftTableAlias .
'.*')
233 ->from($leftTableName, $leftTableAlias);
249 $constraint1 = $constraint->getConstraint1();
250 $constraint2 = $constraint->getConstraint2();
254 return $this->queryBuilder->expr()->andX(
261 if ($constraint instanceof Qom\OrInterface) {
262 $constraint1 = $constraint->getConstraint1();
263 $constraint2 = $constraint->getConstraint2();
264 if (($constraint1 instanceof Qom\ConstraintInterface)
265 && ($constraint2 instanceof Qom\ConstraintInterface)
267 return $this->queryBuilder->expr()->orX(
269 $this->parseConstraint($constraint->getConstraint2(), $source)
274 if ($constraint instanceof Qom\NotInterface) {
275 return ' NOT(' . $this->
parseConstraint($constraint->getConstraint(), $source) .
')';
277 if ($constraint instanceof Qom\ComparisonInterface) {
280 throw new \RuntimeException(
'not implemented', 1476199898);
290 protected function parseOrderings(array $orderings, Qom\SourceInterface $source)
292 foreach ($orderings as $propertyName => $order) {
294 throw new UnsupportedOrderException(
'Unsupported order encountered.', 1242816074);
298 if ($source instanceof Qom\SelectorInterface) {
299 $className = $source->getNodeTypeName();
300 $tableName = $this->dataMapper->convertClassNameToTableName($className);
301 $fullPropertyPath =
'';
302 while (strpos($propertyName,
'.') !==
false) {
305 } elseif ($source instanceof Qom\JoinInterface) {
306 $tableName = $source->getLeft()->getSelectorName();
308 $columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
310 $this->queryBuilder->addOrderBy(
$tableName .
'.' . $columnName, $order);
312 $this->queryBuilder->addOrderBy($columnName, $order);
325 foreach ($this->tableAliasMap as $tableAlias =>
$tableName) {
326 if ($index === 0 || !$this->configurationManager->isFeatureEnabled(
'consistentTranslationOverlayHandling')) {
331 $additionalWhereClauses = [];
335 if ($statement !==
'') {
336 $additionalWhereClauses[] = $statement;
338 if (!empty($additionalWhereClauses)) {
339 if (in_array($tableAlias, $this->unionTableAliasCache,
true)) {
340 $this->queryBuilder->andWhere(
341 $this->queryBuilder->expr()->orX(
342 $this->queryBuilder->expr()->andX(...$additionalWhereClauses),
343 $this->queryBuilder->expr()->isNull($tableAlias .
'.uid')
347 $this->queryBuilder->andWhere(...$additionalWhereClauses);
366 if ($comparison->getOperand2() ===
null) {
369 $value = $this->dataMapper->getPlainValue($comparison->getOperand2());
371 throw new \RuntimeException(
'Source is not of type "SelectorInterface"', 1395362539);
373 $className = $source->getNodeTypeName();
374 $tableName = $this->dataMapper->convertClassNameToTableName($className);
375 $operand1 = $comparison->getOperand1();
376 $propertyName = $operand1->getPropertyName();
377 $fullPropertyPath =
'';
378 while (strpos($propertyName,
'.') !==
false) {
381 $columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
382 $dataMap = $this->dataMapper->getDataMap($className);
383 $columnMap = $dataMap->getColumnMap($propertyName);
386 $relationTableName = $columnMap->getRelationTableName();
387 $queryBuilderForSubselect = $this->queryBuilder->getConnection()->createQueryBuilder();
388 $queryBuilderForSubselect
389 ->select($columnMap->getParentKeyFieldName())
390 ->from($relationTableName)
392 $queryBuilderForSubselect->expr()->eq(
393 $columnMap->getChildKeyFieldName(),
394 $this->queryBuilder->createNamedParameter($value)
398 if ($additionalWhereForMatchFields) {
399 $queryBuilderForSubselect->andWhere($additionalWhereForMatchFields);
402 return $this->queryBuilder->expr()->comparison(
403 $this->queryBuilder->quoteIdentifier(
$tableName .
'.uid'),
405 '(' . $queryBuilderForSubselect->getSQL() .
')'
409 $parentKeyFieldName = $columnMap->getParentKeyFieldName();
410 if (isset($parentKeyFieldName)) {
411 $childTableName = $columnMap->getChildTableName();
414 $queryBuilderForSubselect = $this->queryBuilder->getConnection()->createQueryBuilder();
415 $queryBuilderForSubselect
416 ->select($parentKeyFieldName)
417 ->from($childTableName)
419 $queryBuilderForSubselect->expr()->eq(
426 return $this->queryBuilder->expr()->eq(
428 '(' . $queryBuilderForSubselect->getSQL() .
')'
431 return $this->queryBuilder->expr()->inSet(
433 $this->queryBuilder->quote($value)
436 throw new RepositoryException(
'Unsupported or non-existing property name "' . $propertyName .
'" used in relation matching.', 1327065745);
452 $value = $comparison->getOperand2();
453 $fieldName = $this->
parseOperand($comparison->getOperand1(), $source);
455 $exprBuilder = $this->queryBuilder->expr();
456 switch ($comparison->getOperator()) {
460 foreach ($value as $singleValue) {
461 $plainValue = $this->dataMapper->getPlainValue($singleValue);
462 if ($plainValue !==
null) {
469 'The IN operator needs a non-empty value list to compare against. ' .
470 'The given value list is empty.',
474 $expr = $exprBuilder->comparison($fieldName,
'IN',
'(' . implode(
', ', $plainValues) .
')');
477 if ($value ===
null) {
478 $expr = $fieldName .
' IS NULL';
481 $expr = $exprBuilder->comparison($fieldName, $exprBuilder::EQ, $placeHolder);
485 $expr = $fieldName .
' IS NULL';
488 if ($value ===
null) {
489 $expr = $fieldName .
' IS NOT NULL';
492 $expr = $exprBuilder->comparison($fieldName, $exprBuilder::NEQ, $placeHolder);
496 $expr = $fieldName .
' IS NOT NULL';
500 $expr = $exprBuilder->comparison($fieldName, $exprBuilder::LT, $placeHolder);
504 $expr = $exprBuilder->comparison($fieldName, $exprBuilder::LTE, $placeHolder);
508 $expr = $exprBuilder->comparison($fieldName, $exprBuilder::GT, $placeHolder);
512 $expr = $exprBuilder->comparison($fieldName, $exprBuilder::GTE, $placeHolder);
516 $expr = $exprBuilder->comparison($fieldName,
'LIKE', $placeHolder);
520 'Unsupported operator encountered.',
537 $parameterType = gettype($value);
538 switch ($parameterType) {
540 return \PDO::PARAM_INT;
542 return \PDO::PARAM_STR;
544 throw new \InvalidArgumentException(
545 'Unsupported parameter type encountered. Expected integer or string, ' . $parameterType .
' given.',
563 $consistentHandlingEnabled = $this->configurationManager->isFeatureEnabled(
'consistentTranslationOverlayHandling');
564 if ($consistentHandlingEnabled
567 && $value->_getProperty(
'_localizedUid') > 0
569 $plainValue = (int)$value->_getProperty(
'_localizedUid');
571 $plainValue = $this->dataMapper->getPlainValue($value);
574 $placeholder = $this->queryBuilder->createNamedParameter($plainValue, $parameterType);
588 $constraintSQL =
'LOWER(' . $this->
parseOperand($operand->getOperand(), $source) .
')';
589 } elseif ($operand instanceof Qom\UpperCaseInterface) {
590 $constraintSQL =
'UPPER(' . $this->
parseOperand($operand->getOperand(), $source) .
')';
591 } elseif ($operand instanceof Qom\PropertyValueInterface) {
592 $propertyName = $operand->getPropertyName();
594 if ($source instanceof Qom\SelectorInterface) {
595 $className = $source->getNodeTypeName();
596 $tableName = $this->dataMapper->convertClassNameToTableName($className);
597 $fullPropertyPath =
'';
598 while (strpos($propertyName,
'.') !==
false) {
601 } elseif ($source instanceof Qom\JoinInterface) {
602 $tableName = $source->getJoinCondition()->getSelector1Name();
604 $columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
606 $constraintSQL = $this->queryBuilder->getConnection()->quoteIdentifier($constraintSQL);
608 throw new \InvalidArgumentException(
'Given operand has invalid type "' . get_class($operand) .
'".', 1395710211);
610 return $constraintSQL;
620 if ($className !==
null) {
621 $dataMap = $this->dataMapper->getDataMap($className);
622 if ($dataMap->getRecordTypeColumnName() !==
null) {
624 if ($dataMap->getRecordType() !==
null) {
625 $recordTypes[] = $dataMap->getRecordType();
627 foreach ($dataMap->getSubclasses() as $subclassName) {
628 $subclassDataMap = $this->dataMapper->getDataMap($subclassName);
629 if ($subclassDataMap->getRecordType() !==
null) {
630 $recordTypes[] = $subclassDataMap->getRecordType();
633 if (!empty($recordTypes)) {
634 $recordTypeStatements = [];
635 foreach ($recordTypes as $recordType) {
637 $recordTypeStatements[] = $this->queryBuilder->expr()->eq(
638 $tableName .
'.' . $dataMap->getRecordTypeColumnName(),
639 $this->queryBuilder->createNamedParameter($recordType)
642 $this->queryBuilder->andWhere(
643 $this->queryBuilder->expr()->orX(...$recordTypeStatements)
662 $additionalWhereForMatchFields = [];
663 $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
664 if (is_array($relationTableMatchFields) && !empty($relationTableMatchFields)) {
665 foreach ($relationTableMatchFields as $fieldName => $value) {
666 $additionalWhereForMatchFields[] = $exprBuilder->eq($childTableAlias .
'.' . $fieldName, $this->queryBuilder->createNamedParameter($value));
670 if (isset($parentTable)) {
671 $parentTableFieldName = $columnMap->getParentTableFieldName();
672 if (!empty($parentTableFieldName)) {
673 $additionalWhereForMatchFields[] = $exprBuilder->eq($childTableAlias .
'.' . $parentTableFieldName, $this->queryBuilder->createNamedParameter($parentTable));
677 if (!empty($additionalWhereForMatchFields)) {
678 return $exprBuilder->andX(...$additionalWhereForMatchFields);
695 if ($this->configurationManager->isFeatureEnabled(
'consistentTranslationOverlayHandling')) {
701 if (!empty($systemLanguageStatement)) {
702 $whereClause[] = $systemLanguageStatement;
708 if (!empty($pageIdStatement)) {
709 $whereClause[] = $pageIdStatement;
731 if ($this->environmentService->isEnvironmentInFrontendMode()) {
737 if (!empty($statement)) {
739 $statement = strtolower(substr($statement, 1, 3)) ===
'and' ? substr($statement, 5) : $statement;
758 if ($ignoreEnableFields && !$includeDeleted) {
759 if (!empty($enableFieldsToBeIgnored)) {
765 } elseif (!$ignoreEnableFields && !$includeDeleted) {
767 } elseif (!$ignoreEnableFields && $includeDeleted) {
768 throw new InconsistentQuerySettingsException(
'Query setting "ignoreEnableFields=FALSE" can not be used together with "includeDeleted=TRUE" in frontend context.', 1460975922);
784 if (!$ignoreEnableFields) {
815 if ($mode ===
'strict') {
816 $queryBuilderForSubselect = $this->queryBuilder->getConnection()->createQueryBuilder();
818 $queryBuilderForSubselect
822 $queryBuilderForSubselect->expr()->andX(
827 return $this->queryBuilder->expr()->orX(
828 $this->queryBuilder->expr()->eq($tableAlias .
'.' . $languageField, -1),
829 $this->queryBuilder->expr()->andX(
830 $this->queryBuilder->expr()->eq($tableAlias .
'.' . $languageField, (
int)$querySettings->
getLanguageUid()),
831 $this->queryBuilder->expr()->eq($tableAlias .
'.' .
$GLOBALS[
'TCA'][
$tableName][
'ctrl'][
'transOrigPointerField'], 0)
833 $this->queryBuilder->expr()->andX(
834 $this->queryBuilder->expr()->eq($tableAlias .
'.' . $languageField, 0),
835 $this->queryBuilder->expr()->in(
836 $tableAlias .
'.uid',
837 $queryBuilderForSubselect->getSQL()
842 $queryBuilderForSubselect = $this->queryBuilder->getConnection()->createQueryBuilder();
844 $queryBuilderForSubselect
848 $queryBuilderForSubselect->expr()->andX(
853 return $this->queryBuilder->expr()->orX(
854 $this->queryBuilder->expr()->in($tableAlias .
'.' . $languageField, [(
int)$querySettings->
getLanguageUid(), -1]),
855 $this->queryBuilder->expr()->andX(
856 $this->queryBuilder->expr()->eq($tableAlias .
'.' . $languageField, 0),
857 $this->queryBuilder->expr()->notIn(
858 $tableAlias .
'.uid',
859 $queryBuilderForSubselect->getSQL()
864 return $this->queryBuilder->expr()->in(
865 $tableAlias .
'.' . $languageField,
894 return $this->queryBuilder->expr()->in(
895 $tableAlias .
'.' . $languageField,
902 return $this->queryBuilder->expr()->in(
903 $tableAlias .
'.' . $languageField,
908 $defLangTableAlias = $tableAlias .
'_dl';
909 $defaultLanguageRecordsSubSelect = $this->queryBuilder->getConnection()->createQueryBuilder();
910 $defaultLanguageRecordsSubSelect
911 ->select($defLangTableAlias .
'.uid')
914 $defaultLanguageRecordsSubSelect->expr()->andX(
915 $defaultLanguageRecordsSubSelect->expr()->eq($defLangTableAlias .
'.' . $transOrigPointerField, 0),
916 $defaultLanguageRecordsSubSelect->expr()->eq($defLangTableAlias .
'.' . $languageField, 0)
922 $andConditions[] = $this->queryBuilder->expr()->eq($tableAlias .
'.' . $languageField, -1);
924 $andConditions[] = $this->queryBuilder->expr()->andX(
925 $this->queryBuilder->expr()->eq($tableAlias .
'.' . $languageField, (
int)$querySettings->
getLanguageUid()),
926 $this->queryBuilder->expr()->in(
927 $tableAlias .
'.' . $transOrigPointerField,
928 $defaultLanguageRecordsSubSelect->getSQL()
931 if ($mode !==
'hideNonTranslated') {
935 $translatedOnlyTableAlias = $tableAlias .
'_to';
936 $queryBuilderForSubselect = $this->queryBuilder->getConnection()->createQueryBuilder();
937 $queryBuilderForSubselect
938 ->select($translatedOnlyTableAlias .
'.' . $transOrigPointerField)
941 $queryBuilderForSubselect->expr()->andX(
942 $queryBuilderForSubselect->expr()->gt($translatedOnlyTableAlias .
'.' . $transOrigPointerField, 0),
943 $queryBuilderForSubselect->expr()->eq($translatedOnlyTableAlias .
'.' . $languageField, (
int)$querySettings->
getLanguageUid())
947 $andConditions[] = $this->queryBuilder->expr()->andX(
948 $this->queryBuilder->expr()->eq($tableAlias .
'.' . $languageField, 0),
949 $this->queryBuilder->expr()->notIn(
950 $tableAlias .
'.uid',
951 $queryBuilderForSubselect->getSQL()
956 return $this->queryBuilder->expr()->orX(...$andConditions);
975 switch ($rootLevel) {
978 $storagePageIds = [0];
982 if (empty($storagePageIds)) {
983 $storagePageIds = [0];
985 $storagePageIds[] = 0;
990 if (empty($storagePageIds)) {
991 throw new InconsistentQuerySettingsException(
'Missing storage page ids.', 1365779762);
998 $storagePageIds = array_map(
'intval', $storagePageIds);
999 if (count($storagePageIds) === 1) {
1000 return $this->queryBuilder->expr()->eq($tableAlias .
'.pid', reset($storagePageIds));
1002 return $this->queryBuilder->expr()->in($tableAlias .
'.pid', $storagePageIds);
1011 protected function parseJoin(Qom\JoinInterface $join, $leftTableAlias)
1013 $leftSource = $join->getLeft();
1014 $leftClassName = $leftSource->getNodeTypeName();
1016 $rightSource = $join->getRight();
1017 if ($rightSource instanceof Qom\JoinInterface) {
1018 $left = $rightSource->getLeft();
1019 $rightClassName = $left->getNodeTypeName();
1020 $rightTableName = $left->getSelectorName();
1022 $rightClassName = $rightSource->getNodeTypeName();
1023 $rightTableName = $rightSource->getSelectorName();
1024 $this->queryBuilder->addSelect($rightTableName .
'.*');
1028 $joinCondition = $join->getJoinCondition();
1029 $joinConditionExpression =
null;
1030 if ($joinCondition instanceof Qom\EquiJoinCondition) {
1031 $column1Name = $this->dataMapper->convertPropertyNameToColumnName($joinCondition->getProperty1Name(), $leftClassName);
1032 $column2Name = $this->dataMapper->convertPropertyNameToColumnName($joinCondition->getProperty2Name(), $rightClassName);
1034 $joinConditionExpression = $this->queryBuilder->expr()->eq(
1035 $leftTableAlias .
'.' . $column1Name,
1036 $this->queryBuilder->quoteIdentifier($rightTableAlias .
'.' . $column2Name)
1039 $this->queryBuilder->leftJoin($leftTableAlias, $rightTableName, $rightTableAlias, $joinConditionExpression);
1040 if ($rightSource instanceof Qom\JoinInterface) {
1041 $this->
parseJoin($rightSource, $rightTableAlias);
1055 if (isset($fullPropertyPath) && isset($this->tablePropertyMap[$fullPropertyPath])) {
1056 return $this->tablePropertyMap[$fullPropertyPath];
1061 while (isset($this->tableAliasMap[$alias])) {
1068 if (isset($fullPropertyPath)) {
1069 $this->tablePropertyMap[$fullPropertyPath] = $alias;
1089 $explodedPropertyPath = explode(
'.', $propertyPath, 2);
1090 $propertyName = $explodedPropertyPath[0];
1091 $columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
1092 $realTableName = $this->dataMapper->convertClassNameToTableName($className);
1093 $tableName = $this->tablePropertyMap[$fullPropertyPath] ?? $realTableName;
1094 $columnMap = $this->dataMapper->getDataMap($className)->getColumnMap($propertyName);
1096 if ($columnMap ===
null) {
1097 throw new MissingColumnMapException(
'The ColumnMap for property "' . $propertyName .
'" of class "' . $className .
'" is missing.', 1355142232);
1100 $parentKeyFieldName = $columnMap->getParentKeyFieldName();
1101 $childTableName = $columnMap->getChildTableName();
1103 if ($childTableName ===
null) {
1104 throw new InvalidRelationConfigurationException(
'The relation information for property "' . $propertyName .
'" of class "' . $className .
'" is missing.', 1353170925);
1107 $fullPropertyPath .= ($fullPropertyPath ===
'') ? $propertyName :
'.' . $propertyName;
1108 $childTableAlias = $this->
getUniqueAlias($childTableName, $fullPropertyPath);
1111 if (in_array($childTableAlias, $this->unionTableAliasCache,
true)) {
1112 $propertyPath = $explodedPropertyPath[1];
1114 $className = $this->dataMapper->getType($className, $propertyName);
1119 if (isset($parentKeyFieldName)) {
1121 $joinConditionExpression = $this->queryBuilder->expr()->eq(
1123 $this->queryBuilder->quoteIdentifier($childTableAlias .
'.' . $parentKeyFieldName)
1126 $joinConditionExpression = $this->queryBuilder->expr()->eq(
1128 $this->queryBuilder->quoteIdentifier($childTableAlias .
'.uid')
1131 $this->queryBuilder->leftJoin(
$tableName, $childTableName, $childTableAlias, $joinConditionExpression);
1132 $this->unionTableAliasCache[] = $childTableAlias;
1133 $this->queryBuilder->andWhere(
1138 if (isset($parentKeyFieldName)) {
1139 $joinConditionExpression = $this->queryBuilder->expr()->eq(
1141 $this->queryBuilder->quoteIdentifier($childTableAlias .
'.' . $parentKeyFieldName)
1144 $joinConditionExpression = $this->queryBuilder->expr()->inSet(
1146 $this->queryBuilder->quoteIdentifier($childTableAlias .
'.uid'),
1150 $this->queryBuilder->leftJoin(
$tableName, $childTableName, $childTableAlias, $joinConditionExpression);
1151 $this->unionTableAliasCache[] = $childTableAlias;
1152 $this->queryBuilder->andWhere(
1155 $this->suggestDistinctQuery =
true;
1157 $relationTableName = $columnMap->getRelationTableName();
1158 $relationTableAlias = $this->
getUniqueAlias($relationTableName, $fullPropertyPath .
'_mm');
1160 $joinConditionExpression = $this->queryBuilder->expr()->andX(
1161 $this->queryBuilder->expr()->eq(
1163 $this->queryBuilder->quoteIdentifier(
1164 $relationTableAlias .
'.' . $columnMap->getParentKeyFieldName()
1167 $this->getAdditionalMatchFieldsStatement($this->queryBuilder->expr(), $columnMap, $relationTableAlias, $realTableName)
1169 $this->queryBuilder->leftJoin(
$tableName, $relationTableName, $relationTableAlias, $joinConditionExpression);
1170 $joinConditionExpression = $this->queryBuilder->expr()->eq(
1171 $relationTableAlias .
'.' . $columnMap->getChildKeyFieldName(),
1172 $this->queryBuilder->quoteIdentifier($childTableAlias .
'.uid')
1174 $this->queryBuilder->leftJoin($relationTableAlias, $childTableName, $childTableAlias, $joinConditionExpression);
1175 $this->unionTableAliasCache[] = $childTableAlias;
1176 $this->suggestDistinctQuery =
true;
1178 throw new Exception(
'Could not determine type of relation.', 1252502725);
1180 $propertyPath = $explodedPropertyPath[1];
1182 $className = $this->dataMapper->getType($className, $propertyName);
1198 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable(
$tableName);
1199 $quotedTableName = $connection->quoteIdentifier(
$tableName);
1200 $quotedTableAlias = $connection->quoteIdentifier($tableAlias);
1201 $statement = str_replace(
1203 [$tableAlias .
'.', $quotedTableAlias .
'.'],
1216 if (!$this->pageRepository instanceof PageRepository) {
1217 $this->pageRepository = GeneralUtility::makeInstance(PageRepository::class);