Completed
Push — 2.x ( 359f6e )
by Sullivan
14:45
created

ProxyQuery::setMaxResults()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 6
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
/*
3
 * This file is part of the symfony package.
4
 * (c) Fabien Potencier <[email protected]>
5
 * (c) Jonathan H. Wage <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Sonata\DoctrineORMAdminBundle\Datagrid;
12
13
use Doctrine\ORM\QueryBuilder;
14
use Doctrine\ORM\Query;
15
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
16
17
/**
18
 * This class try to unify the query usage with Doctrine
19
 */
20
class ProxyQuery implements ProxyQueryInterface
21
{
22
    protected $queryBuilder;
23
24
    protected $sortBy;
25
26
    protected $sortOrder;
27
28
    protected $uniqueParameterId;
29
30
    protected $entityJoinAliases;
31
32
    /**
33
     * @param mixed $queryBuilder
34
     */
35
    public function __construct($queryBuilder)
36
    {
37
        $this->queryBuilder      = $queryBuilder;
38
        $this->uniqueParameterId = 0;
39
        $this->entityJoinAliases = array();
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45
    public function execute(array $params = array(), $hydrationMode = null)
46
    {
47
        // always clone the original queryBuilder
48
        $queryBuilder = clone $this->queryBuilder;
49
50
        // todo : check how doctrine behave, potential SQL injection here ...
51
        if ($this->getSortBy()) {
52
            $sortBy = $this->getSortBy();
53
            if (strpos($sortBy, '.') === false) { // add the current alias
54
                $sortBy = $queryBuilder->getRootAlias() . '.' . $sortBy;
55
            }
56
            $queryBuilder->addOrderBy($sortBy, $this->getSortOrder());
57
        } else {
58
            $queryBuilder->resetDQLPart( 'orderBy' );
59
        }
60
61
        return $this->getFixedQueryBuilder($queryBuilder)->getQuery()->execute($params, $hydrationMode);
62
    }
63
64
    /**
65
     * This method alters the query to return a clean set of object with a working
66
     * set of Object
67
     *
68
     * @param \Doctrine\ORM\QueryBuilder $queryBuilder
69
     *
70
     * @return \Doctrine\ORM\QueryBuilder
71
     */
72
    private function getFixedQueryBuilder(QueryBuilder $queryBuilder)
73
    {
74
        $queryBuilderId = clone $queryBuilder;
75
76
        // step 1 : retrieve the targeted class
77
        $from  = $queryBuilderId->getDQLPart('from');
78
        $class = $from[0]->getFrom();
79
        $metadata = $queryBuilderId->getEntityManager()->getMetadataFactory()->getMetadataFor($class);
80
81
        // step 2 : retrieve identifier columns
82
        $idNames = $metadata->getIdentifierFieldNames();
83
84
        // step 3 : retrieve the different subjects ids
85
        $selects = array();
86
        $idxSelect = '';
87
        foreach ($idNames as $idName) {
88
            $select = sprintf('%s.%s', $queryBuilderId->getRootAlias(), $idName);
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\ORM\QueryBuilder::getRootAlias() has been deprecated with message: Please use $qb->getRootAliases() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
89
            // Put the ID select on this array to use it on results QB
90
            $selects[$idName] = $select;
91
            // Use IDENTITY if id is a relation too. See: http://doctrine-orm.readthedocs.org/en/latest/reference/dql-doctrine-query-language.html
92
            // Should work only with doctrine/orm: ~2.2
93
            $idSelect = $select;
94
            if ($metadata->hasAssociation($idName)) {
95
                $idSelect = sprintf('IDENTITY(%s) as %s', $idSelect, $idName);
96
            }
97
            $idxSelect .= ($idxSelect !== '' ? ', ' : '') . $idSelect;
98
        }
99
        $queryBuilderId->resetDQLPart('select');
100
        $queryBuilderId->add('select', 'DISTINCT '.$idxSelect);
0 ignored issues
show
Documentation introduced by
'DISTINCT ' . $idxSelect is of type string, but the function expects a object<Doctrine\ORM\Query\Expr\Base>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
101
102
        // for SELECT DISTINCT, ORDER BY expressions must appear in idxSelect list
103
        /* Consider
104
            SELECT DISTINCT x FROM tab ORDER BY y;
105
        For any particular x-value in the table there might be many different y
106
        values.  Which one will you use to sort that x-value in the output?
107
        */
108
        // todo : check how doctrine behave, potential SQL injection here ...
109
        if ($this->getSortBy()) {
110
            $sortBy = $this->getSortBy();
111
            if (strpos($sortBy, '.') === false) { // add the current alias
112
                $sortBy = $queryBuilderId->getRootAlias() . '.' . $sortBy;
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\ORM\QueryBuilder::getRootAlias() has been deprecated with message: Please use $qb->getRootAliases() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
113
            }
114
            $sortBy .= ' AS __order_by';
115
            $queryBuilderId->addSelect($sortBy);
116
        }
117
118
        $results    = $queryBuilderId->getQuery()->execute(array(), Query::HYDRATE_ARRAY);
119
        $idxMatrix  = array();
120
        foreach ($results as $id) {
121
            foreach ($idNames as $idName) {
122
                $idxMatrix[$idName][] = $id[$idName];
123
            }
124
        }
125
126
        // step 4 : alter the query to match the targeted ids
127
        foreach ($idxMatrix as $idName => $idx) {
128
            if (count($idx) > 0) {
129
                $idxParamName = sprintf('%s_idx', $idName);
130
                $queryBuilder->andWhere(sprintf('%s IN (:%s)', $selects[$idName], $idxParamName));
131
                $queryBuilder->setParameter($idxParamName, $idx);
132
                $queryBuilder->setMaxResults(null);
133
                $queryBuilder->setFirstResult(null);
134
            }
135
        }
136
137
        return $queryBuilder;
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public function __call($name, $args)
144
    {
145
        return call_user_func_array(array($this->queryBuilder, $name), $args);
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151
    public function __get($name)
152
    {
153
        return $this->queryBuilder->$name;
154
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159
    public function setSortBy($parentAssociationMappings, $fieldMapping)
160
    {
161
        $alias        = $this->entityJoin($parentAssociationMappings);
162
        $this->sortBy = $alias . '.' . $fieldMapping['fieldName'];
163
164
        return $this;
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
170
    public function getSortBy()
171
    {
172
        return $this->sortBy;
173
    }
174
175
    /**
176
     * {@inheritdoc}
177
     */
178
    public function setSortOrder($sortOrder)
179
    {
180
        $this->sortOrder = $sortOrder;
181
182
        return $this;
183
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188
    public function getSortOrder()
189
    {
190
        return $this->sortOrder;
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196
    public function getSingleScalarResult()
197
    {
198
        $query = $this->queryBuilder->getQuery();
199
200
        return $query->getSingleScalarResult();
201
    }
202
203
    /**
204
     * {@inheritdoc}
205
     */
206
    public function __clone()
207
    {
208
        $this->queryBuilder = clone $this->queryBuilder;
209
    }
210
211
    /**
212
     * @return mixed
213
     */
214
    public function getQueryBuilder()
215
    {
216
        return $this->queryBuilder;
217
    }
218
219
    /**
220
     * {@inheritdoc}
221
     */
222
    public function setFirstResult($firstResult)
223
    {
224
        $this->queryBuilder->setFirstResult($firstResult);
225
226
        return $this;
227
    }
228
229
    /**
230
     * {@inheritdoc}
231
     */
232
    public function getFirstResult()
233
    {
234
        return $this->queryBuilder->getFirstResult();
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240
    public function setMaxResults($maxResults)
241
    {
242
        $this->queryBuilder->setMaxResults($maxResults);
243
244
        return $this;
245
    }
246
247
    /**
248
     * {@inheritdoc}
249
     */
250
    public function getMaxResults()
251
    {
252
        return $this->queryBuilder->getMaxResults();
253
    }
254
255
    /**
256
     * {@inheritdoc}
257
     */
258
    public function getUniqueParameterId()
259
    {
260
        return $this->uniqueParameterId++;
261
    }
262
263
    /**
264
     * {@inheritdoc}
265
     */
266
    public function entityJoin(array $associationMappings)
267
    {
268
        $alias = $this->queryBuilder->getRootAlias();
269
270
        $newAlias = 's';
271
272
        foreach ($associationMappings as $associationMapping) {
273
            $newAlias .= '_' . $associationMapping['fieldName'];
274
            if (!in_array($newAlias, $this->entityJoinAliases)) {
275
                $this->entityJoinAliases[] = $newAlias;
276
                $this->queryBuilder->leftJoin(sprintf('%s.%s', $alias, $associationMapping['fieldName']), $newAlias);
277
            }
278
279
            $alias = $newAlias;
280
        }
281
282
        return $alias;
283
    }
284
}
285