Complex classes like ProxyQuery often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ProxyQuery, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
23 | class ProxyQuery implements ProxyQueryInterface |
||
24 | { |
||
25 | /** |
||
26 | * @var QueryBuilder |
||
27 | */ |
||
28 | protected $queryBuilder; |
||
29 | |||
30 | /** |
||
31 | * @var string |
||
32 | */ |
||
33 | protected $sortBy; |
||
34 | |||
35 | /** |
||
36 | * @var mixed |
||
37 | */ |
||
38 | protected $sortOrder; |
||
39 | |||
40 | /** |
||
41 | * @var int |
||
42 | */ |
||
43 | protected $uniqueParameterId; |
||
44 | |||
45 | /** |
||
46 | * @var string[] |
||
47 | */ |
||
48 | protected $entityJoinAliases; |
||
49 | |||
50 | /** |
||
51 | * The map of query hints. |
||
52 | * |
||
53 | * @var array |
||
54 | */ |
||
55 | protected $hints = array(); |
||
56 | |||
57 | /** |
||
58 | * @param QueryBuilder $queryBuilder |
||
59 | */ |
||
60 | public function __construct($queryBuilder) |
||
61 | { |
||
62 | $this->queryBuilder = $queryBuilder; |
||
63 | $this->uniqueParameterId = 0; |
||
64 | $this->entityJoinAliases = array(); |
||
65 | } |
||
66 | |||
67 | /** |
||
68 | * {@inheritdoc} |
||
69 | */ |
||
70 | public function __call($name, $args) |
||
71 | { |
||
72 | return call_user_func_array(array($this->queryBuilder, $name), $args); |
||
73 | } |
||
74 | |||
75 | /** |
||
76 | * {@inheritdoc} |
||
77 | */ |
||
78 | public function __get($name) |
||
79 | { |
||
80 | return $this->queryBuilder->$name; |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * {@inheritdoc} |
||
85 | */ |
||
86 | public function __clone() |
||
87 | { |
||
88 | $this->queryBuilder = clone $this->queryBuilder; |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * {@inheritdoc} |
||
93 | */ |
||
94 | public function execute(array $params = array(), $hydrationMode = null) |
||
95 | { |
||
96 | // always clone the original queryBuilder |
||
97 | $queryBuilder = clone $this->queryBuilder; |
||
98 | |||
99 | $rootAlias = current($queryBuilder->getRootAliases()); |
||
100 | |||
101 | // todo : check how doctrine behave, potential SQL injection here ... |
||
102 | if ($this->getSortBy()) { |
||
103 | $sortBy = $this->getSortBy(); |
||
104 | if (strpos($sortBy, '.') === false) { // add the current alias |
||
105 | $sortBy = $rootAlias.'.'.$sortBy; |
||
106 | } |
||
107 | $queryBuilder->addOrderBy($sortBy, $this->getSortOrder()); |
||
108 | } else { |
||
109 | $queryBuilder->resetDQLPart('orderBy'); |
||
110 | } |
||
111 | |||
112 | /* By default, always add a sort on the identifier fields of the first |
||
113 | * used entity in the query, because RDBMS do not guarantee a |
||
114 | * particular order when no ORDER BY clause is specified, or when |
||
115 | * the field used for sorting is not unique. |
||
116 | */ |
||
117 | |||
118 | $identifierFields = $queryBuilder |
||
119 | ->getEntityManager() |
||
120 | ->getMetadataFactory() |
||
121 | ->getMetadataFor(current($queryBuilder->getRootEntities())) |
||
122 | ->getIdentifierFieldNames(); |
||
123 | |||
124 | $existingOrders = array(); |
||
125 | /** @var Query\Expr\OrderBy $order */ |
||
126 | foreach ($queryBuilder->getDQLPart('orderBy') as $order) { |
||
127 | foreach ($order->getParts() as $part) { |
||
128 | $existingOrders[] = trim(str_replace(array(Criteria::DESC, Criteria::ASC), '', $part)); |
||
129 | } |
||
130 | } |
||
131 | |||
132 | foreach ($identifierFields as $identifierField) { |
||
133 | $order = $rootAlias.'.'.$identifierField; |
||
134 | if (!in_array($order, $existingOrders)) { |
||
135 | $queryBuilder->addOrderBy( |
||
136 | $order, |
||
137 | $this->getSortOrder() // reusing the sort order is the most natural way to go |
||
138 | ); |
||
139 | } |
||
140 | } |
||
141 | |||
142 | $query = $this->getFixedQueryBuilder($queryBuilder)->getQuery(); |
||
143 | foreach ($this->hints as $name => $value) { |
||
144 | $query->setHint($name, $value); |
||
145 | } |
||
146 | return $query->execute($params, $hydrationMode); |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Sets a query hint. If the hint name is not recognized, it is silently ignored. |
||
151 | * |
||
152 | * @param string $name The name of the hint. |
||
153 | * @param mixed $value The value of the hint. |
||
154 | * |
||
155 | * @return ProxyQueryInterface |
||
156 | */ |
||
157 | public function setHint($name, $value) |
||
158 | { |
||
159 | $this->hints[$name] = $value; |
||
160 | |||
161 | return $this; |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * {@inheritdoc} |
||
166 | */ |
||
167 | public function setSortBy($parentAssociationMappings, $fieldMapping) |
||
168 | { |
||
169 | $alias = $this->entityJoin($parentAssociationMappings); |
||
170 | $this->sortBy = $alias.'.'.$fieldMapping['fieldName']; |
||
171 | |||
172 | return $this; |
||
173 | } |
||
174 | |||
175 | /** |
||
176 | * {@inheritdoc} |
||
177 | */ |
||
178 | public function getSortBy() |
||
179 | { |
||
180 | return $this->sortBy; |
||
181 | } |
||
182 | |||
183 | /** |
||
184 | * {@inheritdoc} |
||
185 | */ |
||
186 | public function setSortOrder($sortOrder) |
||
187 | { |
||
188 | $this->sortOrder = $sortOrder; |
||
189 | |||
190 | return $this; |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * {@inheritdoc} |
||
195 | */ |
||
196 | public function getSortOrder() |
||
197 | { |
||
198 | return $this->sortOrder; |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * {@inheritdoc} |
||
203 | */ |
||
204 | public function getSingleScalarResult() |
||
205 | { |
||
206 | $query = $this->queryBuilder->getQuery(); |
||
207 | |||
208 | return $query->getSingleScalarResult(); |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * @return mixed |
||
213 | */ |
||
214 | public function getQueryBuilder() |
||
218 | |||
219 | /** |
||
220 | * {@inheritdoc} |
||
221 | */ |
||
222 | public function setFirstResult($firstResult) |
||
228 | |||
229 | /** |
||
230 | * {@inheritdoc} |
||
231 | */ |
||
232 | public function getFirstResult() |
||
236 | |||
237 | /** |
||
238 | * {@inheritdoc} |
||
239 | */ |
||
240 | public function setMaxResults($maxResults) |
||
246 | |||
247 | /** |
||
248 | * {@inheritdoc} |
||
249 | */ |
||
250 | public function getMaxResults() |
||
254 | |||
255 | /** |
||
256 | * {@inheritdoc} |
||
257 | */ |
||
258 | public function getUniqueParameterId() |
||
262 | |||
263 | /** |
||
264 | * {@inheritdoc} |
||
265 | */ |
||
266 | public function entityJoin(array $associationMappings) |
||
267 | { |
||
268 | $alias = $this->queryBuilder->getRootAlias(); |
||
|
|||
269 | |||
270 | $newAlias = 's'; |
||
271 | |||
272 | $joinedEntities = $this->queryBuilder->getDQLPart('join'); |
||
273 | |||
300 | |||
301 | /** |
||
302 | * This method alters the query to return a clean set of object with a working |
||
303 | * set of Object. |
||
304 | * |
||
305 | * @param QueryBuilder $queryBuilder |
||
306 | * |
||
307 | * @return QueryBuilder |
||
308 | */ |
||
309 | protected function getFixedQueryBuilder(QueryBuilder $queryBuilder) |
||
376 | |||
377 | private function addOrderedColumns(QueryBuilder $queryBuilder) |
||
387 | } |
||
388 |
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.