Total Complexity | 45 |
Total Lines | 564 |
Duplicated Lines | 0 % |
Changes | 7 | ||
Bugs | 5 | Features | 0 |
Complex classes like ModulesController 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.
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 ModulesController, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
33 | class ModulesController extends AppController |
||
34 | { |
||
35 | /** |
||
36 | * Object type currently used |
||
37 | * |
||
38 | * @var string |
||
39 | */ |
||
40 | protected $objectType = null; |
||
41 | |||
42 | /** |
||
43 | * {@inheritDoc} |
||
44 | */ |
||
45 | public function initialize(): void |
||
46 | { |
||
47 | parent::initialize(); |
||
48 | |||
49 | $this->loadComponent('Categories'); |
||
50 | $this->loadComponent('History'); |
||
51 | $this->loadComponent('ObjectsEditors'); |
||
52 | $this->loadComponent('Properties'); |
||
53 | $this->loadComponent('ProjectConfiguration'); |
||
54 | $this->loadComponent('Query'); |
||
55 | $this->loadComponent('Thumbs'); |
||
56 | $this->loadComponent('BEdita/WebTools.ApiFormatter'); |
||
57 | |||
58 | if (!empty($this->request)) { |
||
59 | $this->objectType = $this->request->getParam('object_type'); |
||
60 | $this->Modules->setConfig('currentModuleName', $this->objectType); |
||
61 | $this->Schema->setConfig('type', $this->objectType); |
||
62 | } |
||
63 | |||
64 | $this->Security->setConfig('unlockedActions', ['save']); |
||
65 | } |
||
66 | |||
67 | /** |
||
68 | * {@inheritDoc} |
||
69 | * @codeCoverageIgnore |
||
70 | */ |
||
71 | public function beforeRender(Event $event): ?Response |
||
76 | } |
||
77 | |||
78 | /** |
||
79 | * Display resources list. |
||
80 | * |
||
81 | * @return \Cake\Http\Response|null |
||
82 | */ |
||
83 | public function index(): ?Response |
||
84 | { |
||
85 | $this->request->allowMethod(['get']); |
||
86 | |||
87 | // handle filter and query parameters using session |
||
88 | $result = $this->applySessionFilter(); |
||
89 | if ($result != null) { |
||
90 | return $result; |
||
91 | } |
||
92 | |||
93 | try { |
||
94 | $response = $this->apiClient->getObjects($this->objectType, $this->Query->index()); |
||
95 | } catch (BEditaClientException $e) { |
||
96 | $this->log($e, LogLevel::ERROR); |
||
97 | $this->Flash->error($e->getMessage(), ['params' => $e]); |
||
98 | // remove session filter to avoid error repetition |
||
99 | $session = $this->request->getSession(); |
||
100 | $session->delete(sprintf('%s.filter', $this->Modules->getConfig('currentModuleName'))); |
||
101 | |||
102 | return $this->redirect(['_name' => 'dashboard']); |
||
103 | } |
||
104 | |||
105 | $this->ProjectConfiguration->read(); |
||
106 | |||
107 | $response = $this->ApiFormatter->embedIncluded((array)$response); |
||
108 | $objects = (array)Hash::get($response, 'data'); |
||
109 | $this->set('objects', $objects); |
||
110 | $this->set('meta', (array)Hash::get($response, 'meta')); |
||
111 | $this->set('links', (array)Hash::get($response, 'links')); |
||
112 | $this->set('types', ['right' => $this->Schema->descendants($this->objectType)]); |
||
113 | |||
114 | $this->set('properties', $this->Properties->indexList($this->objectType)); |
||
115 | |||
116 | // base/custom filters for filter view |
||
117 | $this->set('filter', $this->Properties->filterList($this->objectType)); |
||
118 | |||
119 | // base/custom bulk actions for index view |
||
120 | $this->set('bulkActions', $this->Properties->bulkList($this->objectType)); |
||
121 | |||
122 | // objectTypes schema |
||
123 | $this->set('schema', $this->getSchemaForIndex($this->objectType)); |
||
124 | |||
125 | // set prevNext for views navigations |
||
126 | $this->setObjectNav($objects); |
||
127 | |||
128 | return null; |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * View single resource. |
||
133 | * |
||
134 | * @param string|int $id Resource ID. |
||
135 | * @return \Cake\Http\Response|null |
||
136 | */ |
||
137 | public function view($id): ?Response |
||
190 | } |
||
191 | |||
192 | /** |
||
193 | * View single resource by id, doing a proper redirect (302) to resource module view by type. |
||
194 | * If no resource found by ID, redirect to referer. |
||
195 | * |
||
196 | * @param string|int $id Resource ID. |
||
197 | * @return \Cake\Http\Response|null |
||
198 | */ |
||
199 | public function uname($id): ?Response |
||
217 | } |
||
218 | |||
219 | /** |
||
220 | * Display new resource form. |
||
221 | * |
||
222 | * @return \Cake\Http\Response|null |
||
223 | */ |
||
224 | public function create(): ?Response |
||
260 | } |
||
261 | |||
262 | /** |
||
263 | * Create new object from ajax request. |
||
264 | * |
||
265 | * @return void |
||
266 | */ |
||
267 | public function save(): void |
||
268 | { |
||
269 | $this->viewBuilder()->setClassName('Json'); // force json response |
||
270 | $this->request->allowMethod(['post']); |
||
271 | $requestData = $this->prepareRequest($this->objectType); |
||
272 | unset($requestData['_csrfToken']); |
||
273 | // extract related objects data |
||
274 | $relatedData = (array)Hash::get($requestData, '_api'); |
||
275 | unset($requestData['_api']); |
||
276 | |||
277 | try { |
||
278 | // upload file (if available) |
||
279 | $this->Modules->upload($requestData); |
||
280 | |||
281 | // save data |
||
282 | $response = $this->apiClient->save($this->objectType, $requestData); |
||
283 | $objectId = (string)Hash::get($response, 'data.id'); |
||
284 | $this->Modules->saveRelated($objectId, $this->objectType, $relatedData); |
||
285 | } catch (BEditaClientException $error) { |
||
286 | $this->log($error->getMessage(), LogLevel::ERROR); |
||
287 | $this->Flash->error($error->getMessage(), ['params' => $error]); |
||
288 | |||
289 | $this->set(['error' => $error->getAttributes()]); |
||
290 | $this->set('_serialize', ['error']); |
||
291 | |||
292 | // set session data to recover form |
||
293 | $this->Modules->setDataFromFailedSave($this->objectType, $requestData); |
||
294 | |||
295 | return; |
||
296 | } |
||
297 | if ($response['data']) { |
||
298 | $response['data'] = [ $response['data'] ]; |
||
299 | } |
||
300 | |||
301 | $this->Thumbs->urls($response); |
||
302 | |||
303 | $this->set((array)$response); |
||
304 | $this->set('_serialize', array_keys($response)); |
||
305 | } |
||
306 | |||
307 | /** |
||
308 | * Clone single object. |
||
309 | * |
||
310 | * @param string|int $id Object ID. |
||
311 | * @return \Cake\Http\Response|null |
||
312 | */ |
||
313 | public function clone($id): ?Response |
||
314 | { |
||
315 | $this->viewBuilder()->setTemplate('view'); |
||
316 | |||
317 | $schema = $this->Schema->getSchema(); |
||
318 | if (!is_array($schema)) { |
||
319 | $this->Flash->error(__('Cannot create abstract objects or objects without schema')); |
||
320 | |||
321 | return $this->redirect(['_name' => 'modules:list', 'object_type' => $this->objectType]); |
||
322 | } |
||
323 | try { |
||
324 | $response = $this->apiClient->getObject($id, $this->objectType); |
||
325 | $attributes = $response['data']['attributes']; |
||
326 | $attributes['uname'] = ''; |
||
327 | unset($attributes['relationships']); |
||
328 | $attributes['title'] = $this->request->getQuery('title'); |
||
329 | } catch (BEditaClientException $e) { |
||
330 | $this->log($e, LogLevel::ERROR); |
||
331 | $this->Flash->error($e->getMessage(), ['params' => $e]); |
||
332 | |||
333 | return $this->redirect(['_name' => 'modules:view', 'object_type' => $this->objectType, 'id' => $id]); |
||
334 | } |
||
335 | $object = [ |
||
336 | 'type' => $this->objectType, |
||
337 | 'attributes' => $attributes, |
||
338 | ]; |
||
339 | $this->History->load($id, $object); |
||
340 | $this->set(compact('object', 'schema')); |
||
341 | $this->set('properties', $this->Properties->viewGroups($object, $this->objectType)); |
||
342 | |||
343 | return null; |
||
344 | } |
||
345 | |||
346 | /** |
||
347 | * Delete single resource. |
||
348 | * |
||
349 | * @return \Cake\Http\Response|null |
||
350 | */ |
||
351 | public function delete(): ?Response |
||
380 | ]); |
||
381 | } |
||
382 | |||
383 | /** |
||
384 | * Relation data load via API => `GET /:object_type/:id/related/:relation` |
||
385 | * |
||
386 | * @param string|int $id The object ID. |
||
387 | * @param string $relation The relation name. |
||
388 | * @return void |
||
389 | */ |
||
390 | public function related($id, string $relation): void |
||
417 | } |
||
418 | |||
419 | /** |
||
420 | * Load resources of $type callig api `GET /:type/` |
||
421 | * Json response |
||
422 | * |
||
423 | * @param string|int $id the object identifier. |
||
424 | * @param string $type the resource type name. |
||
425 | * @return void |
||
426 | */ |
||
427 | public function resources($id, string $type): void |
||
428 | { |
||
429 | $this->request->allowMethod(['get']); |
||
430 | $query = $this->Query->prepare($this->request->getQueryParams()); |
||
431 | try { |
||
432 | $response = $this->apiClient->get($type, $query); |
||
433 | } catch (BEditaClientException $error) { |
||
434 | $this->log($error, LogLevel::ERROR); |
||
435 | |||
436 | $this->set(compact('error')); |
||
437 | $this->set('_serialize', ['error']); |
||
438 | |||
439 | return; |
||
440 | } |
||
441 | |||
442 | $this->set((array)$response); |
||
443 | $this->set('_serialize', array_keys($response)); |
||
444 | } |
||
445 | |||
446 | /** |
||
447 | * Relation data load callig api `GET /:object_type/:id/relationships/:relation` |
||
448 | * Json response |
||
449 | * |
||
450 | * @param string|int $id The object ID. |
||
451 | * @param string $relation The relation name. |
||
452 | * @return void |
||
453 | */ |
||
454 | public function relationships($id, string $relation): void |
||
455 | { |
||
456 | $this->request->allowMethod(['get']); |
||
457 | $available = $this->availableRelationshipsUrl($relation); |
||
458 | |||
459 | try { |
||
460 | $query = $this->Query->prepare($this->request->getQueryParams()); |
||
461 | $response = $this->apiClient->get($available, $query); |
||
462 | |||
463 | $this->Thumbs->urls($response); |
||
464 | } catch (BEditaClientException $ex) { |
||
465 | $this->log($ex, LogLevel::ERROR); |
||
466 | |||
467 | $this->set([ |
||
468 | 'error' => $ex->getMessage(), |
||
469 | '_serialize' => ['error'], |
||
470 | ]); |
||
471 | |||
472 | return; |
||
473 | } |
||
474 | |||
475 | $this->set((array)$response); |
||
476 | $this->set('_serialize', array_keys($response)); |
||
477 | } |
||
478 | |||
479 | /** |
||
480 | * Retrieve URL to get objects available for a relation |
||
481 | * |
||
482 | * @param string $relation The relation name. |
||
483 | * @return string |
||
484 | */ |
||
485 | protected function availableRelationshipsUrl(string $relation): string |
||
486 | { |
||
487 | $defaults = [ |
||
488 | 'children' => '/objects', |
||
489 | 'parent' => '/folders', |
||
490 | 'parents' => '/folders', |
||
491 | ]; |
||
492 | $defaultUrl = (string)Hash::get($defaults, $relation); |
||
493 | if (!empty($defaultUrl)) { |
||
494 | return $defaultUrl; |
||
495 | } |
||
496 | |||
497 | $relationsSchema = $this->Schema->getRelationsSchema(); |
||
498 | $types = $this->Modules->relatedTypes($relationsSchema, $relation); |
||
499 | if (count($types) === 1) { |
||
500 | return sprintf('/%s', $types[0]); |
||
501 | } |
||
502 | |||
503 | return '/objects?filter[type][]=' . implode('&filter[type][]=', $types); |
||
504 | } |
||
505 | |||
506 | /** |
||
507 | * get object properties and format them for index |
||
508 | * |
||
509 | * @param string $objectType objecte type name |
||
510 | * |
||
511 | * @return array $schema |
||
512 | */ |
||
513 | public function getSchemaForIndex($objectType): array |
||
527 | } |
||
528 | |||
529 | /** |
||
530 | * List categories for the object type. |
||
531 | * |
||
532 | * @return \Cake\Http\Response|null |
||
533 | */ |
||
534 | public function listCategories() |
||
535 | { |
||
536 | $this->viewBuilder()->setTemplate('categories'); |
||
537 | |||
538 | $this->request->allowMethod(['get']); |
||
539 | $response = $this->Categories->index($this->objectType, $this->request->getQueryParams()); |
||
540 | $resources = $this->Categories->map($response); |
||
541 | $roots = $this->Categories->getAvailableRoots($resources); |
||
542 | $categoriesTree = $this->Categories->tree($resources); |
||
543 | |||
544 | $this->set(compact('resources', 'roots', 'categoriesTree')); |
||
545 | $this->set('meta', (array)$response['meta']); |
||
546 | $this->set('links', (array)$response['links']); |
||
547 | $this->set('schema', $this->Schema->getSchema()); |
||
548 | $this->set('properties', $this->Properties->indexList('categories')); |
||
549 | $this->set('filter', $this->Properties->filterList('categories')); |
||
550 | $this->set('object_types', [$this->objectType]); |
||
551 | |||
552 | return null; |
||
553 | } |
||
554 | |||
555 | /** |
||
556 | * Save category. |
||
557 | * |
||
558 | * @return \Cake\Http\Response|null |
||
559 | */ |
||
560 | public function saveCategory(): ?Response |
||
574 | ]); |
||
575 | } |
||
576 | |||
577 | /** |
||
578 | * Remove single category. |
||
579 | * |
||
580 | * @param string $id Category ID. |
||
581 | * |
||
582 | * @return \Cake\Http\Response|null |
||
583 | */ |
||
584 | public function removeCategory(string $id): ?Response |
||
597 | ]); |
||
598 | } |
||
600 |
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.