TYPO3 CMS  TYPO3_7-6
Typo3DbQueryParser.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
32 
37 {
43  protected $databaseHandle;
44 
48  protected $dataMapper;
49 
55  protected $pageRepository;
56 
61 
66  {
67  $this->dataMapper = $dataMapper;
68  }
69 
74  {
75  $this->environmentService = $environmentService;
76  }
77 
86  protected $tablePropertyMap = [];
87 
91  public function __construct()
92  {
93  $this->databaseHandle = $GLOBALS['TYPO3_DB'];
94  }
95 
102  public function preparseQuery(QueryInterface $query)
103  {
104  list($parameters, $operators) = $this->preparseComparison($query->getConstraint());
105  $hashPartials = [
106  $query->getQuerySettings(),
107  $query->getSource(),
108  array_keys($parameters),
109  $operators,
110  $query->getOrderings(),
111  ];
112  $hash = md5(serialize($hashPartials));
113 
114  return [$hash, $parameters];
115  }
116 
130  protected function preparseComparison($comparison, $qomPath = '')
131  {
132  $parameters = [];
133  $operators = [];
134  $objectsToParse = [];
135 
136  $delimiter = '';
137  if ($comparison instanceof Qom\AndInterface) {
138  $delimiter = 'AND';
139  $objectsToParse = [$comparison->getConstraint1(), $comparison->getConstraint2()];
140  } elseif ($comparison instanceof Qom\OrInterface) {
141  $delimiter = 'OR';
142  $objectsToParse = [$comparison->getConstraint1(), $comparison->getConstraint2()];
143  } elseif ($comparison instanceof Qom\NotInterface) {
144  $delimiter = 'NOT';
145  $objectsToParse = [$comparison->getConstraint()];
146  } elseif ($comparison instanceof Qom\ComparisonInterface) {
147  $operand1 = $comparison->getOperand1();
148  $parameterIdentifier = $this->normalizeParameterIdentifier($qomPath . $operand1->getPropertyName());
149  $comparison->setParameterIdentifier($parameterIdentifier);
150  $operator = $comparison->getOperator();
151  $operand2 = $comparison->getOperand2();
152  if ($operator === QueryInterface::OPERATOR_IN) {
153  $items = [];
154  foreach ($operand2 as $value) {
155  $value = $this->dataMapper->getPlainValue($value);
156  if ($value !== null) {
157  $items[] = $value;
158  }
159  }
160  $parameters[$parameterIdentifier] = $items;
161  } else {
162  $parameters[$parameterIdentifier] = $operand2;
163  }
164  $operators[] = $operator;
165  } elseif (!is_object($comparison)) {
166  $parameters = [[], $comparison];
167  return [$parameters, $operators];
168  } else {
169  throw new \Exception('Can not hash Query Component "' . get_class($comparison) . '".', 1392840462);
170  }
171 
172  $childObjectIterator = 0;
173  foreach ($objectsToParse as $objectToParse) {
174  list($preparsedParameters, $preparsedOperators) = $this->preparseComparison($objectToParse, $qomPath . $delimiter . $childObjectIterator++);
175  if (!empty($preparsedParameters)) {
176  $parameters = array_merge($parameters, $preparsedParameters);
177  }
178  if (!empty($preparsedOperators)) {
179  $operators = array_merge($operators, $preparsedOperators);
180  }
181  }
182 
183  return [$parameters, $operators];
184  }
185 
193  public function normalizeParameterIdentifier($identifier)
194  {
195  return ':' . preg_replace('/[^A-Za-z0-9]/', '', $identifier);
196  }
197 
204  public function parseQuery(QueryInterface $query)
205  {
206  $this->tablePropertyMap = [];
207  $sql = [];
208  $sql['keywords'] = [];
209  $sql['tables'] = [];
210  $sql['unions'] = [];
211  $sql['fields'] = [];
212  $sql['where'] = [];
213  $sql['additionalWhereClause'] = [];
214  $sql['orderings'] = [];
215  $sql['limit'] = ((int)$query->getLimit() ?: null);
216  $sql['offset'] = ((int)$query->getOffset() ?: null);
217  $sql['tableAliasMap'] = [];
218  $source = $query->getSource();
219  $this->parseSource($source, $sql);
220  $this->parseConstraint($query->getConstraint(), $source, $sql);
221  $this->parseOrderings($query->getOrderings(), $source, $sql);
222 
223  foreach ($sql['tableAliasMap'] as $tableAlias => $tableName) {
224  $additionalWhereClause = $this->getAdditionalWhereClause($query->getQuerySettings(), $tableName, $tableAlias);
225  if ($additionalWhereClause !== '') {
226  $additionalWhereClause = $this->addNullConditionToStatementIfRequired($sql, $additionalWhereClause, $tableAlias);
227  $sql['additionalWhereClause'][] = $additionalWhereClause;
228  }
229  }
230 
231  return $sql;
232  }
233 
243  public function addDynamicQueryParts(QuerySettingsInterface $querySettings, array &$sql)
244  {
245  if (!isset($sql['additionalWhereClause'])) {
246  throw new \InvalidArgumentException('Invalid statement given.', 1399512421);
247  }
248  foreach ($sql['tableAliasMap'] as $tableAlias => $tableName) {
249  $statement = $this->getVisibilityConstraintStatement($querySettings, $tableName, $tableAlias);
250  if ($statement !== '') {
251  $statement = $this->addNullConditionToStatementIfRequired($sql, $statement, $tableAlias);
252  $sql['additionalWhereClause'][] = $statement;
253  }
254  }
255  }
256 
268  protected function addNullConditionToStatementIfRequired(array $sql, $statement, $tableAlias)
269  {
270  if (isset($sql['unions'][$tableAlias])) {
271  $statement = '((' . $statement . ') OR ' . $tableAlias . '.uid' . ' IS NULL)';
272  }
273 
274  return $statement;
275  }
276 
284  protected function parseSource(Qom\SourceInterface $source, array &$sql)
285  {
286  if ($source instanceof Qom\SelectorInterface) {
287  $className = $source->getNodeTypeName();
288  $tableName = $this->dataMapper->getDataMap($className)->getTableName();
289  $this->addRecordTypeConstraint($className, $sql);
290  $tableName = $this->getUniqueAlias($sql, $tableName);
291  $sql['fields'][$tableName] = $tableName . '.*';
292  $sql['tables'][$tableName] = $tableName;
293  } elseif ($source instanceof Qom\JoinInterface) {
294  $this->parseJoin($source, $sql);
295  }
296  }
297 
306  protected function parseConstraint(Qom\ConstraintInterface $constraint = null, Qom\SourceInterface $source, array &$sql)
307  {
308  if ($constraint instanceof Qom\AndInterface) {
309  $sql['where'][] = '(';
310  $this->parseConstraint($constraint->getConstraint1(), $source, $sql);
311  $sql['where'][] = ' AND ';
312  $this->parseConstraint($constraint->getConstraint2(), $source, $sql);
313  $sql['where'][] = ')';
314  } elseif ($constraint instanceof Qom\OrInterface) {
315  $sql['where'][] = '(';
316  $this->parseConstraint($constraint->getConstraint1(), $source, $sql);
317  $sql['where'][] = ' OR ';
318  $this->parseConstraint($constraint->getConstraint2(), $source, $sql);
319  $sql['where'][] = ')';
320  } elseif ($constraint instanceof Qom\NotInterface) {
321  $sql['where'][] = 'NOT (';
322  $this->parseConstraint($constraint->getConstraint(), $source, $sql);
323  $sql['where'][] = ')';
324  } elseif ($constraint instanceof Qom\ComparisonInterface) {
325  $this->parseComparison($constraint, $source, $sql);
326  }
327  }
328 
338  protected function parseOrderings(array $orderings, Qom\SourceInterface $source, array &$sql)
339  {
340  foreach ($orderings as $propertyName => $order) {
341  switch ($order) {
343  $order = 'ASC';
344  break;
346  $order = 'DESC';
347  break;
348  default:
349  throw new UnsupportedOrderException('Unsupported order encountered.', 1242816074);
350  }
351  $className = '';
352  $tableName = '';
353  if ($source instanceof Qom\SelectorInterface) {
354  $className = $source->getNodeTypeName();
355  $tableName = $this->dataMapper->convertClassNameToTableName($className);
356  $fullPropertyPath = '';
357  while (strpos($propertyName, '.') !== false) {
358  $this->addUnionStatement($className, $tableName, $propertyName, $sql, $fullPropertyPath);
359  }
360  } elseif ($source instanceof Qom\JoinInterface) {
361  $tableName = $source->getLeft()->getSelectorName();
362  }
363  $columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
364  if ($tableName !== '') {
365  $sql['orderings'][] = $tableName . '.' . $columnName . ' ' . $order;
366  } else {
367  $sql['orderings'][] = $columnName . ' ' . $order;
368  }
369  }
370  }
371 
382  protected function parseComparison(Qom\ComparisonInterface $comparison, Qom\SourceInterface $source, array &$sql)
383  {
384  $parameterIdentifier = $this->normalizeParameterIdentifier($comparison->getParameterIdentifier());
385 
386  $operator = $comparison->getOperator();
387  $operand2 = $comparison->getOperand2();
388  if ($operator === QueryInterface::OPERATOR_IN) {
389  $hasValue = false;
390  foreach ($operand2 as $value) {
391  $value = $this->dataMapper->getPlainValue($value);
392  if ($value !== null) {
393  $parameters[] = $value;
394  $hasValue = true;
395  }
396  }
397  if ($hasValue === false) {
398  $sql['where'][] = '1<>1';
399  } else {
400  $this->parseDynamicOperand($comparison, $source, $sql);
401  }
402  } elseif ($operator === QueryInterface::OPERATOR_CONTAINS) {
403  if ($operand2 === null) {
404  $sql['where'][] = '1<>1';
405  } else {
406  if (!$source instanceof Qom\SelectorInterface) {
407  throw new \RuntimeException('Source is not of type "SelectorInterface"', 1395362539);
408  }
409  $className = $source->getNodeTypeName();
410  $tableName = $this->dataMapper->convertClassNameToTableName($className);
411  $operand1 = $comparison->getOperand1();
412  $propertyName = $operand1->getPropertyName();
413  $fullPropertyPath = '';
414  while (strpos($propertyName, '.') !== false) {
415  $this->addUnionStatement($className, $tableName, $propertyName, $sql, $fullPropertyPath);
416  }
417  $columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
418  $dataMap = $this->dataMapper->getDataMap($className);
419  $columnMap = $dataMap->getColumnMap($propertyName);
420  $typeOfRelation = $columnMap instanceof ColumnMap ? $columnMap->getTypeOfRelation() : null;
421  if ($typeOfRelation === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
422  $relationTableName = $columnMap->getRelationTableName();
423  $additionalWhereForMatchFields = $this->getAdditionalMatchFieldsStatement($columnMap, $relationTableName, $relationTableName);
424  $sql['where'][] = $tableName . '.uid IN (SELECT ' . $columnMap->getParentKeyFieldName() . ' FROM ' . $relationTableName . ' WHERE ' . $columnMap->getChildKeyFieldName() . '=' . $parameterIdentifier . $additionalWhereForMatchFields . ')';
425  } elseif ($typeOfRelation === ColumnMap::RELATION_HAS_MANY) {
426  $parentKeyFieldName = $columnMap->getParentKeyFieldName();
427  if (isset($parentKeyFieldName)) {
428  $childTableName = $columnMap->getChildTableName();
429  $sql['where'][] = $tableName . '.uid=(SELECT ' . $childTableName . '.' . $parentKeyFieldName . ' FROM ' . $childTableName . ' WHERE ' . $childTableName . '.uid=' . $parameterIdentifier . ')';
430  } else {
431  $sql['where'][] = 'FIND_IN_SET(' . $parameterIdentifier . ', ' . $tableName . '.' . $columnName . ')';
432  }
433  } else {
434  throw new RepositoryException('Unsupported or non-existing property name "' . $propertyName . '" used in relation matching.', 1327065745);
435  }
436  }
437  } else {
438  $this->parseDynamicOperand($comparison, $source, $sql);
439  }
440  }
441 
450  protected function parseDynamicOperand(Qom\ComparisonInterface $comparison, Qom\SourceInterface $source, array &$sql)
451  {
452  $operator = $this->resolveOperator($comparison->getOperator());
453  $operand = $comparison->getOperand1();
454 
455  $constraintSQL = $this->parseOperand($operand, $source, $sql) . ' ' . $operator . ' ';
456 
457  $parameterIdentifier = $this->normalizeParameterIdentifier($comparison->getParameterIdentifier());
458  if ($operator === 'IN') {
459  $parameterIdentifier = '(' . $parameterIdentifier . ')';
460  }
461  $constraintSQL .= $parameterIdentifier;
462 
463  $sql['where'][] = $constraintSQL;
464  }
465 
473  protected function parseOperand(Qom\DynamicOperandInterface $operand, Qom\SourceInterface $source, array &$sql)
474  {
475  if ($operand instanceof Qom\LowerCaseInterface) {
476  $constraintSQL = 'LOWER(' . $this->parseOperand($operand->getOperand(), $source, $sql) . ')';
477  } elseif ($operand instanceof Qom\UpperCaseInterface) {
478  $constraintSQL = 'UPPER(' . $this->parseOperand($operand->getOperand(), $source, $sql) . ')';
479  } elseif ($operand instanceof Qom\PropertyValueInterface) {
480  $propertyName = $operand->getPropertyName();
481  $className = '';
482  if ($source instanceof Qom\SelectorInterface) {
483  // @todo Only necessary to differ from Join
484  $className = $source->getNodeTypeName();
485  $tableName = $this->dataMapper->convertClassNameToTableName($className);
486  $fullPropertyPath = '';
487  while (strpos($propertyName, '.') !== false) {
488  $this->addUnionStatement($className, $tableName, $propertyName, $sql, $fullPropertyPath);
489  }
490  } elseif ($source instanceof Qom\JoinInterface) {
491  $tableName = $source->getJoinCondition()->getSelector1Name();
492  }
493  $columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
494  $constraintSQL = (!empty($tableName) ? $tableName . '.' : '') . $columnName;
495  } else {
496  throw new \InvalidArgumentException('Given operand has invalid type "' . get_class($operand) . '".', 1395710211);
497  }
498  return $constraintSQL;
499  }
500 
508  protected function addRecordTypeConstraint($className, &$sql)
509  {
510  if ($className !== null) {
511  $dataMap = $this->dataMapper->getDataMap($className);
512  if ($dataMap->getRecordTypeColumnName() !== null) {
513  $recordTypes = [];
514  if ($dataMap->getRecordType() !== null) {
515  $recordTypes[] = $dataMap->getRecordType();
516  }
517  foreach ($dataMap->getSubclasses() as $subclassName) {
518  $subclassDataMap = $this->dataMapper->getDataMap($subclassName);
519  if ($subclassDataMap->getRecordType() !== null) {
520  $recordTypes[] = $subclassDataMap->getRecordType();
521  }
522  }
523  if (!empty($recordTypes)) {
524  $recordTypeStatements = [];
525  foreach ($recordTypes as $recordType) {
526  $tableName = $dataMap->getTableName();
527  $recordTypeStatements[] = $tableName . '.' . $dataMap->getRecordTypeColumnName() . '=' . $this->databaseHandle->fullQuoteStr($recordType, $tableName);
528  }
529  $sql['additionalWhereClause'][] = '(' . implode(' OR ', $recordTypeStatements) . ')';
530  }
531  }
532  }
533  }
534 
545  protected function getAdditionalMatchFieldsStatement($columnMap, $childTableName, $childTableAlias, $parentTable = null)
546  {
547  $additionalWhereForMatchFields = '';
548 
549  $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
550  if (is_array($relationTableMatchFields) && !empty($relationTableMatchFields)) {
551  $additionalWhere = [];
552  foreach ($relationTableMatchFields as $fieldName => $value) {
553  $additionalWhere[] = $childTableAlias . '.' . $fieldName . ' = ' . $this->databaseHandle->fullQuoteStr($value, $childTableName);
554  }
555  $additionalWhereForMatchFields .= ' AND ' . implode(' AND ', $additionalWhere);
556  }
557 
558  if (isset($parentTable)) {
559  $parentTableFieldName = $columnMap->getParentTableFieldName();
560  if (isset($parentTableFieldName) && $parentTableFieldName !== '') {
561  $additionalWhereForMatchFields .= ' AND ' . $childTableAlias . '.' . $parentTableFieldName . ' = ' . $this->databaseHandle->fullQuoteStr($parentTable, $childTableAlias);
562  }
563  }
564 
565  return $additionalWhereForMatchFields;
566  }
567 
576  protected function getAdditionalWhereClause(QuerySettingsInterface $querySettings, $tableName, $tableAlias = null)
577  {
578  $sysLanguageStatement = '';
579  if ($querySettings->getRespectSysLanguage()) {
580  $sysLanguageStatement = $this->getSysLanguageStatement($tableName, $tableAlias, $querySettings);
581  }
582 
583  $pageIdStatement = '';
584  if ($querySettings->getRespectStoragePage()) {
585  $pageIdStatement = $this->getPageIdStatement($tableName, $tableAlias, $querySettings->getStoragePageIds());
586  }
587 
588  if ($sysLanguageStatement !== '' && $pageIdStatement !== '') {
589  $whereClause = $sysLanguageStatement . ' AND ' . $pageIdStatement;
590  } elseif ($sysLanguageStatement !== '') {
591  $whereClause = $sysLanguageStatement;
592  } else {
593  $whereClause = $pageIdStatement;
594  }
595 
596  return $whereClause;
597  }
598 
607  protected function getVisibilityConstraintStatement(QuerySettingsInterface $querySettings, $tableName, $tableAlias)
608  {
609  $statement = '';
610  if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
611  $ignoreEnableFields = $querySettings->getIgnoreEnableFields();
612  $enableFieldsToBeIgnored = $querySettings->getEnableFieldsToBeIgnored();
613  $includeDeleted = $querySettings->getIncludeDeleted();
614  if ($this->environmentService->isEnvironmentInFrontendMode()) {
615  $statement .= $this->getFrontendConstraintStatement($tableName, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted);
616  } else {
617  // TYPO3_MODE === 'BE'
618  $statement .= $this->getBackendConstraintStatement($tableName, $ignoreEnableFields, $includeDeleted);
619  }
620  if (!empty($statement)) {
621  $statement = $this->replaceTableNameWithAlias($statement, $tableName, $tableAlias);
622  $statement = strtolower(substr($statement, 1, 3)) === 'and' ? substr($statement, 5) : $statement;
623  }
624  }
625  return $statement;
626  }
627 
638  protected function getFrontendConstraintStatement($tableName, $ignoreEnableFields, array $enableFieldsToBeIgnored = [], $includeDeleted)
639  {
640  $statement = '';
641  if ($ignoreEnableFields && !$includeDeleted) {
642  if (!empty($enableFieldsToBeIgnored)) {
643  // array_combine() is necessary because of the way \TYPO3\CMS\Frontend\Page\PageRepository::enableFields() is implemented
644  $statement .= $this->getPageRepository()->enableFields($tableName, -1, array_combine($enableFieldsToBeIgnored, $enableFieldsToBeIgnored));
645  } else {
646  $statement .= $this->getPageRepository()->deleteClause($tableName);
647  }
648  } elseif (!$ignoreEnableFields && !$includeDeleted) {
649  $statement .= $this->getPageRepository()->enableFields($tableName);
650  } elseif (!$ignoreEnableFields && $includeDeleted) {
651  throw new InconsistentQuerySettingsException('Query setting "ignoreEnableFields=FALSE" can not be used together with "includeDeleted=TRUE" in frontend context.', 1327678173);
652  }
653  return $statement;
654  }
655 
664  protected function getBackendConstraintStatement($tableName, $ignoreEnableFields, $includeDeleted)
665  {
666  $statement = '';
667  if (!$ignoreEnableFields) {
668  $statement .= BackendUtility::BEenableFields($tableName);
669  }
670  if (!$includeDeleted) {
671  $statement .= BackendUtility::deleteClause($tableName);
672  }
673  return $statement;
674  }
675 
684  protected function getSysLanguageStatement($tableName, $tableAlias, $querySettings)
685  {
686  $sysLanguageStatement = '';
687  if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
688  if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])) {
689  // Select all entries for the current language
690  $additionalWhereClause = $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' IN (' . (int)$querySettings->getLanguageUid() . ',-1)';
691  // If any language is set -> get those entries which are not translated yet
692  // They will be removed by \TYPO3\CMS\Frontend\Page\PageRepository::getRecordOverlay if not matching overlay mode
693  if (isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
694  && $querySettings->getLanguageUid() > 0
695  ) {
696  $mode = $querySettings->getLanguageMode();
697  if ($mode === 'strict') {
698  $additionalWhereClause = $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=-1' .
699  ' OR (' . $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' = ' . (int)$querySettings->getLanguageUid() .
700  ' AND ' . $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '=0' .
701  ') OR (' . $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
702  ' AND ' . $tableAlias . '.uid IN (SELECT ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
703  ' FROM ' . $tableName .
704  ' WHERE ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '>0' .
705  ' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=' . (int)$querySettings->getLanguageUid();
706  } else {
707  $additionalWhereClause .= ' OR (' . $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
708  ' AND ' . $tableAlias . '.uid NOT IN (SELECT ' . $tableAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
709  ' FROM ' . $tableName .
710  ' WHERE ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '>0' .
711  ' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=' . (int)$querySettings->getLanguageUid();
712  }
713 
714  // Add delete clause to ensure all entries are loaded
715  if (isset($GLOBALS['TCA'][$tableName]['ctrl']['delete'])) {
716  $additionalWhereClause .= ' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['delete'] . '=0';
717  }
718  $additionalWhereClause .= '))';
719  }
720  $sysLanguageStatement = '(' . $additionalWhereClause . ')';
721  }
722  }
723  return $sysLanguageStatement;
724  }
725 
735  protected function getPageIdStatement($tableName, $tableAlias, array $storagePageIds)
736  {
737  if (!isset($GLOBALS['TCA'][$tableName]['ctrl'])) {
738  return '';
739  }
740 
741  $rootLevel = (int)$GLOBALS['TCA'][$tableName]['ctrl']['rootLevel'];
742  switch ($rootLevel) {
743  // Only in pid 0
744  case 1:
745  return $tableAlias . '.pid = 0';
746  // Pid 0 and pagetree
747  case -1:
748  if (empty($storagePageIds)) {
749  return $tableAlias . '.pid = 0';
750  }
751  $storagePageIds[] = 0;
752  break;
753  // Only pagetree or not set
754  case 0:
755  if (empty($storagePageIds)) {
756  throw new InconsistentQuerySettingsException('Missing storage page ids.', 1365779762);
757  }
758  break;
759  // Invalid configuration
760  default:
761  return '';
762  }
763  return $tableAlias . '.pid IN (' . implode(',', $this->databaseHandle->cleanIntArray($storagePageIds)) . ')';
764  }
765 
773  protected function parseJoin(Qom\JoinInterface $join, array &$sql)
774  {
775  $leftSource = $join->getLeft();
776  $leftClassName = $leftSource->getNodeTypeName();
777  $leftTableName = $leftSource->getSelectorName();
778  $this->addRecordTypeConstraint($leftClassName, $sql);
779  $rightSource = $join->getRight();
780  if ($rightSource instanceof Qom\JoinInterface) {
781  $left = $rightSource->getLeft();
782  $rightClassName = $left->getNodeTypeName();
783  $rightTableName = $left->getSelectorName();
784  } else {
785  $rightClassName = $rightSource->getNodeTypeName();
786  $rightTableName = $rightSource->getSelectorName();
787  $sql['fields'][$leftTableName] = $rightTableName . '.*';
788  }
789  $this->addRecordTypeConstraint($rightClassName, $sql);
790  $leftTableName = $this->getUniqueAlias($sql, $leftTableName);
791  $sql['tables'][$leftTableName] = $leftTableName;
792  $rightTableName = $this->getUniqueAlias($sql, $rightTableName);
793  $sql['unions'][$rightTableName] = 'LEFT JOIN ' . $rightTableName;
794  $joinCondition = $join->getJoinCondition();
795  if ($joinCondition instanceof Qom\EquiJoinCondition) {
796  $column1Name = $this->dataMapper->convertPropertyNameToColumnName($joinCondition->getProperty1Name(), $leftClassName);
797  $column2Name = $this->dataMapper->convertPropertyNameToColumnName($joinCondition->getProperty2Name(), $rightClassName);
798  $sql['unions'][$rightTableName] .= ' ON ' . $leftTableName . '.' . $column1Name . ' = ' . $rightTableName . '.' . $column2Name;
799  }
800  if ($rightSource instanceof Qom\JoinInterface) {
801  $this->parseJoin($rightSource, $sql);
802  }
803  }
804 
814  protected function getUniqueAlias(array &$sql, $tableName, $fullPropertyPath = null)
815  {
816  if (isset($fullPropertyPath) && isset($this->tablePropertyMap[$fullPropertyPath])) {
817  return $this->tablePropertyMap[$fullPropertyPath];
818  }
819 
820  $alias = $tableName;
821  $i = 0;
822  while (isset($sql['tableAliasMap'][$alias])) {
823  $alias = $tableName . $i;
824  $i++;
825  }
826 
827  $sql['tableAliasMap'][$alias] = $tableName;
828 
829  if (isset($fullPropertyPath)) {
830  $this->tablePropertyMap[$fullPropertyPath] = $alias;
831  }
832 
833  return $alias;
834  }
835 
849  protected function addUnionStatement(&$className, &$tableName, &$propertyPath, array &$sql, &$fullPropertyPath)
850  {
851  $explodedPropertyPath = explode('.', $propertyPath, 2);
852  $propertyName = $explodedPropertyPath[0];
853  $columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
854  $realTableName = $this->dataMapper->convertClassNameToTableName($className);
855  $tableName = isset($this->tablePropertyMap[$fullPropertyPath]) ? $this->tablePropertyMap[$fullPropertyPath] : $realTableName;
856  $columnMap = $this->dataMapper->getDataMap($className)->getColumnMap($propertyName);
857 
858  if ($columnMap === null) {
859  throw new Exception\MissingColumnMapException('The ColumnMap for property "' . $propertyName . '" of class "' . $className . '" is missing.', 1355142232);
860  }
861 
862  $parentKeyFieldName = $columnMap->getParentKeyFieldName();
863  $childTableName = $columnMap->getChildTableName();
864 
865  if ($childTableName === null) {
866  throw new Exception\InvalidRelationConfigurationException('The relation information for property "' . $propertyName . '" of class "' . $className . '" is missing.', 1353170925);
867  }
868 
869  $fullPropertyPath .= ($fullPropertyPath === '') ? $propertyName : '.' . $propertyName;
870  $childTableAlias = $this->getUniqueAlias($sql, $childTableName, $fullPropertyPath);
871 
872  // If there is already exists a union with the current identifier we do not need to build it again and exit early.
873  if (isset($sql['unions'][$childTableAlias])) {
874  $propertyPath = $explodedPropertyPath[1];
875  $tableName = $childTableAlias;
876  $className = $this->dataMapper->getType($className, $propertyName);
877  return;
878  }
879 
880  if ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_ONE) {
881  if (isset($parentKeyFieldName)) {
882  $sql['unions'][$childTableAlias] = 'LEFT JOIN ' . $childTableName . ' AS ' . $childTableAlias . ' ON ' . $tableName . '.uid=' . $childTableAlias . '.' . $parentKeyFieldName;
883  } else {
884  $sql['unions'][$childTableAlias] = 'LEFT JOIN ' . $childTableName . ' AS ' . $childTableAlias . ' ON ' . $tableName . '.' . $columnName . '=' . $childTableAlias . '.uid';
885  }
886  $sql['unions'][$childTableAlias] .= $this->getAdditionalMatchFieldsStatement($columnMap, $childTableName, $childTableAlias, $realTableName);
887  } elseif ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_MANY) {
888  if (isset($parentKeyFieldName)) {
889  $sql['unions'][$childTableAlias] = 'LEFT JOIN ' . $childTableName . ' AS ' . $childTableAlias . ' ON ' . $tableName . '.uid=' . $childTableAlias . '.' . $parentKeyFieldName;
890  } else {
891  $onStatement = '(FIND_IN_SET(' . $childTableAlias . '.uid, ' . $tableName . '.' . $columnName . '))';
892  $sql['unions'][$childTableAlias] = 'LEFT JOIN ' . $childTableName . ' AS ' . $childTableAlias . ' ON ' . $onStatement;
893  }
894  $sql['unions'][$childTableAlias] .= $this->getAdditionalMatchFieldsStatement($columnMap, $childTableName, $childTableAlias, $realTableName);
895  } elseif ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
896  $relationTableName = $columnMap->getRelationTableName();
897  $relationTableAlias = $relationTableAlias = $this->getUniqueAlias($sql, $relationTableName, $fullPropertyPath . '_mm');
898  $sql['unions'][$relationTableAlias] = 'LEFT JOIN ' . $relationTableName . ' AS ' . $relationTableAlias . ' ON ' . $tableName . '.uid=' . $relationTableAlias . '.' . $columnMap->getParentKeyFieldName();
899  $sql['unions'][$relationTableAlias] .= $this->getAdditionalMatchFieldsStatement($columnMap, $relationTableName, $relationTableAlias, $realTableName);
900  $sql['unions'][$childTableAlias] = 'LEFT JOIN ' . $childTableName . ' AS ' . $childTableAlias . ' ON ' . $relationTableAlias . '.' . $columnMap->getChildKeyFieldName() . '=' . $childTableAlias . '.uid';
901  } else {
902  throw new Exception('Could not determine type of relation.', 1252502725);
903  }
904  // @todo check if there is another solution for this
905  $sql['keywords']['distinct'] = 'DISTINCT';
906  $propertyPath = $explodedPropertyPath[1];
907  $tableName = $childTableAlias;
908  $className = $this->dataMapper->getType($className, $propertyName);
909  }
910 
920  protected function replaceTableNameWithAlias($statement, $tableName, $tableAlias)
921  {
922  if ($tableAlias !== $tableName) {
923  $statement = str_replace($tableName . '.', $tableAlias . '.', $statement);
924  }
925 
926  return $statement;
927  }
928 
936  protected function resolveOperator($operator)
937  {
938  switch ($operator) {
940  $operator = 'IN';
941  break;
943  $operator = '=';
944  break;
946  $operator = 'IS';
947  break;
949  $operator = '!=';
950  break;
952  $operator = 'IS NOT';
953  break;
955  $operator = '<';
956  break;
958  $operator = '<=';
959  break;
961  $operator = '>';
962  break;
964  $operator = '>=';
965  break;
967  $operator = 'LIKE';
968  break;
969  default:
970  throw new Exception('Unsupported operator encountered.', 1242816073);
971  }
972  return $operator;
973  }
974 
978  protected function getPageRepository()
979  {
980  if (!$this->pageRepository instanceof PageRepository) {
981  if ($this->environmentService->isEnvironmentInFrontendMode() && is_object($this->getTSFE())) {
982  $this->pageRepository = $this->getTSFE()->sys_page;
983  } else {
984  $this->pageRepository = GeneralUtility::makeInstance(PageRepository::class);
985  }
986  }
987 
988  return $this->pageRepository;
989  }
990 
994  protected function getTSFE()
995  {
996  return isset($GLOBALS['TSFE']) ? $GLOBALS['TSFE'] : null;
997  }
998 }
addDynamicQueryParts(QuerySettingsInterface $querySettings, array &$sql)
getBackendConstraintStatement($tableName, $ignoreEnableFields, $includeDeleted)
parseOrderings(array $orderings, Qom\SourceInterface $source, array &$sql)
getVisibilityConstraintStatement(QuerySettingsInterface $querySettings, $tableName, $tableAlias)
parseConstraint(Qom\ConstraintInterface $constraint=null, Qom\SourceInterface $source, array &$sql)
static BEenableFields($table, $inv=false)
parseComparison(Qom\ComparisonInterface $comparison, Qom\SourceInterface $source, array &$sql)
getPageIdStatement($tableName, $tableAlias, array $storagePageIds)
getSysLanguageStatement($tableName, $tableAlias, $querySettings)
parseDynamicOperand(Qom\ComparisonInterface $comparison, Qom\SourceInterface $source, array &$sql)
addNullConditionToStatementIfRequired(array $sql, $statement, $tableAlias)
parseOperand(Qom\DynamicOperandInterface $operand, Qom\SourceInterface $source, array &$sql)
injectEnvironmentService(EnvironmentService $environmentService)
getFrontendConstraintStatement($tableName, $ignoreEnableFields, array $enableFieldsToBeIgnored=[], $includeDeleted)
addUnionStatement(&$className, &$tableName, &$propertyPath, array &$sql, &$fullPropertyPath)
getUniqueAlias(array &$sql, $tableName, $fullPropertyPath=null)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
$sql
Definition: server.php:84
getAdditionalWhereClause(QuerySettingsInterface $querySettings, $tableName, $tableAlias=null)
getAdditionalMatchFieldsStatement($columnMap, $childTableName, $childTableAlias, $parentTable=null)
static deleteClause($table, $tableAlias='')