Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like JsonApiSerializer 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 JsonApiSerializer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
18 | class JsonApiSerializer extends ArraySerializer |
||
19 | { |
||
20 | protected $baseUrl; |
||
21 | protected $rootObjects; |
||
22 | |||
23 | /** |
||
24 | * JsonApiSerializer constructor. |
||
25 | * |
||
26 | * @param string $baseUrl |
||
27 | */ |
||
28 | 38 | public function __construct($baseUrl = null) |
|
33 | |||
34 | /** |
||
35 | * Serialize a collection. |
||
36 | * |
||
37 | * @param string $resourceKey |
||
38 | * @param array $data |
||
39 | * |
||
40 | * @return array |
||
41 | */ |
||
42 | 24 | public function collection($resourceKey, array $data) |
|
52 | |||
53 | /** |
||
54 | * Serialize an item. |
||
55 | * |
||
56 | * @param string $resourceKey |
||
57 | * @param array $data |
||
58 | * |
||
59 | * @return array |
||
60 | */ |
||
61 | 38 | public function item($resourceKey, array $data) |
|
100 | |||
101 | /** |
||
102 | * Serialize the paginator. |
||
103 | * |
||
104 | * @param PaginatorInterface $paginator |
||
105 | * |
||
106 | * @return array |
||
107 | */ |
||
108 | 3 | public function paginator(PaginatorInterface $paginator) |
|
138 | |||
139 | /** |
||
140 | * Serialize the meta. |
||
141 | * |
||
142 | * @param array $meta |
||
143 | * |
||
144 | * @return array |
||
145 | */ |
||
146 | 37 | public function meta(array $meta) |
|
161 | |||
162 | /** |
||
163 | * @return array |
||
164 | */ |
||
165 | 2 | public function null() |
|
171 | |||
172 | /** |
||
173 | * Serialize the included data. |
||
174 | * |
||
175 | * @param ResourceInterface $resource |
||
176 | * @param array $data |
||
177 | * |
||
178 | * @return array |
||
179 | */ |
||
180 | 37 | public function includedData(ResourceInterface $resource, array $data) |
|
197 | |||
198 | /** |
||
199 | * Indicates if includes should be side-loaded. |
||
200 | * |
||
201 | * @return bool |
||
202 | */ |
||
203 | 38 | public function sideloadIncludes() |
|
207 | |||
208 | /** |
||
209 | * @param array $data |
||
210 | * @param array $includedData |
||
211 | * |
||
212 | * @return array |
||
213 | */ |
||
214 | 37 | public function injectData($data, $includedData) |
|
224 | |||
225 | /** |
||
226 | * Hook to manipulate the final sideloaded includes. |
||
227 | * The JSON API specification does not allow the root object to be included |
||
228 | * into the sideloaded `included`-array. We have to make sure it is |
||
229 | * filtered out, in case some object links to the root object in a |
||
230 | * relationship. |
||
231 | * |
||
232 | * @param array $includedData |
||
233 | * @param array $data |
||
234 | * |
||
235 | * @return array |
||
236 | */ |
||
237 | 37 | public function filterIncludes($includedData, $data) |
|
254 | |||
255 | /** |
||
256 | * Get the mandatory fields for the serializer |
||
257 | * |
||
258 | * @return array |
||
259 | */ |
||
260 | 4 | public function getMandatoryFields() |
|
264 | |||
265 | /** |
||
266 | * Filter function to delete root objects from array. |
||
267 | * |
||
268 | * @param array $object |
||
269 | * |
||
270 | * @return bool |
||
271 | */ |
||
272 | 18 | protected function filterRootObject($object) |
|
276 | |||
277 | /** |
||
278 | * Set the root objects of the JSON API tree. |
||
279 | * |
||
280 | * @param array $objects |
||
281 | */ |
||
282 | 18 | protected function setRootObjects(array $objects = []) |
|
288 | |||
289 | /** |
||
290 | * Determines whether an object is a root object of the JSON API tree. |
||
291 | * |
||
292 | * @param array $object |
||
293 | * |
||
294 | * @return bool |
||
295 | */ |
||
296 | 18 | protected function isRootObject($object) |
|
301 | |||
302 | /** |
||
303 | * @param array|null $data |
||
304 | * |
||
305 | * @return bool |
||
306 | */ |
||
307 | 30 | View Code Duplication | protected function isCollection($data) |
316 | |||
317 | /** |
||
318 | * @param array|null $data |
||
319 | * |
||
320 | * @return bool |
||
321 | */ |
||
322 | 22 | View Code Duplication | protected function isNull($data) |
330 | |||
331 | /** |
||
332 | * @param array|null $data |
||
333 | * |
||
334 | * @return bool |
||
335 | */ |
||
336 | 21 | View Code Duplication | protected function isEmpty($data) |
344 | |||
345 | /** |
||
346 | * @param array $data |
||
347 | * @param array $relationships |
||
348 | * |
||
349 | * @return array |
||
350 | */ |
||
351 | 22 | protected function fillRelationships($data, $relationships) |
|
365 | |||
366 | /** |
||
367 | * @param array $includedData |
||
368 | * |
||
369 | * @return array |
||
370 | */ |
||
371 | 37 | protected function parseRelationships($includedData) |
|
386 | |||
387 | /** |
||
388 | * @param array $data |
||
389 | * |
||
390 | * @return integer |
||
391 | */ |
||
392 | 38 | protected function getIdFromData(array $data) |
|
401 | |||
402 | /** |
||
403 | * Keep all sideloaded inclusion data on the top level. |
||
404 | * |
||
405 | * @param array $data |
||
406 | * |
||
407 | * @return array |
||
408 | */ |
||
409 | 37 | protected function pullOutNestedIncludedData(array $data) |
|
424 | |||
425 | /** |
||
426 | * Whether or not the serializer should include `links` for resource objects. |
||
427 | * |
||
428 | * @return bool |
||
429 | */ |
||
430 | 37 | protected function shouldIncludeLinks() |
|
434 | |||
435 | /** |
||
436 | * Check if the objects are part of a collection or not |
||
437 | * |
||
438 | * @param array $includeObject |
||
439 | * |
||
440 | * @return array |
||
441 | */ |
||
442 | 20 | private function createIncludeObjects($includeObject) |
|
454 | |||
455 | /** |
||
456 | * Sets the RootObjects, either as collection or not. |
||
457 | * |
||
458 | * @param array $data |
||
459 | */ |
||
460 | 18 | private function createRootObjects($data) |
|
468 | |||
469 | |||
470 | /** |
||
471 | * Loops over the relationships of the provided data and formats it |
||
472 | * |
||
473 | * @param array $data |
||
474 | * @param array $relationship |
||
475 | * @param string $key |
||
476 | * |
||
477 | * @return array |
||
478 | */ |
||
479 | 9 | private function fillRelationshipAsCollection($data, $relationship, $key) |
|
487 | |||
488 | |||
489 | /** |
||
490 | * @param array $data |
||
491 | * @param array $relationship |
||
492 | * @param string $key |
||
493 | * |
||
494 | * @return array |
||
495 | */ |
||
496 | 15 | private function fillRelationshipAsSingleResource($data, $relationship, $key) |
|
502 | |||
503 | /** |
||
504 | * @param mixed $includeKey |
||
505 | * @param array $relationships |
||
506 | * @param array|null $includeObject |
||
507 | * @param string $key |
||
508 | * |
||
509 | * @return array |
||
510 | */ |
||
511 | 22 | private function buildRelationships($includeKey, $relationships, $includeObject, $key) |
|
537 | |||
538 | /** |
||
539 | * @param mixed $includeKey |
||
540 | * @param array $relationships |
||
541 | * |
||
542 | * @return array |
||
543 | */ |
||
544 | 22 | private function addIncludekeyToRelationsIfNotSet($includeKey, $relationships) |
|
553 | |||
554 | /** |
||
555 | * @param array $includeObject |
||
556 | * @param array $relationship |
||
557 | * |
||
558 | * @return array |
||
559 | */ |
||
560 | 11 | private function addIncludedDataToRelationship($includeObject, $relationship) |
|
571 | |||
572 | /** |
||
573 | * {@inheritdoc} |
||
574 | */ |
||
575 | 36 | public function injectAvailableIncludeData($data, $availableIncludes) |
|
596 | |||
597 | /** |
||
598 | * Adds links for all available includes to a single resource. |
||
599 | * |
||
600 | * @param array $resource The resource to add relationship links to |
||
601 | * @param string $relationshipKey The resource key of the relationship |
||
602 | */ |
||
603 | 12 | private function addRelationshipLinks($resource, $relationshipKey) |
|
621 | |||
622 | /** |
||
623 | * @param array $includeObjects |
||
624 | * @param array $linkedIds |
||
625 | * @param array $serializedData |
||
626 | * |
||
627 | * @return array |
||
628 | */ |
||
629 | 20 | private function serializeIncludedObjectsWithCacheKey($includeObjects, $linkedIds, $serializedData) |
|
642 | } |
||
643 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.