From 1ed287635a6cb15f5edfad0d110374a0519d7f28 Mon Sep 17 00:00:00 2001 From: guilhermeblanco Date: Thu, 15 Jan 2009 16:10:22 +0000 Subject: [PATCH] [2.0] More implementation under ORM/Query --- .../ORM/Query/Parser/DeleteStatement.php | 13 ++-- .../FieldAliasIdentificationVariable.php | 3 +- .../Parser/JoinAssociationPathExpression.php | 70 +++++++++++++++++ .../JoinCollectionValuedPathExpression.php | 13 +--- ...nSingleValuedAssociationPathExpression.php | 74 ++++++++++++++++++ .../ORM/Query/Parser/SelectClause.php | 4 +- .../ORM/Query/Parser/SelectExpression.php | 33 ++++---- .../ORM/Query/Parser/UpdateStatement.php | 13 ++-- lib/Doctrine/ORM/Query/Token.php | 78 ++++++++++--------- 9 files changed, 215 insertions(+), 86 deletions(-) create mode 100644 lib/Doctrine/ORM/Query/Parser/JoinAssociationPathExpression.php create mode 100644 lib/Doctrine/ORM/Query/Parser/JoinSingleValuedAssociationPathExpression.php diff --git a/lib/Doctrine/ORM/Query/Parser/DeleteStatement.php b/lib/Doctrine/ORM/Query/Parser/DeleteStatement.php index 4ef4a4c21..a37ecc2dd 100644 --- a/lib/Doctrine/ORM/Query/Parser/DeleteStatement.php +++ b/lib/Doctrine/ORM/Query/Parser/DeleteStatement.php @@ -29,23 +29,20 @@ * @since 2.0 * @version $Revision$ */ -class Doctrine_ORM_Query_Parser_DeleteStatement extends Doctrine_ORM_Query_Parser +class Doctrine_ORM_Query_Parser_DeleteStatement extends Doctrine_ORM_Query_ParserRule { - protected $_AST = null; - - public function syntax() { // DeleteStatement ::= DeleteClause [WhereClause] - $this->_AST = $this->AST('DeleteStatement'); + $AST = $this->AST('DeleteStatement'); - $this->_AST->setDeleteClause($this->parse('DeleteClause')); + $AST->setDeleteClause($this->parse('DeleteClause')); if ($this->_isNextToken(Doctrine_ORM_Query_Token::T_WHERE)) { - $this->_AST->setWhereClause($this->parse('WhereClause')); + $AST->setWhereClause($this->parse('WhereClause')); } // Return AST node - return $this->_AST; + return $AST; } } diff --git a/lib/Doctrine/ORM/Query/Parser/FieldAliasIdentificationVariable.php b/lib/Doctrine/ORM/Query/Parser/FieldAliasIdentificationVariable.php index 19e7e2a47..6804d30ea 100644 --- a/lib/Doctrine/ORM/Query/Parser/FieldAliasIdentificationVariable.php +++ b/lib/Doctrine/ORM/Query/Parser/FieldAliasIdentificationVariable.php @@ -48,7 +48,8 @@ class Doctrine_ORM_Query_Parser_FieldAliasIdentificationVariable extends Doctrin if ($parserResult->hasFieldAlias($this->_fieldAlias)) { // We should throw semantical error if there's already a field for this alias - $message = "Cannot re-declare field alias '" . $this->_fieldAlias . "'."; + $message = "Cannot re-declare field alias '" . $this->_fieldAlias + . "' near '" . $this->_parser->getQueryPiece($this->_parser->token) . "'."; $this->_parser->semanticalError($message); } diff --git a/lib/Doctrine/ORM/Query/Parser/JoinAssociationPathExpression.php b/lib/Doctrine/ORM/Query/Parser/JoinAssociationPathExpression.php new file mode 100644 index 000000000..d1552fd2d --- /dev/null +++ b/lib/Doctrine/ORM/Query/Parser/JoinAssociationPathExpression.php @@ -0,0 +1,70 @@ +. + */ + +/** + * JoinAssociationPathExpression ::= JoinCollectionValuedPathExpression | JoinSingleValuedAssociationPathExpression + * + * @author Guilherme Blanco + * @author Janne Vanhala + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://www.phpdoctrine.org + * @since 2.0 + * @version $Revision$ + */ +class Doctrine_ORM_Query_Parser_JoinAssociationPathExpression extends Doctrine_ORM_Query_ParserRule +{ + public function syntax() + { + // JoinAssociationPathExpression ::= JoinCollectionValuedPathExpression | JoinSingleValuedAssociationPathExpression + return $this->parse($this->_isSingleValuedPathExpression() + ? 'JoinSingleValuedAssociationPathExpression' + : 'JoinCollectionValuedPathExpression' + ); + } + + + private function _isSingleValuedPathExpression() + { + $parserResult = $this->_parser->getParserResult(); + + // Trying to recoginize this grammar: + // IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField) + $token = $this->_parser->lookahead; + $this->_parser->getScanner()->resetPeek(); + + if ($parserResult->hasQueryComponent($token['value'])) { + $queryComponent = $parserResult->getQueryComponent($token['value']); + $token = $this->_parser->getScanner()->peek(); + + // If we have a dot ".", then next char must be the "*" + if ($token['type'] === Doctrine_ORM_Query_Token::T_DOT) { + $token = $this->_parser->getScanner()->peek(); + + if ( ! ($queryComponent['metadata']->hasAssociation($token['value']) && + $queryComponent['metadata']->getAssociation($token['value'])->isOneToTone())) { + return true; + } + } + } + + return false; + } +} diff --git a/lib/Doctrine/ORM/Query/Parser/JoinCollectionValuedPathExpression.php b/lib/Doctrine/ORM/Query/Parser/JoinCollectionValuedPathExpression.php index 1e0c1da3c..7dd387ef0 100644 --- a/lib/Doctrine/ORM/Query/Parser/JoinCollectionValuedPathExpression.php +++ b/lib/Doctrine/ORM/Query/Parser/JoinCollectionValuedPathExpression.php @@ -59,17 +59,8 @@ class Doctrine_ORM_Query_Parser_JoinCollectionValuedPathExpression extends Doctr $this->_parser->semanticalError($message); } - if ( ! $queryComponent['metadata']->hasAssociation($fieldName)) { - $componentName = $queryComponent['metadata']->getClassName(); - - $message = "Field '" . $fieldName . "' is not an association in component '" . $componentName . "'."; - - $this->_parser->semanticalError($message); - } - - $mapping = $queryComponent['metadata']->getAssociation($fieldName); - - if ($mapping instanceof Doctrine_ORM_Mapping_OneToOneMapping) { + if ( ! ($queryComponent['metadata']->hasAssociation($fieldName) && + $queryComponent['metadata']->getAssociation($fieldName)->isOneToTone())) { $componentName = $queryComponent['metadata']->getClassName(); $message = "Field '" . $fieldName . "' does not map to a collection valued association in component '" diff --git a/lib/Doctrine/ORM/Query/Parser/JoinSingleValuedAssociationPathExpression.php b/lib/Doctrine/ORM/Query/Parser/JoinSingleValuedAssociationPathExpression.php new file mode 100644 index 000000000..e0262267a --- /dev/null +++ b/lib/Doctrine/ORM/Query/Parser/JoinSingleValuedAssociationPathExpression.php @@ -0,0 +1,74 @@ +. + */ + +/** + * JoinSingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + * + * @author Guilherme Blanco + * @author Janne Vanhala + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link http://www.phpdoctrine.org + * @since 2.0 + * @version $Revision$ + */ +class Doctrine_ORM_Query_Parser_JoinSingleValuedAssociationPathExpression extends Doctrine_ORM_Query_ParserRule +{ + protected $_AST = null; + + + public function syntax() + { + // JoinSingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField + $this->_AST = $this->AST('JoinSingleValuedAssociationPathExpression'); + + $this->_AST->setIdentificationVariable($this->parse('IdentificationVariable')); + $this->_parser->match(Doctrine_ORM_Query_Token::T_DOT); + $this->_AST->setSingleValuedAssociationField($this->parse('SingleValuedAssociationField')); + } + + + public function semantical() + { + $parserResult = $this->_parser->getParserResult(); + $queryComponent = $parserResult->getQueryComponent($this->_AST->setIdentificationVariable()); + $fieldName = $this->_AST->setSingleValuedAssociationField(); + + if ( ! $queryComponent['metadata']->hasField($fieldName)) { + $componentName = $queryComponent['metadata']->getClassName(); + + $message = "Field '" . $fieldName . "' does not exist in component '" . $componentName . "'."; + + $this->_parser->semanticalError($message); + } + + if ( ! ($queryComponent['metadata']->hasAssociation($fieldName) && + ! $queryComponent['metadata']->getAssociation($fieldName)->isOneToTone())) { + $componentName = $queryComponent['metadata']->getClassName(); + + $message = "Field '" . $fieldName . "' does not map to a single valued association in component '" + . $componentName . "'."; + + $this->_parser->semanticalError($message); + } + + return $this->_AST; + } +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/Parser/SelectClause.php b/lib/Doctrine/ORM/Query/Parser/SelectClause.php index 8695d47c5..d0007bc93 100644 --- a/lib/Doctrine/ORM/Query/Parser/SelectClause.php +++ b/lib/Doctrine/ORM/Query/Parser/SelectClause.php @@ -53,8 +53,8 @@ class Doctrine_ORM_Query_Parser_SelectClause extends Doctrine_ORM_Query_ParserRu // Process SelectExpressions (1..N) $this->_selectExpressions[] = $this->parse('SelectExpression'); - while ($this->_isNextToken(',')) { - $this->_parser->match(','); + while ($this->_isNextToken(Doctrine_ORM_Query_Token::T_COMMA)) { + $this->_parser->match(Doctrine_ORM_Query_Token::T_COMMA); $this->_selectExpressions[] = $this->parse('SelectExpression'); } diff --git a/lib/Doctrine/ORM/Query/Parser/SelectExpression.php b/lib/Doctrine/ORM/Query/Parser/SelectExpression.php index 67555dc7f..dd53576f3 100644 --- a/lib/Doctrine/ORM/Query/Parser/SelectExpression.php +++ b/lib/Doctrine/ORM/Query/Parser/SelectExpression.php @@ -20,9 +20,8 @@ */ /** - * SelectExpression ::= IdentificationVariable ["." "*"] | - * (StateFieldPathExpression | AggregateExpression | "(" Subselect ")" ) - * [["AS"] FieldIdentificationVariable] + * SelectExpression ::= IdentificationVariable ["." "*"] | StateFieldPathExpression | + * (AggregateExpression | "(" Subselect ")") [["AS"] FieldAliasIdentificationVariable] * * @author Guilherme Blanco * @author Janne Vanhala @@ -43,7 +42,8 @@ class Doctrine_ORM_Query_Parser_SelectExpression extends Doctrine_ORM_Query_Pars public function syntax() { // SelectExpression ::= IdentificationVariable ["." "*"] | StateFieldPathExpression | - // ( ( AggregateExpression | "(" Subselect ")" ) ["AS"] FieldIdentificationVariable ) + // (AggregateExpression | "(" Subselect ")") [["AS"] FieldAliasIdentificationVariable] + $this->_AST = $this->AST('SelectExpression'); // First we recognize for an IdentificationVariable (Component alias) if ($this->_isIdentificationVariable()) { @@ -54,16 +54,16 @@ class Doctrine_ORM_Query_Parser_SelectExpression extends Doctrine_ORM_Query_Pars $this->_parser->match('.'); $this->_parser->match('*'); } - } else if ($this->_isFunction() || $this->_isSubselect()) { - $this->_expression = $this->parse( - $this->_isFunction() ? 'AggregateExpression' : 'Subselect' - ); + } else if (($isFunction = $this->_isFunction()) !== false || $this->_isSubselect()) { + $this->_expression = $this->parse($isFunction ? 'AggregateExpression' : 'Subselect'); if ($this->_isNextToken(Doctrine_ORM_Query_Token::T_AS)) { $this->_parser->match(Doctrine_ORM_Query_Token::T_AS); - } - $this->_fieldIdentificationVariable = $this->parse('FieldIdentificationVariable'); + $this->_fieldIdentificationVariable = $this->parse('FieldAliasIdentificationVariable'); + } elseif ($this->_isNextToken(Doctrine_ORM_Query_Token::T_IDENTIFIER)) { + $this->_fieldIdentificationVariable = $this->parse('FieldAliasIdentificationVariable'); + } } else { $this->_expression = $this->parse('StateFieldPathExpression'); } @@ -72,17 +72,14 @@ class Doctrine_ORM_Query_Parser_SelectExpression extends Doctrine_ORM_Query_Pars public function semantical() { - $expression = $this->_expression->semantical(); + $this->_AST->setExpression($this->_expression->semantical()); if ($this->_fieldIdentificationVariable !== null) { - $expr = $expression; - - $expression = $this->AST('SelectExpression'); - $expression->setExpression($expr); - $expression->setFieldIdentificationVariable($this->_fieldIdentificationVariable->semantical()); + $this->_AST->setFieldIdentificationVariable($this->_fieldIdentificationVariable->semantical()); } - return $expression; + // Return AST node + return $this->_AST; } @@ -97,7 +94,7 @@ class Doctrine_ORM_Query_Parser_SelectExpression extends Doctrine_ORM_Query_Pars $token = $this->_parser->getScanner()->peek(); // If we have a dot ".", then next char must be the "*" - if ($token['value'] === '.') { + if ($token['type'] === Doctrine_ORM_Query_Token::T_DOT) { $token = $this->_parser->getScanner()->peek(); return $token['value'] === '*'; diff --git a/lib/Doctrine/ORM/Query/Parser/UpdateStatement.php b/lib/Doctrine/ORM/Query/Parser/UpdateStatement.php index 546e2597b..f6a74dcea 100644 --- a/lib/Doctrine/ORM/Query/Parser/UpdateStatement.php +++ b/lib/Doctrine/ORM/Query/Parser/UpdateStatement.php @@ -29,23 +29,20 @@ * @since 2.0 * @version $Revision$ */ -class Doctrine_ORM_Query_Parser_UpdateStatement extends Doctrine_ORM_Query_Parser +class Doctrine_ORM_Query_Parser_UpdateStatement extends Doctrine_ORM_Query_ParserRule { - protected $_AST = null; - - public function syntax() { // UpdateStatement ::= UpdateClause [WhereClause] - $this->_AST = $this->AST('UpdateStatement'); + $AST = $this->AST('UpdateStatement'); - $this->_AST->setUpdateClause($this->parse('UpdateClause')); + $AST->setUpdateClause($this->parse('UpdateClause')); if ($this->_isNextToken(Doctrine_ORM_Query_Token::T_WHERE)) { - $this->_AST->setWhereClause($this->parse('WhereClause')); + $AST->setWhereClause($this->parse('WhereClause')); } // Return AST node - return $this->_AST; + return $AST; } } diff --git a/lib/Doctrine/ORM/Query/Token.php b/lib/Doctrine/ORM/Query/Token.php index 813ef12e5..333a0560b 100644 --- a/lib/Doctrine/ORM/Query/Token.php +++ b/lib/Doctrine/ORM/Query/Token.php @@ -46,45 +46,46 @@ final class Doctrine_ORM_Query_Token const T_AVG = 106; const T_BETWEEN = 107; const T_BY = 108; - const T_COUNT = 109; - const T_DELETE = 110; - const T_DESC = 111; - const T_DISTINCT = 112; - const T_DOT = 113; - const T_ESCAPE = 114; - const T_EXISTS = 115; - const T_FROM = 116; - const T_GROUP = 117; - const T_HAVING = 118; - const T_IN = 119; - const T_INDEX = 120; - const T_INNER = 121; - const T_IS = 122; - const T_JOIN = 123; - const T_LEFT = 124; - const T_LIKE = 125; - const T_LIMIT = 126; - const T_MAX = 127; - const T_MIN = 128; - const T_MOD = 129; - const T_NOT = 130; - const T_NULL = 131; - const T_OFFSET = 132; - const T_ON = 133; - const T_OR = 134; - const T_ORDER = 135; - const T_OUTER = 136; - const T_SELECT = 137; - const T_SET = 138; - const T_SIZE = 139; - const T_SOME = 140; - const T_SUM = 141; - const T_UPDATE = 142; - const T_WHERE = 143; - const T_WITH = 144; + const T_COMMA = 109; + const T_COUNT = 110; + const T_DELETE = 111; + const T_DESC = 112; + const T_DISTINCT = 113; + const T_DOT = 114; + const T_ESCAPE = 115; + const T_EXISTS = 116; + const T_FROM = 117; + const T_GROUP = 118; + const T_HAVING = 119; + const T_IN = 120; + const T_INDEX = 121; + const T_INNER = 122; + const T_IS = 123; + const T_JOIN = 124; + const T_LEFT = 125; + const T_LIKE = 126; + const T_LIMIT = 127; + const T_MAX = 128; + const T_MIN = 129; + const T_MOD = 130; + const T_NOT = 131; + const T_NULL = 132; + const T_OFFSET = 133; + const T_ON = 134; + const T_OR = 135; + const T_ORDER = 136; + const T_OUTER = 137; + const T_SELECT = 138; + const T_SET = 139; + const T_SIZE = 140; + const T_SOME = 141; + const T_SUM = 142; + const T_UPDATE = 143; + const T_WHERE = 144; + const T_WITH = 145; - const T_TRUE = 145; - const T_FALSE = 146; + const T_TRUE = 146; + const T_FALSE = 147; protected $_keywordsTable = array(); @@ -100,6 +101,7 @@ final class Doctrine_ORM_Query_Token $this->addKeyword(self::T_AVG, "AVG"); $this->addKeyword(self::T_BETWEEN, "BETWEEN"); $this->addKeyword(self::T_BY, "BY"); + $this->addKeyword(self::T_COMMA, ","); $this->addKeyword(self::T_COUNT, "COUNT"); $this->addKeyword(self::T_DELETE, "DELETE"); $this->addKeyword(self::T_DESC, "DESC");