Complex classes like AbstractFieldsConfigurationFactory 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 AbstractFieldsConfigurationFactory, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
26 | abstract class AbstractFieldsConfigurationFactory |
||
27 | { |
||
28 | /** |
||
29 | * @var Types |
||
30 | */ |
||
31 | private $types; |
||
32 | |||
33 | /** |
||
34 | * @var EntityManager |
||
35 | */ |
||
36 | private $entityManager; |
||
37 | |||
38 | /** |
||
39 | * Doctrine metadata for the entity |
||
40 | * |
||
41 | * @var ClassMetadata |
||
42 | */ |
||
43 | private $metadata; |
||
44 | |||
45 | /** |
||
46 | * The identity field name, eg: "id" |
||
47 | * |
||
48 | * @var string |
||
49 | */ |
||
50 | private $identityField; |
||
51 | |||
52 | 14 | public function __construct(Types $types, EntityManager $entityManager) |
|
57 | |||
58 | /** |
||
59 | * Returns the regexp pattern to filter method names |
||
60 | */ |
||
61 | abstract protected function getMethodPattern(): string; |
||
62 | |||
63 | /** |
||
64 | * Get the entire configuration for a method |
||
65 | * |
||
66 | * @param ReflectionMethod $method |
||
67 | * |
||
68 | * @return null|array |
||
69 | */ |
||
70 | abstract protected function methodToConfiguration(ReflectionMethod $method): ?array; |
||
71 | |||
72 | /** |
||
73 | * Create a configuration for all fields of Doctrine entity |
||
74 | * |
||
75 | * @param string $className |
||
76 | * |
||
77 | * @return array |
||
78 | */ |
||
79 | 14 | public function create(string $className): array |
|
111 | |||
112 | /** |
||
113 | * Returns whether the getter is excluded |
||
114 | * |
||
115 | * @param ReflectionMethod $method |
||
116 | * |
||
117 | * @return bool |
||
118 | */ |
||
119 | 14 | private function isExcluded(ReflectionMethod $method): bool |
|
125 | |||
126 | /** |
||
127 | * Get annotation reader |
||
128 | * |
||
129 | * @return Reader |
||
130 | */ |
||
131 | 14 | protected function getAnnotationReader(): Reader |
|
135 | |||
136 | /** |
||
137 | * Get instance of GraphQL type from a PHP class name |
||
138 | * |
||
139 | * Supported syntaxes are the following: |
||
140 | * |
||
141 | * - `?MyType` |
||
142 | * - `null|MyType` |
||
143 | * - `MyType|null` |
||
144 | * - `MyType[]` |
||
145 | * - `?MyType[]` |
||
146 | * - `null|MyType[]` |
||
147 | * - `MyType[]|null` |
||
148 | * |
||
149 | * @param ReflectionMethod $method |
||
150 | * @param null|string $typeDeclaration |
||
151 | * @param bool $isEntityId |
||
152 | * |
||
153 | * @return null|Type |
||
154 | */ |
||
155 | 14 | protected function getTypeFromPhpDeclaration(ReflectionMethod $method, ?string $typeDeclaration, bool $isEntityId = false): ?Type |
|
179 | |||
180 | /** |
||
181 | * Prepend namespace of the method if the class actually exists |
||
182 | * |
||
183 | * @param ReflectionMethod $method |
||
184 | * @param string $type |
||
185 | * |
||
186 | * @return string |
||
187 | */ |
||
188 | 10 | private function adjustNamespace(ReflectionMethod $method, string $type): string |
|
198 | |||
199 | /** |
||
200 | * Get a GraphQL type instance from PHP type hinted type, possibly looking up the content of collections |
||
201 | * |
||
202 | * @param ReflectionMethod $method |
||
203 | * @param string $fieldName |
||
204 | * |
||
205 | * @throws Exception |
||
206 | * |
||
207 | * @return null|Type |
||
208 | */ |
||
209 | 7 | protected function getTypeFromReturnTypeHint(ReflectionMethod $method, string $fieldName): ?Type |
|
233 | |||
234 | /** |
||
235 | * Convert a reflected type to GraphQL Type |
||
236 | * |
||
237 | * @param ReflectionType $reflectionType |
||
238 | * @param bool $isEntityId |
||
239 | * |
||
240 | * @return Type |
||
241 | */ |
||
242 | 7 | protected function reflectionTypeToType(ReflectionType $reflectionType, bool $isEntityId = false): Type |
|
256 | |||
257 | /** |
||
258 | * Look up which field is the ID |
||
259 | * |
||
260 | * @param string $className |
||
261 | */ |
||
262 | 14 | private function findIdentityField(string $className): void |
|
271 | |||
272 | /** |
||
273 | * Returns the fully qualified method name |
||
274 | * |
||
275 | * @param ReflectionMethod $method |
||
276 | * |
||
277 | * @return string |
||
278 | */ |
||
279 | 7 | protected function getMethodFullName(ReflectionMethod $method): string |
|
283 | |||
284 | /** |
||
285 | * Throws exception if type is an array |
||
286 | * |
||
287 | * @param ReflectionParameter $param |
||
288 | * @param null|string $type |
||
289 | * |
||
290 | * @throws Exception |
||
291 | */ |
||
292 | 10 | protected function throwIfArray(ReflectionParameter $param, ?string $type): void |
|
298 | |||
299 | /** |
||
300 | * Returns whether the given field name is the identity for the entity |
||
301 | * |
||
302 | * @param string $fieldName |
||
303 | * |
||
304 | * @return bool |
||
305 | */ |
||
306 | 11 | protected function isIdentityField(string $fieldName): bool |
|
310 | |||
311 | /** |
||
312 | * Finds the target entity in the given association |
||
313 | * |
||
314 | * @param string $fieldName |
||
315 | * |
||
316 | * @return null|string |
||
317 | */ |
||
318 | 3 | private function getTargetEntity(string $fieldName): ?string |
|
322 | |||
323 | /** |
||
324 | * Return the default value, if any, of the property for the current entity |
||
325 | * |
||
326 | * It does take into account that the property might be defined on a parent class |
||
327 | * of entity. And it will find it if that is the case. |
||
328 | * |
||
329 | * @param string $fieldName |
||
330 | * |
||
331 | * @return mixed |
||
332 | */ |
||
333 | 4 | protected function getPropertyDefaultValue(string $fieldName) |
|
343 | |||
344 | /** |
||
345 | * Returns a type from our registry |
||
346 | * |
||
347 | * @param string $type |
||
348 | * @param bool $isEntityId |
||
349 | * |
||
350 | * @return Type |
||
351 | */ |
||
352 | 11 | private function getTypeFromRegistry(string $type, bool $isEntityId): Type |
|
360 | |||
361 | /** |
||
362 | * Input with default values cannot be non-null |
||
363 | * |
||
364 | * @param Type $type |
||
365 | * @param mixed $defaultValue |
||
366 | * |
||
367 | * @return Type |
||
368 | */ |
||
369 | 10 | protected function nonNullIfHasDefault(?Type $type, $defaultValue): ?Type |
|
377 | |||
378 | /** |
||
379 | * Throws exception if argument type is invalid |
||
380 | * |
||
381 | * @param ReflectionParameter $param |
||
382 | * @param Type $type |
||
383 | * @param string $annotation |
||
384 | * |
||
385 | * @throws Exception |
||
386 | */ |
||
387 | 10 | protected function throwIfNotInputType(ReflectionParameter $param, ?Type $type, string $annotation): void |
|
401 | } |
||
402 |
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.