_em = $em; $this->_parserResult = $parserResult; $sqlToDqlAliasMap = array(); foreach ($parserResult->getQueryComponents() as $dqlAlias => $qComp) { if ($dqlAlias != 'dctrn') { $sqlAlias = $this->generateTableAlias($qComp['metadata']->getTableName()); $sqlToDqlAliasMap[$sqlAlias] = $dqlAlias; } } // SQL => DQL alias stored in ParserResult, needed for hydration. $parserResult->setTableAliasMap($sqlToDqlAliasMap); // DQL => SQL alias stored only locally, needed for SQL construction. $this->_dqlToSqlAliasMap = array_flip($sqlToDqlAliasMap); } public function walkSelectStatement(AST\SelectStatement $AST) { $sql = $this->walkSelectClause($AST->getSelectClause()); $sql .= $this->walkFromClause($AST->getFromClause()); $sql .= $AST->getWhereClause() ? $this->walkWhereClause($AST->getWhereClause()) : ''; $sql .= $AST->getGroupByClause() ? $this->walkGroupByClause($AST->getGroupByClause()) : ''; //... more clauses return $sql; } public function walkSelectClause($selectClause) { return 'SELECT ' . (($selectClause->isDistinct()) ? 'DISTINCT ' : '') . implode(', ', array_map(array(&$this, 'walkSelectExpression'), $selectClause->getSelectExpressions())); } public function walkFromClause($fromClause) { $sql = ' FROM '; $identificationVarDecls = $fromClause->getIdentificationVariableDeclarations(); $firstIdentificationVarDecl = $identificationVarDecls[0]; $rangeDecl = $firstIdentificationVarDecl->getRangeVariableDeclaration(); $sql .= $rangeDecl->getClassMetadata()->getTableName() . ' ' . $this->_dqlToSqlAliasMap[$rangeDecl->getAliasIdentificationVariable()]; foreach ($firstIdentificationVarDecl->getJoinVariableDeclarations() as $joinVarDecl) { $sql .= $this->walkJoinVariableDeclaration($joinVarDecl); } return $sql; } /** * Walks down a JoinVariableDeclaration AST node and creates the corresponding SQL. * * @param JoinVariableDeclaration $joinVarDecl * @return string */ public function walkJoinVariableDeclaration($joinVarDecl) { $join = $joinVarDecl->getJoin(); $joinType = $join->getJoinType(); if ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) { $sql = ' LEFT JOIN '; } else { $sql = ' INNER JOIN '; } $joinAssocPathExpr = $join->getJoinAssociationPathExpression(); $sourceQComp = $this->_parserResult->getQueryComponent($joinAssocPathExpr->getIdentificationVariable()); $targetQComp = $this->_parserResult->getQueryComponent($join->getAliasIdentificationVariable()); $targetTableName = $targetQComp['metadata']->getTableName(); $targetTableAlias = $this->_dqlToSqlAliasMap[$join->getAliasIdentificationVariable()]; $sourceTableAlias = $this->_dqlToSqlAliasMap[$joinAssocPathExpr->getIdentificationVariable()]; $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON '; if ( ! $targetQComp['relation']->isOwningSide()) { $assoc = $targetQComp['metadata']->getAssociationMapping($targetQComp['relation']->getMappedByFieldName()); } else { $assoc = $targetQComp['relation']; } if ($targetQComp['relation']->isOneToOne() || $targetQComp['relation']->isOneToMany()) { $joinColumns = $assoc->getSourceToTargetKeyColumns(); $first = true; foreach ($joinColumns as $sourceColumn => $targetColumn) { if ( ! $first) $sql .= ' AND '; if ($targetQComp['relation']->isOwningSide()) { $sql .= "$sourceTableAlias.$sourceColumn = $targetTableAlias.$targetColumn"; } else { $sql .= "$sourceTableAlias.$targetColumn = $targetTableAlias.$sourceColumn"; } } } else { // ManyToMany //TODO } return $sql; } /** * Walks down a SelectExpression AST node and generates the corresponding SQL. * * @param $selectExpression * @return string */ public function walkSelectExpression($selectExpression) { $sql = ''; if ($selectExpression->getExpression() instanceof AST\PathExpression) { $pathExpression = $selectExpression->getExpression(); if ($pathExpression->isSimpleStateFieldPathExpression()) { $parts = $pathExpression->getParts(); $numParts = count($parts); $dqlAlias = $parts[0]; $fieldName = $parts[$numParts-1]; $qComp = $this->_parserResult->getQueryComponent($dqlAlias); $class = $qComp['metadata']; if ($numParts > 2) { for ($i = 1; $i < $numParts-1; ++$i) { //TODO } } $sqlTableAlias = $this->_dqlToSqlAliasMap[$dqlAlias]; $sql .= $sqlTableAlias . '.' . $class->getColumnName($fieldName) . ' AS ' . $sqlTableAlias . '__' . $class->getColumnName($fieldName); } else if ($pathExpression->isSimpleStateFieldAssociationPathExpression()) { throw new DoctrineException("Not yet implemented."); } else { throw new DoctrineException("Encountered invalid PathExpression during SQL construction."); } } else if ($selectExpression->getExpression() instanceof AST\AggregateExpression) { $aggExpr = $selectExpression->getExpression(); if ( ! $selectExpression->getFieldIdentificationVariable()) { $alias = $this->_scalarAliasCounter++; } else { $alias = $selectExpression->getFieldIdentificationVariable(); } $parts = $aggExpr->getPathExpression()->getParts(); $dqlAlias = $parts[0]; $fieldName = $parts[1]; $qComp = $this->_parserResult->getQueryComponent($dqlAlias); $columnName = $qComp['metadata']->getColumnName($fieldName); $sql .= $aggExpr->getFunctionName() . '('; if ($aggExpr->isDistinct()) $sql .= 'DISTINCT '; $sql .= $this->_dqlToSqlAliasMap[$dqlAlias] . '.' . $columnName; $sql .= ') AS dctrn__' . $alias; } //TODO: else if Subselect else { $dqlAlias = $selectExpression->getExpression(); $queryComp = $this->_parserResult->getQueryComponent($dqlAlias); $class = $queryComp['metadata']; $sqlTableAlias = $this->_dqlToSqlAliasMap[$dqlAlias]; $beginning = true; foreach ($class->getFieldMappings() as $fieldName => $fieldMapping) { if ($beginning) { $beginning = false; } else { $sql .= ', '; } $sql .= $sqlTableAlias . '.' . $fieldMapping['columnName'] . ' AS ' . $sqlTableAlias . '__' . $fieldMapping['columnName']; } } return $sql; } public function walkGroupByClause($groupByClause) { return ' GROUP BY ' . implode(', ', array_map(array(&$this, 'walkGroupByItem'), $groupByClause->getGroupByItems())); } public function walkGroupByItem($pathExpr) { //TODO: support general SingleValuedPathExpression, not just state field $parts = $pathExpr->getParts(); $qComp = $this->_parserResult->getQueryComponent($parts[0]); $columnName = $qComp['metadata']->getColumnName($parts[1]); return $this->_dqlToSqlAliasMap[$parts[0]] . '.' . $columnName; } public function walkUpdateStatement(AST\UpdateStatement $AST) { } public function walkDeleteStatement(AST\DeleteStatement $AST) { } public function walkWhereClause($whereClause) { $sql = ' WHERE '; $condExpr = $whereClause->getConditionalExpression(); $sql .= implode(' OR ', array_map(array(&$this, 'walkConditionalTerm'), $condExpr->getConditionalTerms())); return $sql; } public function walkConditionalTerm($condTerm) { return implode(' AND ', array_map(array(&$this, 'walkConditionalFactor'), $condTerm->getConditionalFactors())); } public function walkConditionalFactor($factor) { $sql = ''; if ($factor->isNot()) $sql .= ' NOT '; $primary = $factor->getConditionalPrimary(); if ($primary->isSimpleConditionalExpression()) { $simpleCond = $primary->getSimpleConditionalExpression(); if ($simpleCond instanceof AST\ComparisonExpression) { $sql .= $this->walkComparisonExpression($simpleCond); } else if ($simpleCond instanceof AST\LikeExpression) { $sql .= $this->walkLikeExpression($simpleCond); } // else if ... } else if ($primary->isConditionalExpression()) { $sql .= '(' . implode(' OR ', array_map(array(&$this, 'walkConditionalTerm'), $primary->getConditionalExpression()->getConditionalTerms())) . ')'; } return $sql; } public function walkLikeExpression($likeExpr) { $sql = ''; $stringExpr = $likeExpr->getStringExpression(); if ($stringExpr instanceof AST\PathExpression) { $sql .= $this->walkPathExpression($stringExpr); } //TODO else... $sql .= ' LIKE ' . $likeExpr->getStringPattern(); return $sql; } public function walkComparisonExpression($compExpr) { $sql = ''; if ($compExpr->getLeftExpression() instanceof AST\ArithmeticExpression) { $sql .= $this->walkArithmeticExpression($compExpr->getLeftExpression()); } // else... $sql .= ' ' . $compExpr->getOperator() . ' '; if ($compExpr->getRightExpression() instanceof AST\ArithmeticExpression) { $sql .= $this->walkArithmeticExpression($compExpr->getRightExpression()); } return $sql; } public function walkArithmeticExpression($arithmeticExpr) { $sql = ''; if ($arithmeticExpr->isSimpleArithmeticExpression()) { foreach ($arithmeticExpr->getSimpleArithmeticExpression()->getArithmeticTerms() as $term) { $sql .= $this->walkArithmeticTerm($term); } } else { $sql .= $this->walkSubselect($arithmeticExpr->getSubselect()); } return $sql; } public function walkArithmeticTerm($term) { if (is_string($term)) return $term; return implode(' ', array_map(array(&$this, 'walkArithmeticFactor'), $term->getArithmeticFactors())); } public function walkArithmeticFactor($factor) { if (is_string($factor)) return $factor; $sql = ''; $primary = $factor->getArithmeticPrimary(); if (is_numeric($primary)) { $sql .= $primary; } else if (is_string($primary)) { //TODO: quote string according to platform $sql .= $primary; } else if ($primary instanceof AST\PathExpression) { $sql .= $this->walkPathExpression($primary); } else if ($primary instanceof AST\InputParameter) { if ($primary->isNamed()) { $sql .= ':' . $primary->getName(); } else { $sql .= '?'; } } else if ($primary instanceof AST\SimpleArithmeticExpression) { $sql .= '(' . $this->walkSimpleArithmeticExpression($primary) . ')'; } // else... return $sql; } public function walkSimpleArithmeticExpression($simpleArithmeticExpr) { return implode(' ', array_map(array(&$this, 'walkArithmeticTerm'), $simpleArithmeticExpr->getArithmeticTerms())); } public function walkPathExpression($pathExpr) { $sql = ''; if ($pathExpr->isSimpleStateFieldPathExpression()) { $parts = $pathExpr->getParts(); $numParts = count($parts); $dqlAlias = $parts[0]; $fieldName = $parts[$numParts-1]; $qComp = $this->_parserResult->getQueryComponent($dqlAlias); $class = $qComp['metadata']; if ($numParts > 2) { for ($i = 1; $i < $numParts-1; ++$i) { //TODO } } $sqlTableAlias = $this->_dqlToSqlAliasMap[$dqlAlias]; $sql .= $sqlTableAlias . '.' . $class->getColumnName($fieldName); } else if ($pathExpr->isSimpleStateFieldAssociationPathExpression()) { throw new Doctrine_Exception("Not yet implemented."); } else { throw new Doctrine_ORM_Query_Exception("Encountered invalid PathExpression during SQL construction."); } return $sql; } /** * Generates an SQL table alias from given table name and associates * it with given component alias * * @param string $componentName Component name to be associated with generated table alias * @return string Generated table alias */ public function generateTableAlias($componentName) { $baseAlias = strtolower(preg_replace('/[^A-Z]/', '\\1', $componentName)); // We may have a situation where we have all chars are lowercased if ($baseAlias == '') { // We simply grab the first 2 chars of component name $baseAlias = substr($componentName, 0, 2); } $alias = $baseAlias; if ( ! isset($this->_tableAliasSeeds[$baseAlias])) { $this->_tableAliasSeeds[$baseAlias] = 1; } else { $alias .= $this->_tableAliasSeeds[$baseAlias]++; } return $alias; } }