/** * {@inheritdoc} */ protected function getSelectColumnsSQL() { if ($this->currentPersisterContext->selectColumnListSql !== null) { return $this->currentPersisterContext->selectColumnListSql; } $columnList[] = parent::getSelectColumnsSQL(); $rootClass = $this->em->getClassMetadata($this->class->rootEntityName); $tableAlias = $this->getSQLTableAlias($rootClass->name); // Append discriminator column $discrColumn = $this->class->discriminatorColumn['name']; $discrColumnType = $this->class->discriminatorColumn['type']; $columnList[] = $tableAlias . '.' . $discrColumn; $resultColumnName = $this->platform->getSQLResultCasing($discrColumn); $this->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName); $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumn, false, $discrColumnType); // Append subclass columns foreach ($this->class->subClasses as $subClassName) { $subClass = $this->em->getClassMetadata($subClassName); // Regular columns foreach ($subClass->fieldMappings as $fieldName => $mapping) { if (isset($mapping['inherited'])) { continue; } $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass); } // Foreign key columns foreach ($subClass->associationMappings as $assoc) { if (!$assoc['isOwningSide'] || !($assoc['type'] & ClassMetadata::TO_ONE) || isset($assoc['inherited'])) { continue; } $className = isset($assoc['inherited']) ? $assoc['inherited'] : $this->class->name; $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) { $columnList[] = $this->getSelectJoinColumnSQL($tableAlias, $srcColumn, $className, PersisterHelper::getTypeOfColumn($assoc['sourceToTargetKeyColumns'][$srcColumn], $targetClass, $this->em)); } } } $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList); return $this->currentPersisterContext->selectColumnListSql; }
/** * Ensure this method is never called. This persister overrides getSelectEntitiesSQL directly. * * @return string */ protected function getSelectColumnsSQL() { // Create the column list fragment only once if ($this->currentPersisterContext->selectColumnListSql !== null) { return $this->currentPersisterContext->selectColumnListSql; } $columnList = array(); $discrColumn = $this->class->discriminatorColumn['name']; $discrColumnType = $this->class->discriminatorColumn['type']; $baseTableAlias = $this->getSQLTableAlias($this->class->name); $resultColumnName = $this->platform->getSQLResultCasing($discrColumn); $this->currentPersisterContext->rsm->addEntityResult($this->class->name, 'r'); $this->currentPersisterContext->rsm->setDiscriminatorColumn('r', $resultColumnName); $this->currentPersisterContext->rsm->addMetaResult('r', $resultColumnName, $discrColumn, false, $discrColumnType); // Add regular columns foreach ($this->class->fieldMappings as $fieldName => $mapping) { $class = isset($mapping['inherited']) ? $this->em->getClassMetadata($mapping['inherited']) : $this->class; $columnList[] = $this->getSelectColumnSQL($fieldName, $class); } // Add foreign key columns foreach ($this->class->associationMappings as $mapping) { if (!$mapping['isOwningSide'] || !($mapping['type'] & ClassMetadata::TO_ONE)) { continue; } $tableAlias = isset($mapping['inherited']) ? $this->getSQLTableAlias($mapping['inherited']) : $baseTableAlias; foreach ($mapping['targetToSourceKeyColumns'] as $srcColumn) { $className = isset($mapping['inherited']) ? $mapping['inherited'] : $this->class->name; $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); $columnList[] = $this->getSelectJoinColumnSQL($tableAlias, $srcColumn, $className, PersisterHelper::getTypeOfColumn($mapping['sourceToTargetKeyColumns'][$srcColumn], $targetClass, $this->em)); } } // Add discriminator column (DO NOT ALIAS, see AbstractEntityInheritancePersister#processSQLResult). $tableAlias = $this->class->rootEntityName == $this->class->name ? $baseTableAlias : $this->getSQLTableAlias($this->class->rootEntityName); $columnList[] = $tableAlias . '.' . $discrColumn; // sub tables foreach ($this->class->subClasses as $subClassName) { $subClass = $this->em->getClassMetadata($subClassName); $tableAlias = $this->getSQLTableAlias($subClassName); // Add subclass columns foreach ($subClass->fieldMappings as $fieldName => $mapping) { if (isset($mapping['inherited'])) { continue; } $columnList[] = $this->getSelectColumnSQL($fieldName, $subClass); } // Add join columns (foreign keys) foreach ($subClass->associationMappings as $mapping) { if (!$mapping['isOwningSide'] || !($mapping['type'] & ClassMetadata::TO_ONE) || isset($mapping['inherited'])) { continue; } foreach ($mapping['targetToSourceKeyColumns'] as $srcColumn) { $className = isset($mapping['inherited']) ? $mapping['inherited'] : $subClass->name; $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); $columnList[] = $this->getSelectJoinColumnSQL($tableAlias, $srcColumn, $className, PersisterHelper::getTypeOfColumn($mapping['sourceToTargetKeyColumns'][$srcColumn], $targetClass, $this->em)); } } } $this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList); return $this->currentPersisterContext->selectColumnListSql; }
/** * Infers field types to be used by parameter type casting. * * @param string $field * @param mixed $value * * @return array * * @throws \Doctrine\ORM\Query\QueryException */ private function getTypes($field, $value, ClassMetadata $class) { $types = array(); switch (true) { case isset($class->fieldMappings[$field]): $types = array_merge($types, PersisterHelper::getTypeOfField($field, $class, $this->em)); break; case isset($class->associationMappings[$field]): $assoc = $class->associationMappings[$field]; $class = $this->em->getClassMetadata($assoc['targetEntity']); if (!$assoc['isOwningSide']) { $assoc = $class->associationMappings[$assoc['mappedBy']]; $class = $this->em->getClassMetadata($assoc['targetEntity']); } $columns = $assoc['type'] === ClassMetadata::MANY_TO_MANY ? $assoc['relationToTargetKeyColumns'] : $assoc['sourceToTargetKeyColumns']; foreach ($columns as $column) { $types[] = PersisterHelper::getTypeOfColumn($column, $class, $this->em); } break; default: $types[] = null; break; } if (is_array($value)) { return array_map(function ($type) { return Type::getType($type)->getBindingType() + Connection::ARRAY_PARAM_OFFSET; }, $types); } return $types; }
/** * Initializes a new <tt>MultiTableUpdateExecutor</tt>. * * Internal note: Any SQL construction and preparation takes place in the constructor for * best performance. With a query cache the executor will be cached. * * @param \Doctrine\ORM\Query\AST\Node $AST The root AST node of the DQL query. * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker The walker used for SQL generation from the AST. */ public function __construct(AST\Node $AST, $sqlWalker) { $em = $sqlWalker->getEntityManager(); $conn = $em->getConnection(); $platform = $conn->getDatabasePlatform(); $quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); $updateClause = $AST->updateClause; $primaryClass = $sqlWalker->getEntityManager()->getClassMetadata($updateClause->abstractSchemaName); $rootClass = $em->getClassMetadata($primaryClass->rootEntityName); $updateItems = $updateClause->updateItems; $tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); $idColumnNames = $rootClass->getIdentifierColumnNames(); $idColumnList = implode(', ', $idColumnNames); // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $updateClause->aliasIdentificationVariable); $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' . ' SELECT t0.' . implode(', t0.', $idColumnNames); $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $updateClause->aliasIdentificationVariable); $fromClause = new AST\FromClause([new AST\IdentificationVariableDeclaration($rangeDecl, null, [])]); $this->_insertSql .= $sqlWalker->walkFromClause($fromClause); // 2. Create ID subselect statement used in UPDATE ... WHERE ... IN (subselect) $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable; // 3. Create and store UPDATE statements $classNames = array_merge($primaryClass->parentClasses, [$primaryClass->name], $primaryClass->subClasses); $i = -1; foreach (array_reverse($classNames) as $className) { $affected = false; $class = $em->getClassMetadata($className); $updateSql = 'UPDATE ' . $quoteStrategy->getTableName($class, $platform) . ' SET '; foreach ($updateItems as $updateItem) { $field = $updateItem->pathExpression->field; if (isset($class->fieldMappings[$field]) && !isset($class->fieldMappings[$field]['inherited']) || isset($class->associationMappings[$field]) && !isset($class->associationMappings[$field]['inherited'])) { $newValue = $updateItem->newValue; if (!$affected) { $affected = true; ++$i; } else { $updateSql .= ', '; } $updateSql .= $sqlWalker->walkUpdateItem($updateItem); if ($newValue instanceof AST\InputParameter) { $this->_sqlParameters[$i][] = $newValue->name; ++$this->_numParametersInUpdateClause; } } } if ($affected) { $this->_sqlStatements[$i] = $updateSql . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')'; } } // Append WHERE clause to insertSql, if there is one. if ($AST->whereClause) { $this->_insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); } // 4. Store DDL for temporary identifier table. $columnDefinitions = []; foreach ($idColumnNames as $idColumnName) { $columnDefinitions[$idColumnName] = ['notnull' => true, 'type' => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em))]; } $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; $this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable); }
/** * Delete Class Table Inheritance entities. * A temporary table is needed to keep IDs to be deleted in both parent and child class' tables. * * Thanks Steve Ebersole (Hibernate) for idea on how to tackle reliably this scenario, we owe him a beer! =) * * @param PersistentCollection $collection * * @return int * * @throws \Doctrine\DBAL\DBALException */ private function deleteJoinedEntityCollection(PersistentCollection $collection) { $mapping = $collection->getMapping(); $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); $rootClass = $this->em->getClassMetadata($targetClass->rootEntityName); // 1) Build temporary table DDL $tempTable = $this->platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); $idColumnNames = $rootClass->getIdentifierColumnNames(); $idColumnList = implode(', ', $idColumnNames); $columnDefinitions = []; foreach ($idColumnNames as $idColumnName) { $columnDefinitions[$idColumnName] = ['notnull' => true, 'type' => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $this->em))]; } $statement = $this->platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' . $this->platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; $this->conn->executeUpdate($statement); // 2) Build insert table records into temporary table $query = $this->em->createQuery(' SELECT t0.' . implode(', t0.', $rootClass->getIdentifierFieldNames()) . ' FROM ' . $targetClass->name . ' t0 WHERE t0.' . $mapping['mappedBy'] . ' = :owner')->setParameter('owner', $collection->getOwner()); $statement = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ') ' . $query->getSQL(); $parameters = array_values($sourceClass->getIdentifierValues($collection->getOwner())); $numDeleted = $this->conn->executeUpdate($statement, $parameters); // 3) Delete records on each table in the hierarchy $classNames = array_merge($targetClass->parentClasses, [$targetClass->name], $targetClass->subClasses); foreach (array_reverse($classNames) as $className) { $tableName = $this->quoteStrategy->getTableName($this->em->getClassMetadata($className), $this->platform); $statement = 'DELETE FROM ' . $tableName . ' WHERE (' . $idColumnList . ')' . ' IN (SELECT ' . $idColumnList . ' FROM ' . $tempTable . ')'; $this->conn->executeUpdate($statement); } // 4) Drop temporary table $statement = $this->platform->getDropTemporaryTableSQL($tempTable); $this->conn->executeUpdate($statement); return $numDeleted; }
/** * Adds the result set mapping of the results of native SQL queries to the result set. * * @param ClassMetadataInfo $class * @param string $resultSetMappingName * * @return ResultSetMappingBuilder */ public function addNamedNativeQueryResultSetMapping(ClassMetadataInfo $class, $resultSetMappingName) { $counter = 0; $resultMapping = $class->getSqlResultSetMapping($resultSetMappingName); $rootShortName = $class->reflClass->getShortName(); $rootAlias = strtolower($rootShortName[0]) . $counter; if (isset($resultMapping['entities'])) { foreach ($resultMapping['entities'] as $key => $entityMapping) { $classMetadata = $this->em->getClassMetadata($entityMapping['entityClass']); if ($class->reflClass->name == $classMetadata->reflClass->name) { $this->addEntityResult($classMetadata->name, $rootAlias); $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $rootAlias); } else { $shortName = $classMetadata->reflClass->getShortName(); $joinAlias = strtolower($shortName[0]) . ++$counter; $associations = $class->getAssociationsByTargetClass($classMetadata->name); $this->addNamedNativeQueryEntityResultMapping($classMetadata, $entityMapping, $joinAlias); foreach ($associations as $relation => $mapping) { $this->addJoinedEntityResult($mapping['targetEntity'], $joinAlias, $rootAlias, $relation); } } } } if (isset($resultMapping['columns'])) { foreach ($resultMapping['columns'] as $entityMapping) { $type = isset($class->fieldNames[$entityMapping['name']]) ? PersisterHelper::getTypeOfColumn($entityMapping['name'], $class, $this->em) : 'string'; $this->addScalarResult($entityMapping['name'], $entityMapping['name'], $type); } } return $this; }
/** * {@inheritdoc} */ public function walkSelectClause($selectClause) { $sql = 'SELECT ' . ($selectClause->isDistinct ? 'DISTINCT ' : ''); $sqlSelectExpressions = array_filter(array_map([$this, 'walkSelectExpression'], $selectClause->selectExpressions)); if ($this->query->getHint(Query::HINT_INTERNAL_ITERATION) == true && $selectClause->isDistinct) { $this->query->setHint(self::HINT_DISTINCT, true); } $addMetaColumns = !$this->query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) && $this->query->getHydrationMode() == Query::HYDRATE_OBJECT || $this->query->getHydrationMode() != Query::HYDRATE_OBJECT && $this->query->getHint(Query::HINT_INCLUDE_META_COLUMNS); foreach ($this->selectedClasses as $selectedClass) { $class = $selectedClass['class']; $dqlAlias = $selectedClass['dqlAlias']; $resultAlias = $selectedClass['resultAlias']; // Register as entity or joined entity result if ($this->queryComponents[$dqlAlias]['relation'] === null) { $this->rsm->addEntityResult($class->name, $dqlAlias, $resultAlias); } else { $this->rsm->addJoinedEntityResult($class->name, $dqlAlias, $this->queryComponents[$dqlAlias]['parent'], $this->queryComponents[$dqlAlias]['relation']['fieldName']); } if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) { // Add discriminator columns to SQL $rootClass = $this->em->getClassMetadata($class->rootEntityName); $tblAlias = $this->getSQLTableAlias($rootClass->getTableName(), $dqlAlias); $discrColumn = $rootClass->discriminatorColumn; $columnAlias = $this->getSQLColumnAlias($discrColumn['name']); $sqlSelectExpressions[] = $tblAlias . '.' . $discrColumn['name'] . ' AS ' . $columnAlias; $this->rsm->setDiscriminatorColumn($dqlAlias, $columnAlias); $this->rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName'], false, $discrColumn['type']); } // Add foreign key columns to SQL, if necessary if (!$addMetaColumns && !$class->containsForeignIdentifier) { continue; } // Add foreign key columns of class and also parent classes foreach ($class->associationMappings as $assoc) { if (!($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE)) { continue; } else { if (!$addMetaColumns && !isset($assoc['id'])) { continue; } } $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); $isIdentifier = isset($assoc['id']) && $assoc['id'] === true; $owningClass = isset($assoc['inherited']) ? $this->em->getClassMetadata($assoc['inherited']) : $class; $sqlTableAlias = $this->getSQLTableAlias($owningClass->getTableName(), $dqlAlias); foreach ($assoc['joinColumns'] as $joinColumn) { $columnName = $joinColumn['name']; $columnAlias = $this->getSQLColumnAlias($columnName); $columnType = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); $sqlSelectExpressions[] = $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias; $this->rsm->addMetaResult($dqlAlias, $columnAlias, $columnName, $isIdentifier, $columnType); } } // Add foreign key columns to SQL, if necessary if (!$addMetaColumns) { continue; } // Add foreign key columns of subclasses foreach ($class->subClasses as $subClassName) { $subClass = $this->em->getClassMetadata($subClassName); $sqlTableAlias = $this->getSQLTableAlias($subClass->getTableName(), $dqlAlias); foreach ($subClass->associationMappings as $assoc) { // Skip if association is inherited if (isset($assoc['inherited'])) { continue; } if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); foreach ($assoc['joinColumns'] as $joinColumn) { $columnName = $joinColumn['name']; $columnAlias = $this->getSQLColumnAlias($columnName); $columnType = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em); $sqlSelectExpressions[] = $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias; $this->rsm->addMetaResult($dqlAlias, $columnAlias, $columnName, $subClass->isIdentifier($columnName), $columnType); } } } } } $sql .= implode(', ', $sqlSelectExpressions); return $sql; }
/** * Initializes a new <tt>MultiTableDeleteExecutor</tt>. * * Internal note: Any SQL construction and preparation takes place in the constructor for * best performance. With a query cache the executor will be cached. * * @param \Doctrine\ORM\Query\AST\Node $AST The root AST node of the DQL query. * @param \Doctrine\ORM\Query\SqlWalker $sqlWalker The walker used for SQL generation from the AST. */ public function __construct(AST\Node $AST, $sqlWalker) { $em = $sqlWalker->getEntityManager(); $conn = $em->getConnection(); $platform = $conn->getDatabasePlatform(); $quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); $primaryClass = $em->getClassMetadata($AST->deleteClause->abstractSchemaName); $primaryDqlAlias = $AST->deleteClause->aliasIdentificationVariable; $rootClass = $em->getClassMetadata($primaryClass->rootEntityName); $tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName()); $idColumnNames = $rootClass->getIdentifierColumnNames(); $idColumnList = implode(', ', $idColumnNames); // 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause() $sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $primaryDqlAlias); $this->_insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')' . ' SELECT t0.' . implode(', t0.', $idColumnNames); $rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $primaryDqlAlias); $fromClause = new AST\FromClause([new AST\IdentificationVariableDeclaration($rangeDecl, null, [])]); $this->_insertSql .= $sqlWalker->walkFromClause($fromClause); // Append WHERE clause, if there is one. if ($AST->whereClause) { $this->_insertSql .= $sqlWalker->walkWhereClause($AST->whereClause); } // 2. Create ID subselect statement used in DELETE ... WHERE ... IN (subselect) $idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable; // 3. Create and store DELETE statements $classNames = array_merge($primaryClass->parentClasses, [$primaryClass->name], $primaryClass->subClasses); foreach (array_reverse($classNames) as $className) { $tableName = $quoteStrategy->getTableName($em->getClassMetadata($className), $platform); $this->_sqlStatements[] = 'DELETE FROM ' . $tableName . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')'; } // 4. Store DDL for temporary identifier table. $columnDefinitions = []; foreach ($idColumnNames as $idColumnName) { $columnDefinitions[$idColumnName] = ['notnull' => true, 'type' => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em))]; } $this->_createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' (' . $platform->getColumnDeclarationListSQL($columnDefinitions) . ')'; $this->_dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable); }
/** * @param \Doctrine\ORM\PersistentCollection $collection * @param object $element * @param boolean $addFilters Whether the filter SQL should be included or not. * * @return array ordered vector: * - quoted join table name * - where clauses to be added for filtering * - parameters to be bound for filtering * - types of the parameters to be bound for filtering */ private function getJoinTableRestrictions(PersistentCollection $collection, $element, $addFilters) { $filterMapping = $collection->getMapping(); $mapping = $filterMapping; if (!$mapping['isOwningSide']) { $sourceClass = $this->em->getClassMetadata($mapping['targetEntity']); $targetClass = $this->em->getClassMetadata($mapping['sourceEntity']); $sourceId = $this->uow->getEntityIdentifier($element); $targetId = $this->uow->getEntityIdentifier($collection->getOwner()); $mapping = $sourceClass->associationMappings[$mapping['mappedBy']]; } else { $sourceClass = $this->em->getClassMetadata($mapping['sourceEntity']); $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); $sourceId = $this->uow->getEntityIdentifier($collection->getOwner()); $targetId = $this->uow->getEntityIdentifier($element); } $quotedJoinTable = $this->quoteStrategy->getJoinTableName($mapping, $sourceClass, $this->platform); $whereClauses = array(); $params = array(); $types = array(); foreach ($mapping['joinTableColumns'] as $joinTableColumn) { $whereClauses[] = ($addFilters ? 't.' : '') . $joinTableColumn . ' = ?'; if (isset($mapping['relationToTargetKeyColumns'][$joinTableColumn])) { $targetColumn = $mapping['relationToTargetKeyColumns'][$joinTableColumn]; $params[] = $targetId[$targetClass->getFieldForColumn($targetColumn)]; $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $targetClass, $this->em); continue; } // relationToSourceKeyColumns $targetColumn = $mapping['relationToSourceKeyColumns'][$joinTableColumn]; $params[] = $sourceId[$sourceClass->getFieldForColumn($targetColumn)]; $types[] = PersisterHelper::getTypeOfColumn($targetColumn, $sourceClass, $this->em); } if ($addFilters) { $quotedJoinTable .= ' t'; list($joinTargetEntitySQL, $filterSql) = $this->getFilterSql($filterMapping); if ($filterSql) { $quotedJoinTable .= ' ' . $joinTargetEntitySQL; $whereClauses[] = $filterSql; } } return array($quotedJoinTable, $whereClauses, $params, $types); }