Complex classes like NativeQueryBuilder 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 NativeQueryBuilder, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
38 | class NativeQueryBuilder extends Doctrine\DBAL\Query\QueryBuilder |
||
39 | { |
||
40 | |||
41 | use \Kdyby\StrictObjects\Scream; |
||
42 | |||
43 | /** |
||
44 | * @var Mapping\ResultSetMappingBuilder |
||
45 | */ |
||
46 | private $rsm; |
||
47 | |||
48 | /** |
||
49 | * @var Doctrine\ORM\EntityManager |
||
50 | */ |
||
51 | private $em; |
||
52 | |||
53 | /** |
||
54 | * @var int |
||
55 | */ |
||
56 | private $defaultRenameMode = Doctrine\ORM\Query\ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT; |
||
57 | |||
58 | |||
59 | |||
60 | public function __construct(Doctrine\ORM\EntityManager $em) |
||
|
|||
61 | { |
||
62 | parent::__construct($em->getConnection()); |
||
63 | $this->em = $em; |
||
64 | } |
||
65 | |||
66 | |||
67 | |||
68 | /** |
||
69 | * @return NativeQueryWrapper |
||
70 | */ |
||
71 | public function getQuery() |
||
72 | { |
||
73 | $query = new Doctrine\ORM\NativeQuery($this->em); |
||
74 | $query->setResultSetMapping($this->getResultSetMapper()); |
||
75 | $query->setParameters($this->getParameters()); |
||
76 | |||
77 | $wrapped = new NativeQueryWrapper($query); |
||
78 | $wrapped->setFirstResult($this->getFirstResult()); |
||
79 | $wrapped->setMaxResults($this->getMaxResults()); |
||
80 | |||
81 | $hasSelect = (bool)$this->getQueryPart('select'); |
||
82 | if (!$hasSelect && $this->getType() === self::SELECT) { |
||
83 | $select = $this->getResultSetMapper()->generateSelectClause(); |
||
84 | $this->select($select ?: '*'); |
||
85 | } |
||
86 | |||
87 | $query->setSQL($this->getSQL()); |
||
88 | |||
89 | $this->setFirstResult($wrapped->getFirstResult()); |
||
90 | $this->setMaxResults($wrapped->getMaxResults()); |
||
91 | |||
92 | if (!$hasSelect && $this->getType() === self::SELECT) { |
||
93 | $this->resetQueryPart('select'); |
||
94 | } |
||
95 | |||
96 | $rsm = $this->getResultSetMapper(); |
||
97 | if (empty($rsm->fieldMappings) && empty($rsm->scalarMappings)) { |
||
98 | throw new InvalidStateException("No field or columns mapping found, please configure the ResultSetMapper and some fields."); |
||
99 | } |
||
100 | |||
101 | return $wrapped; |
||
102 | } |
||
103 | |||
104 | |||
105 | |||
106 | /** |
||
107 | * @param int $defaultRenameMode |
||
108 | * @return NativeQueryBuilder |
||
109 | */ |
||
110 | public function setDefaultRenameMode($defaultRenameMode) |
||
111 | { |
||
112 | if ($this->rsm !== NULL) { |
||
113 | throw new InvalidStateException("It's too late for changing rename mode for ResultSetMappingBuilder, it has already been created. Call this method earlier."); |
||
114 | } |
||
115 | |||
116 | $this->defaultRenameMode = $defaultRenameMode; |
||
117 | return $this; |
||
118 | } |
||
119 | |||
120 | |||
121 | |||
122 | public function getResultSetMapper() |
||
123 | { |
||
124 | if ($this->rsm === NULL) { |
||
125 | $this->rsm = new Mapping\ResultSetMappingBuilder($this->em, $this->defaultRenameMode); |
||
126 | } |
||
127 | |||
128 | return $this->rsm; |
||
129 | } |
||
130 | |||
131 | |||
132 | |||
133 | /** |
||
134 | * @param string $tableAlias |
||
135 | * @param string|array $columns |
||
136 | * @return NativeQueryBuilder |
||
137 | */ |
||
138 | public function addColumn($tableAlias, $columns) |
||
139 | { |
||
140 | $rsm = $this->getResultSetMapper(); |
||
141 | |||
142 | $args = func_get_args(); |
||
143 | array_shift($args); // shit tableAlias |
||
144 | |||
145 | $class = $this->em->getClassMetadata($rsm->aliasMap[$tableAlias]); |
||
146 | |||
147 | foreach (is_array($columns) ? $columns : $args as $column) { |
||
148 | try { |
||
149 | $field = $class->getFieldForColumn($column); |
||
150 | if ($class->hasField($field)) { |
||
151 | $type = $class->getTypeOfField($field); |
||
152 | |||
153 | } else { |
||
154 | $type = $class->hasAssociation($field) ? 'integer' : 'string'; |
||
155 | } |
||
156 | |||
157 | } catch (Doctrine\ORM\Mapping\MappingException $e) { |
||
158 | $type = 'string'; |
||
159 | |||
160 | if ($class->discriminatorColumn['fieldName'] === $column) { |
||
161 | $type = $class->discriminatorColumn['type']; |
||
162 | } |
||
163 | } |
||
164 | |||
165 | $this->addSelect("{$tableAlias}.{$column} as {$tableAlias}_{$column}"); |
||
166 | $rsm->addScalarResult("{$tableAlias}_{$column}", "{$tableAlias}_{$column}", $type); |
||
167 | } |
||
168 | |||
169 | return $this; |
||
170 | } |
||
171 | |||
172 | |||
173 | |||
174 | public function addSelect($select = NULL) |
||
175 | { |
||
176 | $selects = is_array($select) ? $select : func_get_args(); |
||
177 | foreach ($selects as &$arg) { |
||
178 | if ($arg instanceof Doctrine\DBAL\Query\QueryBuilder) { |
||
179 | $arg = '(' . $arg->getSQL() . ')'; |
||
180 | } |
||
181 | } |
||
182 | |||
183 | return parent::addSelect($selects); |
||
184 | } |
||
185 | |||
186 | |||
187 | |||
188 | /** |
||
189 | * {@inheritdoc} |
||
190 | */ |
||
191 | public function from($from, $alias = NULL) |
||
195 | |||
196 | |||
197 | |||
198 | /** |
||
199 | * {@inheritdoc} |
||
200 | * @return NativeQueryBuilder |
||
201 | */ |
||
202 | public function join($fromAlias, $join, $alias, $condition = null) |
||
206 | |||
207 | |||
208 | |||
209 | /** |
||
210 | * {@inheritdoc} |
||
211 | * @return NativeQueryBuilder |
||
212 | */ |
||
213 | public function innerJoin($fromAlias, $join, $alias, $condition = null) |
||
221 | |||
222 | |||
223 | |||
224 | /** |
||
225 | * {@inheritdoc} |
||
226 | * @return NativeQueryBuilder |
||
227 | */ |
||
228 | public function leftJoin($fromAlias, $join, $alias, $condition = null) |
||
236 | |||
237 | |||
238 | |||
239 | /** |
||
240 | * {@inheritdoc} |
||
241 | * @return NativeQueryBuilder |
||
242 | */ |
||
243 | public function rightJoin($fromAlias, $join, $alias, $condition = null) |
||
251 | |||
252 | |||
253 | |||
254 | /** |
||
255 | * @param string $table |
||
256 | * @param string|null $alias |
||
257 | * @param string $joinedFrom |
||
258 | * @throws \Doctrine\ORM\Mapping\MappingException |
||
259 | * @return string |
||
260 | */ |
||
261 | protected function addTableResultMapping($table, $alias, $joinedFrom = NULL) |
||
321 | |||
322 | |||
323 | |||
324 | /** |
||
325 | * {@inheritdoc} |
||
326 | * @return NativeQueryBuilder |
||
327 | */ |
||
328 | public function where($predicates) |
||
332 | |||
333 | |||
334 | |||
335 | /** |
||
336 | * {@inheritdoc} |
||
337 | * @return NativeQueryBuilder |
||
338 | */ |
||
339 | public function andWhere($where) |
||
343 | |||
344 | |||
345 | |||
346 | /** |
||
347 | * {@inheritdoc} |
||
348 | * @return NativeQueryBuilder |
||
349 | */ |
||
350 | public function orWhere($where) |
||
354 | |||
355 | } |
||
356 |
The
EntityManager
might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:If that code throws an exception and the
EntityManager
is closed. Any other code which depends on the same instance of theEntityManager
during this request will fail.On the other hand, if you instead inject the
ManagerRegistry
, thegetManager()
method guarantees that you will always get a usable manager instance.