Total Complexity | 73 |
Total Lines | 514 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like ActionConfiguration 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 ActionConfiguration, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
21 | class ActionConfiguration extends Configuration |
||
22 | { |
||
23 | protected function configureOptions(OptionsResolver $resolver): void |
||
24 | { |
||
25 | $resolver |
||
26 | // Main info |
||
27 | ->setRequired('name') |
||
28 | ->setAllowedTypes('name', 'string') |
||
29 | ->setRequired('admin_name') |
||
30 | ->setAllowedTypes('admin_name', 'string') |
||
31 | ->setDefault('title', null) |
||
32 | ->addNormalizer('title', $this->getTitleNormalizer()) |
||
33 | ->setAllowedTypes('title', ['string', 'null']) |
||
34 | ->setDefault('icon', null) |
||
35 | ->setAllowedTypes('icon', ['string', 'null']) |
||
36 | ->setDefault('action_class', Action::class) |
||
37 | ->setAllowedTypes('action_class', 'string') |
||
38 | ->setDefault('template', null) |
||
39 | ->addNormalizer('template', $this->getTemplateNormalizer()) |
||
40 | ->setAllowedTypes('template', ['string', 'null']) |
||
41 | |||
42 | // Linked actions |
||
43 | ->setDefault('list_actions', [ |
||
44 | 'edit' => [], |
||
45 | 'delete' => [], |
||
46 | ]) |
||
47 | ->setAllowedTypes('list_actions', 'array') |
||
48 | ->setNormalizer('list_actions', function (Options $options, $value) { |
||
49 | foreach ($value as $actionName => $actionConfiguration) { |
||
50 | $value[$actionName] = LinkNormalizer::normalize( |
||
51 | $actionConfiguration, |
||
52 | $options->offsetGet('name'), |
||
53 | $actionName |
||
54 | ); |
||
55 | } |
||
56 | |||
57 | return $value; |
||
58 | }) |
||
59 | |||
60 | ->setDefault('item_actions', [ |
||
61 | 'create' => [], |
||
62 | ]) |
||
63 | ->setAllowedTypes('item_actions', 'array') |
||
64 | ->setNormalizer('item_actions', function (Options $options, $value) { |
||
65 | foreach ($value as $actionName => $actionConfiguration) { |
||
66 | $value[$actionName] = LinkNormalizer::normalize( |
||
67 | $actionConfiguration, |
||
68 | $options->offsetGet('name'), |
||
69 | $actionName |
||
70 | ); |
||
71 | } |
||
72 | |||
73 | return $value; |
||
74 | }) |
||
75 | |||
76 | // Routing |
||
77 | ->setDefault('controller', AdminAction::class) |
||
78 | ->setAllowedTypes('controller', 'string') |
||
79 | ->setRequired('route') |
||
80 | ->setAllowedTypes('route', 'string') |
||
81 | ->setDefault('route_parameters', []) |
||
82 | ->setAllowedTypes('route_parameters', 'array') |
||
83 | ->addNormalizer('route_parameters', $this->getRouteParametersNormalizer()) |
||
84 | ->setDefault('path', null) |
||
85 | ->setAllowedTypes('path', ['string', 'null']) |
||
86 | ->addNormalizer('path', $this->getPathNormalizer()) |
||
87 | |||
88 | // Fields |
||
89 | ->setDefault('fields', []) |
||
90 | ->setAllowedTypes('fields', 'array') |
||
91 | ->addNormalizer('fields', $this->getFieldsNormalizer()) |
||
92 | |||
93 | // Filter and orders |
||
94 | ->setDefault('order', []) |
||
95 | ->setAllowedTypes('order', 'array') |
||
96 | ->addNormalizer('order', $this->getOrderNormalizer()) |
||
97 | ->setDefault('criteria', []) |
||
98 | ->setAllowedTypes('criteria', 'array') |
||
99 | ->addNormalizer('criteria', $this->getCriteriaNormalizer()) |
||
100 | ->setDefault('filters', []) |
||
101 | ->setAllowedTypes('filters', 'array') |
||
102 | ->addNormalizer('filters', $this->getFiltersNormalizer()) |
||
103 | |||
104 | // Security |
||
105 | ->setDefault('permissions', ['ROLE_ADMIN']) |
||
106 | ->setAllowedTypes('permissions', 'array') |
||
107 | |||
108 | // Export |
||
109 | ->setDefault('export', ['csv', 'xml', 'yaml']) |
||
110 | ->setAllowedTypes('export', 'array') |
||
111 | |||
112 | // Data |
||
113 | ->setDefault('load_strategy', null) |
||
114 | ->setAllowedValues('load_strategy', [ |
||
115 | null, |
||
116 | AdminInterface::LOAD_STRATEGY_NONE, |
||
117 | AdminInterface::LOAD_STRATEGY_UNIQUE, |
||
118 | AdminInterface::LOAD_STRATEGY_MULTIPLE, |
||
119 | ]) |
||
120 | ->addNormalizer('load_strategy', $this->getLoadStrategyNormalizer()) |
||
121 | ->setDefault('repository_method', null) |
||
122 | ->setAllowedTypes('repository_method', ['string', 'null']) |
||
123 | |||
124 | // Pagination |
||
125 | ->setDefault('pager', 'pagerfanta') |
||
126 | ->setAllowedValues('pager', ['pagerfanta', false]) |
||
127 | ->setDefault('max_per_page', 25) |
||
128 | ->setAllowedTypes('max_per_page', 'integer') |
||
129 | ->setDefault('page_parameter', 'page') |
||
130 | ->setAllowedTypes('page_parameter', 'string') |
||
131 | |||
132 | ->setDefault('date_format', 'Y-m-d') |
||
133 | ->setAllowedTypes('date_format', 'string') |
||
134 | |||
135 | // Form |
||
136 | ->setDefault('form', null) |
||
137 | ->setAllowedTypes('form', ['string', 'null', 'boolean']) |
||
138 | ->setDefault('form_options', []) |
||
139 | ->setAllowedTypes('form_options', 'array') |
||
140 | |||
141 | // Redirection after success |
||
142 | ->setDefault('target_route', 'list') |
||
143 | ->setAllowedTypes('target_route', 'string') |
||
144 | ->setDefault('target_route_parameters', []) |
||
145 | ->setAllowedTypes('target_route_parameters', 'array') |
||
146 | ; |
||
147 | } |
||
148 | |||
149 | public function getName(): string |
||
150 | { |
||
151 | return $this->getString('name'); |
||
152 | } |
||
153 | |||
154 | public function getAdminName(): string |
||
155 | { |
||
156 | return $this->getString('admin_name'); |
||
157 | } |
||
158 | |||
159 | public function getTitle(): string |
||
162 | } |
||
163 | |||
164 | public function getIcon(): ?string |
||
165 | { |
||
166 | return $this->get('icon'); |
||
167 | } |
||
168 | |||
169 | public function getActionClass(): string |
||
170 | { |
||
171 | return $this->getString('action_class'); |
||
172 | } |
||
173 | |||
174 | public function getTemplate(): string |
||
175 | { |
||
176 | return $this->get('template'); |
||
177 | } |
||
178 | |||
179 | public function getListActions(): array |
||
180 | { |
||
181 | return $this->get('list_actions'); |
||
182 | } |
||
183 | |||
184 | public function getItemActions(): array |
||
185 | { |
||
186 | return $this->get('item_actions'); |
||
187 | } |
||
188 | |||
189 | public function getController(): string |
||
190 | { |
||
191 | return $this->getString('controller'); |
||
192 | } |
||
193 | |||
194 | public function getRoute(): string |
||
195 | { |
||
196 | return $this->getString('route'); |
||
197 | } |
||
198 | |||
199 | public function getRouteParameters(): array |
||
200 | { |
||
201 | return $this->get('route_parameters'); |
||
202 | } |
||
203 | |||
204 | public function getPath(): string |
||
205 | { |
||
206 | return $this->getString('path'); |
||
207 | } |
||
208 | |||
209 | public function getFields(): array |
||
210 | { |
||
211 | return $this->get('fields'); |
||
212 | } |
||
213 | |||
214 | public function getOrder(): array |
||
215 | { |
||
216 | return $this->get('order'); |
||
217 | } |
||
218 | |||
219 | public function getCriteria(): array |
||
220 | { |
||
221 | return $this->get('criteria'); |
||
222 | } |
||
223 | |||
224 | public function getFilters(): array |
||
225 | { |
||
226 | return $this->get('filters'); |
||
227 | } |
||
228 | |||
229 | public function getPermissions(): array |
||
230 | { |
||
231 | return $this->get('permissions'); |
||
232 | } |
||
233 | |||
234 | public function getExport(): array |
||
235 | { |
||
236 | return $this->get('export'); |
||
237 | } |
||
238 | |||
239 | public function getLoadStrategy(): string |
||
240 | { |
||
241 | return $this->getString('load_strategy'); |
||
242 | } |
||
243 | |||
244 | public function isPaginationEnabled(): bool |
||
245 | { |
||
246 | $pager = $this->get('pager'); |
||
247 | |||
248 | if ($pager === false) { |
||
249 | return false; |
||
250 | } |
||
251 | |||
252 | return true; |
||
253 | } |
||
254 | |||
255 | public function getPager(): string |
||
256 | { |
||
257 | if (!$this->isPaginationEnabled()) { |
||
258 | throw new Exception('The pagination is not enabled'); |
||
259 | } |
||
260 | |||
261 | return $this->getString('pager'); |
||
262 | } |
||
263 | |||
264 | public function getMaxPerPage(): int |
||
265 | { |
||
266 | if (!$this->isPaginationEnabled()) { |
||
267 | throw new Exception('The pagination is not enabled'); |
||
268 | } |
||
269 | |||
270 | return $this->getInt('max_per_page'); |
||
271 | } |
||
272 | |||
273 | public function getPageParameter(): string |
||
274 | { |
||
275 | return $this->getString('page_parameter'); |
||
276 | } |
||
277 | |||
278 | public function getDateFormat(): string |
||
279 | { |
||
280 | return $this->getString('date_format'); |
||
281 | } |
||
282 | |||
283 | public function getForm(): ?string |
||
284 | { |
||
285 | return $this->get('form'); |
||
286 | } |
||
287 | |||
288 | public function getFormOptions(): array |
||
289 | { |
||
290 | return $this->get('form_options'); |
||
291 | } |
||
292 | |||
293 | public function getRepositoryMethod(): ?string |
||
294 | { |
||
295 | return $this->get('repository_method'); |
||
296 | } |
||
297 | |||
298 | public function getTargetRoute(): string |
||
299 | { |
||
300 | return $this->getString('target_route'); |
||
301 | } |
||
302 | |||
303 | public function getTargetRouteParameters(): array |
||
304 | { |
||
305 | return $this->get('target_route_parameters'); |
||
306 | } |
||
307 | |||
308 | private function getTitleNormalizer(): Closure |
||
309 | { |
||
310 | return function (Options $options, $value) { |
||
311 | if ($value === null) { |
||
312 | $value = u($options->offsetGet('name'))->camel()->title()->toString(); |
||
313 | } |
||
314 | |||
315 | return $value; |
||
316 | }; |
||
317 | } |
||
318 | |||
319 | private function getTemplateNormalizer(): Closure |
||
320 | { |
||
321 | return function (Options $options, $value) { |
||
322 | $map = [ |
||
323 | 'create' => '@LAGAdmin/crud/create.html.twig', |
||
324 | 'edit' => '@LAGAdmin/crud/edit.html.twig', |
||
325 | 'list' => '@LAGAdmin/crud/list.html.twig', |
||
326 | 'delete' => '@LAGAdmin/crud/delete.html.twig', |
||
327 | 'batch' => '@LAGAdmin/crud/batch.html.twig', |
||
328 | ]; |
||
329 | |||
330 | if (array_key_exists($options->offsetGet('name'), $map) && $value === null) { |
||
331 | return $map[$options->offsetGet('name')]; |
||
332 | } |
||
333 | |||
334 | throw new ActionConfigurationException( |
||
335 | 'The template should be defined if the action is not named create, edit, list, delete or batch' |
||
336 | ); |
||
337 | }; |
||
338 | } |
||
339 | |||
340 | private function getPathNormalizer(): Closure |
||
341 | { |
||
342 | return function (Options $options, $value) { |
||
343 | if ($value !== null) { |
||
344 | $path = u($value); |
||
345 | |||
346 | if ($path->endsWith('/')) { |
||
347 | $path = $path->slice(0, -1); |
||
348 | } |
||
349 | |||
350 | return $path->toString(); |
||
351 | } |
||
352 | $loadStrategy = $options->offsetGet('load_strategy'); |
||
353 | $path = u($options->offsetGet('admin_name')) |
||
354 | ->snake() |
||
355 | ->replace('_', '-') |
||
356 | ; |
||
357 | |||
358 | if (!$path->endsWith('s')) { |
||
359 | $path = $path->append('s'); |
||
360 | |||
361 | if ($path->endsWith('ys')) { |
||
362 | $path = $path->before('ys')->append('ies'); |
||
363 | } |
||
364 | } |
||
365 | $snakeActionName = u($options->offsetGet('name')) |
||
366 | ->snake() |
||
367 | ->replace('_', '-') |
||
368 | ->toString() |
||
369 | ; |
||
370 | |||
371 | // Edit the the default action. It is not append to the path (ex: articles/{id} for edit, |
||
372 | // articles/{id}/delete for delete) |
||
373 | if ($loadStrategy === AdminInterface::LOAD_STRATEGY_UNIQUE) { |
||
374 | $path = $path->append('/{id}'); |
||
375 | |||
376 | if ($options->offsetGet('name') !== 'edit') { |
||
377 | $path = $path |
||
378 | ->append('/') |
||
379 | ->append($snakeActionName) |
||
380 | ; |
||
381 | } |
||
382 | } |
||
383 | |||
384 | if ($loadStrategy === AdminInterface::LOAD_STRATEGY_NONE) { |
||
385 | $path = $path |
||
386 | ->append('/') |
||
387 | ->append($snakeActionName) |
||
388 | ; |
||
389 | } |
||
390 | |||
391 | return $path->toString(); |
||
392 | }; |
||
393 | } |
||
394 | |||
395 | /** |
||
396 | * Return the field normalizer. It will transform null configuration into array to allow field type guessing |
||
397 | * working. |
||
398 | */ |
||
399 | private function getFieldsNormalizer(): Closure |
||
400 | { |
||
401 | return function (Options $options, $fields) { |
||
402 | $normalizedFields = []; |
||
403 | |||
404 | foreach ($fields as $name => $field) { |
||
405 | if ($field === null) { |
||
406 | $field = []; |
||
407 | } |
||
408 | |||
409 | $normalizedFields[$name] = $field; |
||
410 | } |
||
411 | |||
412 | return $normalizedFields; |
||
413 | }; |
||
414 | } |
||
415 | |||
416 | /** |
||
417 | * Return the order normalizer. It will check if the order value passed are valid. |
||
418 | */ |
||
419 | private function getOrderNormalizer(): Closure |
||
420 | { |
||
421 | return function (Options $options, $order) { |
||
422 | foreach ($order as $field => $sort) { |
||
423 | if (!\is_string($sort) || !\is_string($field) || !\in_array(strtolower($sort), ['asc', 'desc'])) { |
||
424 | throw new Exception('Order value should be an array of string (["field" => $key]), got '.\gettype($sort)); |
||
425 | } |
||
426 | } |
||
427 | |||
428 | return $order; |
||
429 | }; |
||
430 | } |
||
431 | |||
432 | private function getLoadStrategyNormalizer(): Closure |
||
433 | { |
||
434 | return function (Options $options, $value) { |
||
435 | if ($value !== null) { |
||
436 | return $value; |
||
437 | } |
||
438 | |||
439 | if ($options->offsetGet('name') == 'create') { |
||
440 | $value = AdminInterface::LOAD_STRATEGY_NONE; |
||
441 | } elseif ($options->offsetGet('name') == 'list') { |
||
442 | $value = AdminInterface::LOAD_STRATEGY_MULTIPLE; |
||
443 | } else { |
||
444 | $value = AdminInterface::LOAD_STRATEGY_UNIQUE; |
||
445 | } |
||
446 | |||
447 | return $value; |
||
448 | }; |
||
449 | } |
||
450 | |||
451 | /** |
||
452 | * Return the criteria normalizer. It will add the id parameters for the edit and delete actions if no value is |
||
453 | * provided. |
||
454 | */ |
||
455 | private function getCriteriaNormalizer(): Closure |
||
456 | { |
||
457 | return function (Options $options, $value) { |
||
458 | if (!$value) { |
||
459 | $idActions = [ |
||
460 | 'edit', |
||
461 | 'delete', |
||
462 | ]; |
||
463 | |||
464 | if (\in_array($options->offsetGet('name'), $idActions)) { |
||
465 | $value = [ |
||
466 | 'id', |
||
467 | ]; |
||
468 | } |
||
469 | } |
||
470 | |||
471 | return $value; |
||
472 | }; |
||
473 | } |
||
474 | |||
475 | /** |
||
476 | * Return the filters normalizer. |
||
477 | */ |
||
478 | private function getFiltersNormalizer(): Closure |
||
479 | { |
||
480 | return function (Options $options, $data) { |
||
481 | $normalizedData = []; |
||
482 | |||
483 | foreach ($data as $name => $field) { |
||
484 | if (\is_string($field)) { |
||
485 | $field = [ |
||
486 | 'name' => $field, |
||
487 | 'type' => TextType::class, |
||
488 | 'options' => [], |
||
489 | ]; |
||
490 | } else { |
||
491 | $field['name'] = $name; |
||
492 | } |
||
493 | |||
494 | $filterConfiguration = new FilterConfiguration(); |
||
495 | $filterConfiguration->configure($field); |
||
496 | |||
497 | $normalizedData[$field['name']] = $filterConfiguration->toArray(); |
||
498 | } |
||
499 | |||
500 | return $normalizedData; |
||
501 | }; |
||
502 | } |
||
503 | |||
504 | private function getRouteParametersNormalizer(): Closure |
||
516 | }; |
||
517 | } |
||
518 | |||
519 | private function getLinkedActionsNormalizer(): Closure |
||
|
|||
520 | { |
||
521 | return function (Options $options, $value) { |
||
522 | $actions = array_keys($options->offsetGet('actions')); |
||
523 | |||
524 | foreach ($value as $actionName) { |
||
525 | if (!in_array($actionName, $actions)) { |
||
526 | throw new Exception(sprintf( |
||
527 | 'The action "%s" does not exists in the admin "%s"', |
||
528 | $actionName, |
||
529 | $options->offsetGet('name'), |
||
530 | )); |
||
531 | } |
||
532 | } |
||
533 | |||
534 | return $value; |
||
535 | }; |
||
536 | } |
||
537 | } |
||
538 |
This check looks for private methods that have been defined, but are not used inside the class.