|
<?php
|
|
|
|
namespace Vendor\Extension\Persistence\Generic\Storage;
|
|
|
|
/**
|
|
* This is needed because TYPO3.Extbase has a few bugs in its Database Driver
|
|
*
|
|
* @author Philipp Wrann <wrann@pixelpoint.at>
|
|
* @package TYPO3.CMS
|
|
* @subpackage Vendor.Extension
|
|
*/
|
|
class PatchedTypo3DbBackend extends \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Typo3DbBackend {
|
|
|
|
/**
|
|
* Here we added the possibility of makeing joins on the same table,
|
|
* This is important to filter regionalobjects for their location
|
|
* as locations as well as regionalobjects are stored in the same table.
|
|
*
|
|
* @param string &$className
|
|
* @param string &$tableName
|
|
* @param array &$propertyPath
|
|
* @param array &$sql
|
|
* @param string &$parent
|
|
* @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception
|
|
* @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InvalidRelationConfigurationException
|
|
* @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\MissingColumnMapException
|
|
*/
|
|
protected function addUnionStatement(&$className, &$tableName, &$propertyPath, array &$sql, &$parent) {
|
|
|
|
$explodedPropertyPath = explode('.', $propertyPath, 2);
|
|
$propertyName = $explodedPropertyPath[0];
|
|
$columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
|
|
$tableName = $this->dataMapper->convertClassNameToTableName($className);
|
|
$columnMap = $this->dataMapper->getDataMap($className)->getColumnMap($propertyName);
|
|
|
|
if ($columnMap === NULL) {
|
|
throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\MissingColumnMapException('The ColumnMap for property "' . $propertyName . '" of class "' . $className . '" is missing.', 1355142232);
|
|
}
|
|
|
|
// MODIFICATION START
|
|
$parentKeyFieldName = $columnMap->getParentKeyFieldName();
|
|
$childTableName = $realTableName = $columnMap->getChildTableName();
|
|
|
|
/**
|
|
* @todo find better solution for this
|
|
*/
|
|
$alias = ($childTableName == $tableName || isset($sql['tables'][$childTableName]) || isset($sql['unions'][$childTableName])) ? $propertyName : $childTableName;
|
|
$parent = ($parent=='') ? $tableName : $parent;
|
|
|
|
if ($childTableName === NULL) {
|
|
throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InvalidRelationConfigurationException('The relation information for property "' . $propertyName . '" of class "' . $className . '" is missing.', 1353170925);
|
|
}
|
|
|
|
if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_ONE) {
|
|
if (isset($parentKeyFieldName)) {
|
|
// should work as expected
|
|
$sql['unions'][$alias] = 'LEFT JOIN ' . $realTableName . ' AS ' . $alias . ' ON ' . $parent . '.uid=' . $alias . '.' . $parentKeyFieldName;
|
|
} else {
|
|
// should work as expected
|
|
$sql['unions'][$alias] = 'LEFT JOIN ' . $realTableName . ' AS '. $alias . ' ON ' . $parent . '.' . $columnName . '=' . $alias . '.uid';
|
|
}
|
|
$className = $this->dataMapper->getType($className, $propertyName);
|
|
} elseif ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
|
|
if (isset($parentKeyFieldName)) {
|
|
// should work as expected
|
|
$sql['unions'][$alias] = 'LEFT JOIN ' . $realTableName . ' AS ' . $alias . ' ON ' . $parent . '.uid=' . $alias . '.' . $parentKeyFieldName;
|
|
} else {
|
|
// @todo !!!needs tesing!!!
|
|
$onStatement = '(FIND_IN_SET(' . $childTableName . '.uid, ' . $parent . '.' . $columnName . '))';
|
|
$sql['unions'][$alias] = 'LEFT JOIN ' . $alias . ' ON ' . $onStatement;
|
|
}
|
|
$className = $this->dataMapper->getType($className, $propertyName);
|
|
} elseif ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
|
|
$relationTableName = $columnMap->getRelationTableName();
|
|
// @todo !!!needs tesing!!!
|
|
$sql['unions'][$relationTableName] = 'LEFT JOIN ' . $relationTableName . ' ON ' . $parent . '.uid=' . $relationTableName . '.' . $columnMap->getParentKeyFieldName();
|
|
$sql['unions'][$alias] = 'LEFT JOIN ' . $childTableName . ' ON ' . $relationTableName . '.' . $columnMap->getChildKeyFieldName() . '=' . $childTableName . '.uid';
|
|
$className = $this->dataMapper->getType($className, $propertyName);
|
|
} else {
|
|
throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception('Could not determine type of relation.', 1252502725);
|
|
}
|
|
|
|
$parent = $alias;
|
|
$sql['keywords']['distinct'] = 'DISTINCT';
|
|
$propertyPath = $explodedPropertyPath[1];
|
|
$tableName = $alias;
|
|
}
|
|
|
|
/**
|
|
* Parse a DynamicOperand into SQL and parameter arrays.
|
|
*
|
|
* @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\DynamicOperandInterface $operand
|
|
* @param string $operator One of the JCR_OPERATOR_* constants
|
|
* @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source The source
|
|
* @param array &$sql The query parts
|
|
* @param array &$parameters The parameters that will replace the markers
|
|
* @param string $valueFunction an optional SQL function to apply to the operand value
|
|
* @param null $operand2
|
|
* @return void
|
|
*/
|
|
protected function parseDynamicOperand(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\DynamicOperandInterface $operand, $operator, \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source, array &$sql, array &$parameters, $valueFunction = NULL, $operand2 = NULL) {
|
|
if ($operand instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\LowerCaseInterface) {
|
|
$this->parseDynamicOperand($operand->getOperand(), $operator, $source, $sql, $parameters, 'LOWER');
|
|
} elseif ($operand instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\UpperCaseInterface) {
|
|
$this->parseDynamicOperand($operand->getOperand(), $operator, $source, $sql, $parameters, 'UPPER');
|
|
} elseif ($operand instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\PropertyValueInterface) {
|
|
$propertyName = $operand->getPropertyName();
|
|
if ($source instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SelectorInterface) {
|
|
// FIXME Only necessary to differ from Join
|
|
$className = $source->getNodeTypeName();
|
|
$tableName = $this->dataMapper->convertClassNameToTableName($className);
|
|
$parent = '';
|
|
while (strpos($propertyName, '.') !== FALSE) {
|
|
$this->addUnionStatement($className, $tableName, $propertyName, $sql, $parent);
|
|
}
|
|
} elseif ($source instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\JoinInterface) {
|
|
$tableName = $source->getJoinCondition()->getSelector1Name();
|
|
}
|
|
$columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
|
|
$operator = $this->resolveOperator($operator);
|
|
$constraintSQL = '';
|
|
if ($valueFunction === NULL) {
|
|
$constraintSQL .= (!empty($tableName) ? $tableName . '.' : '') . $columnName . ' ' . $operator . ' ?';
|
|
} else {
|
|
$constraintSQL .= $valueFunction . '(' . (!empty($tableName) ? $tableName . '.' : '') . $columnName . ') ' . $operator . ' ?';
|
|
}
|
|
$sql['where'][] = $constraintSQL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse a Comparison into SQL and parameter arrays.
|
|
*
|
|
* @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface $comparison The comparison to parse
|
|
* @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source The source
|
|
* @param array &$sql SQL query parts to add to
|
|
* @param array &$parameters Parameters to bind to the SQL
|
|
* @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\RepositoryException
|
|
* @return void
|
|
*/
|
|
protected function parseComparison(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ComparisonInterface $comparison, \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source, array &$sql, array &$parameters) {
|
|
$operand1 = $comparison->getOperand1();
|
|
$operator = $comparison->getOperator();
|
|
$operand2 = $comparison->getOperand2();
|
|
if ($operator === \TYPO3\CMS\Extbase\Persistence\QueryInterface::OPERATOR_IN) {
|
|
$items = array();
|
|
$hasValue = FALSE;
|
|
foreach ($operand2 as $value) {
|
|
$value = $this->getPlainValue($value);
|
|
if ($value !== NULL) {
|
|
$items[] = $value;
|
|
$hasValue = TRUE;
|
|
}
|
|
}
|
|
if ($hasValue === FALSE) {
|
|
$sql['where'][] = '1<>1';
|
|
} else {
|
|
$this->parseDynamicOperand($operand1, $operator, $source, $sql, $parameters, NULL, $operand2);
|
|
$parameters[] = $items;
|
|
}
|
|
} elseif ($operator === \TYPO3\CMS\Extbase\Persistence\QueryInterface::OPERATOR_CONTAINS) {
|
|
if ($operand2 === NULL) {
|
|
$sql['where'][] = '1<>1';
|
|
} else {
|
|
$className = $source->getNodeTypeName();
|
|
$tableName = $this->dataMapper->convertClassNameToTableName($className);
|
|
$propertyName = $operand1->getPropertyName();
|
|
$parent = '';
|
|
while (strpos($propertyName, '.') !== FALSE) {
|
|
$this->addUnionStatement($className, $tableName, $propertyName, $sql, $parent);
|
|
}
|
|
$columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
|
|
$dataMap = $this->dataMapper->getDataMap($className);
|
|
$columnMap = $dataMap->getColumnMap($propertyName);
|
|
$typeOfRelation = $columnMap instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap ? $columnMap->getTypeOfRelation() : NULL;
|
|
if ($typeOfRelation === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
|
|
// $relationTableName = $columnMap->getRelationTableName();
|
|
// $sql['where'][] = $tableName . '.uid IN (SELECT ' . $columnMap->getParentKeyFieldName() . ' FROM ' . $relationTableName . ' WHERE ' . $columnMap->getChildKeyFieldName() . '=?)';
|
|
// MODIFICATION START
|
|
$relationTableName = $columnMap->getRelationTableName();
|
|
if($matchFields = $columnMap->getRelationTableMatchFields()) {
|
|
$matchFieldsQuery = '';
|
|
foreach ($matchFields as $matchField => $matchValue) {
|
|
$matchFieldsQuery .= ' '.$matchField.' = \''.$matchValue.'\' AND ';
|
|
}
|
|
}
|
|
$sql['where'][] = $tableName . '.uid IN (SELECT ' . $columnMap->getParentKeyFieldName() . ' FROM ' . $relationTableName . ' WHERE ' . $matchFieldsQuery . $columnMap->getChildKeyFieldName() . '=?)';
|
|
$parameters[] = intval($this->getPlainValue($operand2));
|
|
// MODIFICATION END
|
|
} elseif ($typeOfRelation === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
|
|
$parentKeyFieldName = $columnMap->getParentKeyFieldName();
|
|
if (isset($parentKeyFieldName)) {
|
|
$childTableName = $columnMap->getChildTableName();
|
|
$sql['where'][] = $tableName . '.uid=(SELECT ' . $childTableName . '.' . $parentKeyFieldName . ' FROM ' . $childTableName . ' WHERE ' . $childTableName . '.uid=?)';
|
|
$parameters[] = intval($this->getPlainValue($operand2));
|
|
} else {
|
|
$sql['where'][] = 'FIND_IN_SET(?,' . $tableName . '.' . $columnName . ')';
|
|
$parameters[] = intval($this->getPlainValue($operand2));
|
|
}
|
|
} else {
|
|
throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\RepositoryException('Unsupported or non-existing property name "' . $propertyName . '" used in relation matching.', 1327065745);
|
|
}
|
|
}
|
|
} else {
|
|
if ($operand2 === NULL) {
|
|
if ($operator === \TYPO3\CMS\Extbase\Persistence\QueryInterface::OPERATOR_EQUAL_TO) {
|
|
$operator = self::OPERATOR_EQUAL_TO_NULL;
|
|
} elseif ($operator === \TYPO3\CMS\Extbase\Persistence\QueryInterface::OPERATOR_NOT_EQUAL_TO) {
|
|
$operator = self::OPERATOR_NOT_EQUAL_TO_NULL;
|
|
}
|
|
}
|
|
$this->parseDynamicOperand($operand1, $operator, $source, $sql, $parameters);
|
|
$parameters[] = $this->getPlainValue($operand2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Transforms orderings into SQL.
|
|
*
|
|
* @param array $orderings An array of orderings (Tx_Extbase_Persistence_QOM_Ordering)
|
|
* @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source The source
|
|
* @param array &$sql The query parts
|
|
* @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnsupportedOrderException
|
|
* @return void
|
|
*/
|
|
protected function parseOrderings(array $orderings, \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source, array &$sql) {
|
|
foreach ($orderings as $propertyName => $order) {
|
|
switch ($order) {
|
|
case \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelConstantsInterface::JCR_ORDER_ASCENDING:
|
|
|
|
case \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING:
|
|
$order = 'ASC';
|
|
break;
|
|
case \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelConstantsInterface::JCR_ORDER_DESCENDING:
|
|
|
|
case \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING:
|
|
$order = 'DESC';
|
|
break;
|
|
default:
|
|
throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnsupportedOrderException('Unsupported order encountered.', 1242816074);
|
|
}
|
|
$className = '';
|
|
$tableName = '';
|
|
|
|
/**
|
|
* MODIFICATION START
|
|
* This way we enable the use of a function inside ordering
|
|
* this is mysql specific but its needed for our listMode Priority feature
|
|
* This way we can get a ordered list but put some specific objects on top of the list
|
|
*/
|
|
if (stripos($propertyName,'FIELD(') !== FALSE) {
|
|
$parts = explode(',',str_replace(')','',$propertyName));
|
|
$property = str_ireplace('FIELD(','',array_shift($parts));
|
|
$values = array_filter(array_filter($parts,function($value){
|
|
/**
|
|
* Just to make sure nothing is injected here
|
|
* maybe simply use an escape function on values
|
|
*/
|
|
return (preg_match("/[^\w]/",$value)==0);
|
|
}));
|
|
$className = $source->getNodeTypeName();
|
|
$tableName = $this->dataMapper->convertClassNameToTableName($className);
|
|
$columnName = $this->dataMapper->convertPropertyNameToColumnName($property, $className);
|
|
$fieldString = 'FIELD('.$tableName.'.'.$columnName.','.implode(',',$values).')';
|
|
$sql['orderings'][] = $fieldString. ' ' . $order;
|
|
} else {
|
|
/**
|
|
* MODIFICATION END
|
|
*/
|
|
if ($source instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SelectorInterface) {
|
|
$className = $source->getNodeTypeName();
|
|
$tableName = $this->dataMapper->convertClassNameToTableName($className);
|
|
$parent = '';
|
|
while (strpos($propertyName, '.') !== FALSE) {
|
|
$this->addUnionStatement($className, $tableName, $propertyName, $sql, $parent);
|
|
}
|
|
} elseif ($source instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\JoinInterface) {
|
|
$tableName = $source->getLeft()->getSelectorName();
|
|
}
|
|
$columnName = $this->dataMapper->convertPropertyNameToColumnName($propertyName, $className);
|
|
if (strlen($tableName) > 0) {
|
|
$sql['orderings'][] = $tableName . '.' . $columnName . ' ' . $order;
|
|
} else {
|
|
$sql['orderings'][] = $columnName . ' ' . $order;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
?>
|