SizeFunction.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the LGPL. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\ORM\Query\AST\Functions;
  20. use Doctrine\ORM\Query\Lexer;
  21. /**
  22. * "SIZE" "(" CollectionValuedPathExpression ")"
  23. *
  24. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  25. * @link www.doctrine-project.org
  26. * @since 2.0
  27. * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
  28. * @author Jonathan Wage <jonwage@gmail.com>
  29. * @author Roman Borschel <roman@code-factory.org>
  30. * @author Benjamin Eberlei <kontakt@beberlei.de>
  31. */
  32. class SizeFunction extends FunctionNode
  33. {
  34. public $collectionPathExpression;
  35. /**
  36. * @override
  37. * @todo If the collection being counted is already joined, the SQL can be simpler (more efficient).
  38. */
  39. public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
  40. {
  41. $platform = $sqlWalker->getConnection()->getDatabasePlatform();
  42. $dqlAlias = $this->collectionPathExpression->identificationVariable;
  43. $assocField = $this->collectionPathExpression->field;
  44. $qComp = $sqlWalker->getQueryComponent($dqlAlias);
  45. $class = $qComp['metadata'];
  46. $assoc = $class->associationMappings[$assocField];
  47. $sql = 'SELECT COUNT(*) FROM ';
  48. if ($assoc['type'] == \Doctrine\ORM\Mapping\ClassMetadata::ONE_TO_MANY) {
  49. $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']);
  50. $targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->getTableName());
  51. $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias);
  52. $sql .= $targetClass->getQuotedTableName($platform) . ' ' . $targetTableAlias . ' WHERE ';
  53. $owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']];
  54. $first = true;
  55. foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) {
  56. if ($first) $first = false; else $sql .= ' AND ';
  57. $sql .= $targetTableAlias . '.' . $sourceColumn
  58. . ' = '
  59. . $sourceTableAlias . '.' . $class->getQuotedColumnName($class->fieldNames[$targetColumn], $platform);
  60. }
  61. } else { // many-to-many
  62. $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc['targetEntity']);
  63. $owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']];
  64. $joinTable = $owningAssoc['joinTable'];
  65. // SQL table aliases
  66. $joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable['name']);
  67. $sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias);
  68. // join to target table
  69. $sql .= $targetClass->getQuotedJoinTableName($owningAssoc, $platform) . ' ' . $joinTableAlias . ' WHERE ';
  70. $joinColumns = $assoc['isOwningSide']
  71. ? $joinTable['joinColumns']
  72. : $joinTable['inverseJoinColumns'];
  73. $first = true;
  74. foreach ($joinColumns as $joinColumn) {
  75. if ($first) $first = false; else $sql .= ' AND ';
  76. $sourceColumnName = $class->getQuotedColumnName(
  77. $class->fieldNames[$joinColumn['referencedColumnName']], $platform
  78. );
  79. $sql .= $joinTableAlias . '.' . $joinColumn['name']
  80. . ' = '
  81. . $sourceTableAlias . '.' . $sourceColumnName;
  82. }
  83. }
  84. return '(' . $sql . ')';
  85. }
  86. /**
  87. * @override
  88. */
  89. public function parse(\Doctrine\ORM\Query\Parser $parser)
  90. {
  91. $lexer = $parser->getLexer();
  92. $parser->match(Lexer::T_SIZE);
  93. $parser->match(Lexer::T_OPEN_PARENTHESIS);
  94. $this->collectionPathExpression = $parser->CollectionValuedPathExpression();
  95. $parser->match(Lexer::T_CLOSE_PARENTHESIS);
  96. }
  97. }