ProxyQuery.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. <?php
  2. /*
  3. * This file is part of the symfony package.
  4. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  5. * (c) Jonathan H. Wage <jonwage@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Sonata\DoctrineORMAdminBundle\Datagrid;
  11. use Doctrine\ORM\QueryBuilder;
  12. use Doctrine\ORM\Query;
  13. use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
  14. /**
  15. * This class try to unify the query usage with Doctrine
  16. */
  17. class ProxyQuery implements ProxyQueryInterface
  18. {
  19. protected $queryBuilder;
  20. protected $sortBy;
  21. protected $sortOrder;
  22. protected $parameterUniqueId;
  23. protected $entityJoinAliases;
  24. public function __construct(QueryBuilder $queryBuilder)
  25. {
  26. $this->queryBuilder = $queryBuilder;
  27. $this->uniqueParameterId = 0;
  28. $this->entityJoinAliases = array();
  29. }
  30. public function execute(array $params = array(), $hydrationMode = null)
  31. {
  32. // always clone the original queryBuilder
  33. $queryBuilder = clone $this->queryBuilder;
  34. // todo : check how doctrine behave, potential SQL injection here ...
  35. if ($this->getSortBy()) {
  36. $sortBy = $this->getSortBy();
  37. if (strpos($sortBy, '.') === false) { // add the current alias
  38. $sortBy = $queryBuilder->getRootAlias().'.'.$sortBy;
  39. }
  40. $queryBuilder->orderBy($sortBy, $this->getSortOrder());
  41. }
  42. return $this->getFixedQueryBuilder($queryBuilder)->getQuery()->execute($params, $hydrationMode);
  43. }
  44. /**
  45. * This method alters the query to return a clean set of object with a working
  46. * set of Object
  47. *
  48. * @param \Doctrine\ORM\QueryBuilder $queryBuilder
  49. * @return void
  50. */
  51. private function getFixedQueryBuilder(QueryBuilder $queryBuilder)
  52. {
  53. $queryBuilderId = clone $queryBuilder;
  54. // step 1 : retrieve the targeted class
  55. $from = $queryBuilderId->getDQLPart('from');
  56. $class = $from[0]->getFrom();
  57. // step 2 : retrieve the column id
  58. $idName = current($queryBuilderId->getEntityManager()->getMetadataFactory()->getMetadataFor($class)->getIdentifierFieldNames());
  59. // step 3 : retrieve the different subjects id
  60. $select = sprintf('%s.%s', $queryBuilderId->getRootAlias(), $idName);
  61. $queryBuilderId->resetDQLPart('select');
  62. $queryBuilderId->add('select', 'DISTINCT '.$select);
  63. //for SELECT DISTINCT, ORDER BY expressions must appear in select list
  64. /* Consider
  65. SELECT DISTINCT x FROM tab ORDER BY y;
  66. For any particular x-value in the table there might be many different y
  67. values. Which one will you use to sort that x-value in the output?
  68. */
  69. // todo : check how doctrine behave, potential SQL injection here ...
  70. if ($this->getSortBy()) {
  71. $sortBy = $this->getSortBy();
  72. if (strpos($sortBy, '.') === false) { // add the current alias
  73. $sortBy = $queryBuilderId->getRootAlias().'.'.$sortBy;
  74. }
  75. $sortBy .= ' AS __order_by';
  76. $queryBuilderId->addSelect($sortBy);
  77. }
  78. $results = $queryBuilderId->getQuery()->execute(array(), Query::HYDRATE_ARRAY);
  79. $idx = array();
  80. $connection = $queryBuilder->getEntityManager()->getConnection();
  81. foreach($results as $id) {
  82. $idx[] = $connection->quote($id[$idName]);
  83. }
  84. // step 4 : alter the query to match the targeted ids
  85. if (count($idx) > 0) {
  86. $queryBuilder->andWhere(sprintf('%s IN (%s)', $select, implode(',', $idx)));
  87. $queryBuilder->setMaxResults(null);
  88. $queryBuilder->setFirstResult(null);
  89. }
  90. return $queryBuilder;
  91. }
  92. public function __call($name, $args)
  93. {
  94. return call_user_func_array(array($this->queryBuilder, $name), $args);
  95. }
  96. public function setSortBy($parentAssociationMappings, $fieldMapping)
  97. {
  98. $alias = $this->entityJoin($parentAssociationMappings);
  99. $this->sortBy = $alias.'.'.$fieldMapping['fieldName'];
  100. }
  101. public function getSortBy()
  102. {
  103. return $this->sortBy;
  104. }
  105. public function setSortOrder($sortOrder)
  106. {
  107. $this->sortOrder = $sortOrder;
  108. }
  109. public function getSortOrder()
  110. {
  111. return $this->sortOrder;
  112. }
  113. public function getSingleScalarResult()
  114. {
  115. $query = $this->queryBuilder->getQuery();
  116. return $query->getSingleScalarResult();
  117. }
  118. public function __clone()
  119. {
  120. $this->queryBuilder = clone $this->queryBuilder;
  121. }
  122. public function getQueryBuilder()
  123. {
  124. return $this->queryBuilder;
  125. }
  126. public function setFirstResult($firstResult)
  127. {
  128. $this->queryBuilder->setFirstResult($firstResult);
  129. }
  130. public function getFirstResult()
  131. {
  132. $this->queryBuilder->getFirstResult();
  133. }
  134. public function setMaxResults($maxResults)
  135. {
  136. $this->queryBuilder->setMaxResults($maxResults);
  137. }
  138. public function getMaxResults()
  139. {
  140. $this->queryBuilder->getMaxResults();
  141. }
  142. public function getUniqueParameterId()
  143. {
  144. return $this->uniqueParameterId++;
  145. }
  146. public function entityJoin($associationMappings)
  147. {
  148. $alias = $this->queryBuilder->getRootAlias();
  149. $newAlias = 's';
  150. foreach($associationMappings as $associationMapping){
  151. $newAlias .= '_'.$associationMapping['fieldName'];
  152. if (!in_array($newAlias, $this->entityJoinAliases)) {
  153. $this->entityJoinAliases[] = $newAlias;
  154. $this->queryBuilder->leftJoin(sprintf('%s.%s', $alias, $associationMapping['fieldName']), $newAlias);
  155. }
  156. $alias = $newAlias;
  157. }
  158. return $alias;
  159. }
  160. }