Complex classes like JsonApi 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 JsonApi, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class JsonApi |
||
29 | { |
||
30 | /** |
||
31 | * Format single or multiple data items in JSON API format. |
||
32 | * |
||
33 | * @param mixed $items Items to be formatted. |
||
34 | * @param string|null $type Type of items. If missing, an attempt is made to obtain this info from each item's data. |
||
35 | * @return array |
||
36 | * @throws \InvalidArgumentException Throws an exception if `$item` could not be converted to array, or |
||
37 | * if required key `id` is unset or empty. |
||
38 | */ |
||
39 | public static function formatData($items, $type = null) |
||
40 | { |
||
41 | if ($items instanceof Query || $items instanceof CollectionInterface) { |
||
42 | $items = $items->toList(); |
||
43 | } |
||
44 | |||
45 | if (!is_array($items) || !Hash::numeric(array_keys($items))) { |
||
46 | return static::formatItem($items, $type, false); |
||
47 | } |
||
48 | |||
49 | $data = []; |
||
50 | foreach ($items as $item) { |
||
51 | $data[] = static::formatItem($item, $type, true); |
||
52 | } |
||
53 | |||
54 | return $data; |
||
55 | } |
||
56 | |||
57 | /** |
||
58 | * Extract type and API endpoint for item. |
||
59 | * |
||
60 | * @param \Cake\ORM\Entity|array $item Item. |
||
61 | * @param string|null $type Original item type. |
||
62 | * @return array Array with item's type and API endpoint. |
||
63 | */ |
||
64 | protected static function extractType($item, $type) |
||
78 | |||
79 | /** |
||
80 | * Build URL to be used in `links` object. |
||
81 | * |
||
82 | * @param string $name Route name. |
||
83 | * @param string $endpoint Endpoint. |
||
84 | * @param string $type Resource type. |
||
85 | * @param array $options Additional options. |
||
86 | * @return string|null |
||
87 | */ |
||
88 | protected static function buildUrl($name, $endpoint, $type, array $options = []) |
||
117 | |||
118 | /** |
||
119 | * Extract item's ID and attributes. |
||
120 | * |
||
121 | * @param array $item Item's data. |
||
122 | * @return array Array with item's ID, attributes, and metadata. |
||
123 | */ |
||
124 | protected static function extractAttributes(array $item) |
||
125 | { |
||
126 | if (empty($item['id'])) { |
||
127 | throw new \InvalidArgumentException('Key `id` is mandatory'); |
||
128 | } |
||
129 | $id = (string)$item['id']; |
||
130 | $meta = Hash::get($item, 'meta', []); |
||
131 | unset($item['id'], $item['type'], $item['meta']); |
||
132 | |||
133 | array_walk( |
||
134 | $item, |
||
135 | function (&$attribute) { |
||
136 | if ($attribute instanceof \JsonSerializable) { |
||
137 | $attribute = json_decode(json_encode($attribute), true); |
||
138 | } |
||
139 | } |
||
140 | ); |
||
141 | |||
142 | if (!empty($item['_joinData'])) { |
||
143 | $meta += $item['_joinData']; |
||
144 | } |
||
145 | unset($item['_joinData']); |
||
146 | |||
147 | return [$id, $item, $meta]; |
||
148 | } |
||
149 | |||
150 | /** |
||
151 | * Extract relationships for an entity. |
||
152 | * |
||
153 | * @param Entity $entity Entity item. |
||
154 | * @param string $endpoint Default API endpoint for entity type. |
||
155 | * @param string|null $type Type of item. |
||
156 | * @return array |
||
157 | */ |
||
158 | protected static function extractRelationships(Entity $entity, $endpoint, $type = null) |
||
189 | |||
190 | /** |
||
191 | * Format single data item in JSON API format. |
||
192 | * |
||
193 | * @param \Cake\ORM\Entity|array $item Single entity item to be formatted. |
||
194 | * @param string|null $type Type of item. If missing, an attempt is made to obtain this info from item's data. |
||
195 | * @param bool $showLink Display item url in 'links.self', default is true |
||
196 | * @return array |
||
197 | * @throws \InvalidArgumentException Throws an exception if `$item` could not be converted to array, or |
||
198 | * if required key `id` is unset or empty. |
||
199 | */ |
||
200 | protected static function formatItem($item, $type = null, $showLink = true) |
||
237 | |||
238 | /** |
||
239 | * Parse single or multiple data items from JSON API format. |
||
240 | * |
||
241 | * @param array $data Items to be parsed. |
||
242 | * @return array |
||
243 | * @throws \InvalidArgumentException Throws an exception if one of required keys `id` and `type` is unset or empty. |
||
244 | */ |
||
245 | public static function parseData(array $data) |
||
262 | |||
263 | /** |
||
264 | * Parse single data item from JSON API format. |
||
265 | * |
||
266 | * @param array $item Item to be parsed. |
||
267 | * @return array |
||
268 | * @throws \InvalidArgumentException Throws an exception if one of required keys `id` and `type` is unset or empty. |
||
269 | */ |
||
270 | protected static function parseItem(array $item) |
||
293 | } |
||
294 |