CustomTreeWalkersTest.php 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <?php
  2. /*
  3. * $Id$
  4. *
  5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. *
  17. * This software consists of voluntary contributions made by many individuals
  18. * and is licensed under the LGPL. For more information, see
  19. * <http://www.doctrine-project.org>.
  20. */
  21. namespace Doctrine\Tests\ORM\Functional;
  22. use Doctrine\ORM\Query;
  23. require_once __DIR__ . '/../../TestInit.php';
  24. /**
  25. * Test case for custom AST walking and modification.
  26. *
  27. * @author Roman Borschel <roman@code-factory.org>
  28. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  29. * @link http://www.doctrine-project.org
  30. * @since 2.0
  31. */
  32. class CustomTreeWalkersTest extends \Doctrine\Tests\OrmFunctionalTestCase
  33. {
  34. protected function setUp() {
  35. $this->useModelSet('cms');
  36. parent::setUp();
  37. }
  38. public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed)
  39. {
  40. try {
  41. $query = $this->_em->createQuery($dqlToBeTested);
  42. $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\Tests\ORM\Functional\CustomTreeWalker'))
  43. ->useQueryCache(false);
  44. $this->assertEquals($sqlToBeConfirmed, $query->getSql());
  45. $query->free();
  46. } catch (\Exception $e) {
  47. $this->fail($e->getMessage() . ' at "' . $e->getFile() . '" on line ' . $e->getLine());
  48. }
  49. }
  50. public function testSupportsQueriesWithoutWhere()
  51. {
  52. $this->assertSqlGeneration(
  53. 'select u from Doctrine\Tests\Models\CMS\CmsUser u',
  54. "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.id = 1"
  55. );
  56. }
  57. public function testSupportsQueriesWithMultipleConditionalExpressions()
  58. {
  59. $this->assertSqlGeneration(
  60. 'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name or u.name = :otherName',
  61. "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE (c0_.name = ? OR c0_.name = ?) AND c0_.id = 1"
  62. );
  63. }
  64. public function testSupportsQueriesWithSimpleConditionalExpression()
  65. {
  66. $this->assertSqlGeneration(
  67. 'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.name = :name',
  68. "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.name = ? AND c0_.id = 1"
  69. );
  70. }
  71. }
  72. class CustomTreeWalker extends Query\TreeWalkerAdapter
  73. {
  74. public function walkSelectStatement(Query\AST\SelectStatement $selectStatement)
  75. {
  76. // Get the DQL aliases of all the classes we want to modify
  77. $dqlAliases = array();
  78. foreach ($this->_getQueryComponents() as $dqlAlias => $comp) {
  79. // Hard-coded check just for demonstration: We want to modify the query if
  80. // it involves the CmsUser class.
  81. if ($comp['metadata']->name == 'Doctrine\Tests\Models\CMS\CmsUser') {
  82. $dqlAliases[] = $dqlAlias;
  83. }
  84. }
  85. // Create our conditions for all involved classes
  86. $factors = array();
  87. foreach ($dqlAliases as $alias) {
  88. $pathExpr = new Query\AST\PathExpression(Query\AST\PathExpression::TYPE_STATE_FIELD, $alias, 'id');
  89. $pathExpr->type = Query\AST\PathExpression::TYPE_STATE_FIELD;
  90. $comparisonExpr = new Query\AST\ComparisonExpression($pathExpr, '=', 1);
  91. $condPrimary = new Query\AST\ConditionalPrimary;
  92. $condPrimary->simpleConditionalExpression = $comparisonExpr;
  93. $factor = new Query\AST\ConditionalFactor($condPrimary);
  94. $factors[] = $factor;
  95. }
  96. if (($whereClause = $selectStatement->whereClause) !== null) {
  97. // There is already a WHERE clause, so append the conditions
  98. $condExpr = $whereClause->conditionalExpression;
  99. // Since Phase 1 AST optimizations were included, we need to re-add the ConditionalExpression
  100. if ( ! ($condExpr instanceof Query\AST\ConditionalExpression)) {
  101. $condExpr = new Query\AST\ConditionalExpression(array($condExpr));
  102. $whereClause->conditionalExpression = $condExpr;
  103. }
  104. $existingTerms = $whereClause->conditionalExpression->conditionalTerms;
  105. if (count($existingTerms) > 1) {
  106. // More than one term, so we need to wrap all these terms in a single root term
  107. // i.e: "WHERE u.name = :foo or u.other = :bar" => "WHERE (u.name = :foo or u.other = :bar) AND <our condition>"
  108. $primary = new Query\AST\ConditionalPrimary;
  109. $primary->conditionalExpression = new Query\AST\ConditionalExpression($existingTerms);
  110. $existingFactor = new Query\AST\ConditionalFactor($primary);
  111. $term = new Query\AST\ConditionalTerm(array_merge(array($existingFactor), $factors));
  112. $selectStatement->whereClause->conditionalExpression->conditionalTerms = array($term);
  113. } else {
  114. // Just one term so we can simply append our factors to that term
  115. $singleTerm = $selectStatement->whereClause->conditionalExpression->conditionalTerms[0];
  116. // Since Phase 1 AST optimizations were included, we need to re-add the ConditionalExpression
  117. if ( ! ($singleTerm instanceof Query\AST\ConditionalTerm)) {
  118. $singleTerm = new Query\AST\ConditionalTerm(array($singleTerm));
  119. $selectStatement->whereClause->conditionalExpression->conditionalTerms[0] = $singleTerm;
  120. }
  121. $singleTerm->conditionalFactors = array_merge($singleTerm->conditionalFactors, $factors);
  122. $selectStatement->whereClause->conditionalExpression->conditionalTerms = array($singleTerm);
  123. }
  124. } else {
  125. // Create a new WHERE clause with our factors
  126. $term = new Query\AST\ConditionalTerm($factors);
  127. $condExpr = new Query\AST\ConditionalExpression(array($term));
  128. $whereClause = new Query\AST\WhereClause($condExpr);
  129. $selectStatement->whereClause = $whereClause;
  130. }
  131. }
  132. }