Completed
Push — master ( d8083a...61164d )
by Arnaud
13s queued 11s
created

ActionConfiguration   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 425
Duplicated Lines 0 %

Test Coverage

Coverage 77.01%

Importance

Changes 5
Bugs 1 Features 0
Metric Value
eloc 202
c 5
b 1
f 0
dl 0
loc 425
ccs 144
cts 187
cp 0.7701
rs 8.4
wmc 50

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getActionName() 0 3 1
A getFormNormalizer() 0 17 3
A getAdminConfiguration() 0 3 1
A getDefaultTemplate() 0 14 2
A configureRepository() 0 5 1
A getLoadStrategyNormalizer() 0 14 4
A getAdminName() 0 3 1
A getDefaultRoutePath() 0 14 2
A generateRouteName() 0 10 2
A getCriteriaNormalizer() 0 17 3
A getFieldsNormalizer() 0 14 3
A configureOptions() 0 60 1
A getOrderNormalizer() 0 10 5
A getFiltersNormalizer() 0 23 3
A isActionInMapping() 0 3 1
A getTitleNormalizer() 0 24 4
A __construct() 0 7 1
A configureRouting() 0 28 5
B configureMenu() 0 49 7

How to fix   Complexity   

Complex Class

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
2
3
namespace LAG\AdminBundle\Configuration;
4
5
use Closure;
6
use JK\Configuration\Configuration;
7
use LAG\AdminBundle\Admin\Action;
8
use LAG\AdminBundle\Exception\Exception;
9
use LAG\AdminBundle\Form\Type\DeleteType;
10
use LAG\AdminBundle\LAGAdminBundle;
11
use LAG\AdminBundle\Routing\RoutingLoader;
12
use LAG\AdminBundle\Utils\TranslationUtils;
13
use LAG\Component\StringUtils\StringUtils;
14
use Symfony\Component\OptionsResolver\Options;
15
use Symfony\Component\OptionsResolver\OptionsResolver;
16
17
class ActionConfiguration extends Configuration
18
{
19
    /**
20
     * Related Action name.
21
     *
22
     * @var string
23
     */
24
    protected $actionName;
25
26
    /**
27
     * @var AdminConfiguration
28
     */
29
    protected $adminConfiguration;
30
31
    /**
32
     * @var string
33
     */
34
    protected $adminName;
35
36
    /**
37
     * ActionConfiguration constructor.
38
     */
39 4
    public function __construct(string $actionName, string $adminName, AdminConfiguration $adminConfiguration)
40
    {
41 4
        parent::__construct();
42
43 4
        $this->actionName = $actionName;
44 4
        $this->adminConfiguration = $adminConfiguration;
45 4
        $this->adminName = $adminName;
46 4
    }
47
48
    /**
49
     * Define allowed parameters and values for this configuration, using optionsResolver component.
50
     */
51 4
    public function configureOptions(OptionsResolver $resolver)
52
    {
53
        $resolver
54 4
            ->setDefaults([
55 4
                'title' => null,
56
                'class' => Action::class,
57
                'fields' => [],
58
                'permissions' => [
59
                    'ROLE_ADMIN',
60
                ],
61
                'export' => [],
62
                'order' => [],
63
                'icon' => null,
64 4
                'load_strategy' => LAGAdminBundle::LOAD_STRATEGY_NONE,
65 4
                'pager' => $this->adminConfiguration->get('pager'),
66 4
                'page_parameter' => $this->adminConfiguration->get('page_parameter'),
67 4
                'max_per_page' => 30,
68
                'criteria' => [],
69
                'filters' => [],
70 4
                'template' => $this->getDefaultTemplate(),
71
                'sortable' => false,
72 4
                'string_length' => $this->adminConfiguration->get('string_length'),
73 4
                'string_length_truncate' => $this->adminConfiguration->get('string_length_truncate'),
74 4
                'date_format' => $this->adminConfiguration->get('date_format'),
75
                'use_form' => true,
76
                'form' => null,
77
            ])
78 4
            ->setAllowedTypes('title', [
79 4
                'string',
80
                'null',
81
            ])
82 4
            ->setAllowedTypes('class', 'string')
83 4
            ->setAllowedTypes('fields', 'array')
84 4
            ->setAllowedTypes('permissions', 'array')
85 4
            ->setAllowedTypes('export', 'array')
86 4
            ->setAllowedTypes('order', 'array')
87 4
            ->setAllowedTypes('string_length', 'integer')
88 4
            ->setAllowedTypes('string_length_truncate', 'string')
89 4
            ->setNormalizer('fields', $this->getFieldsNormalizer())
90 4
            ->setNormalizer('order', $this->getOrderNormalizer())
91 4
            ->setNormalizer('load_strategy', $this->getLoadStrategyNormalizer())
92 4
            ->setNormalizer('criteria', $this->getCriteriaNormalizer())
93 4
            ->setNormalizer('filters', $this->getFiltersNormalizer())
94 4
            ->setNormalizer('form', $this->getFormNormalizer())
95 4
            ->setNormalizer('title', $this->getTitleNormalizer())
96 4
            ->setAllowedValues('load_strategy', [
97 4
                LAGAdminBundle::LOAD_STRATEGY_NONE,
98 4
                LAGAdminBundle::LOAD_STRATEGY_UNIQUE,
99 4
                LAGAdminBundle::LOAD_STRATEGY_MULTIPLE,
100
            ])
101 4
            ->setAllowedValues('pager', [
102 4
                'pagerfanta',
103
                false,
104
                null,
105
            ])
106
        ;
107
108 4
        $this->configureMenu($resolver);
109 4
        $this->configureRepository($resolver);
110 4
        $this->configureRouting($resolver);
111 2
    }
112
113
    /**
114
     * Generate an admin route name using the pattern in the configuration.
115
     *
116
     * @throws Exception
117
     */
118 4
    protected function generateRouteName(): string
119
    {
120 4
        if (!array_key_exists($this->actionName, $this->adminConfiguration->get('actions'))) {
121 2
            throw new Exception(sprintf('Invalid action name %s for admin %s (available action are: %s)', $this->actionName, $this->adminName, implode(', ', array_keys($this->adminConfiguration->get('actions')))));
122
        }
123
124 2
        return RoutingLoader::generateRouteName(
125 2
            $this->adminName,
126 2
            $this->actionName,
127 2
            $this->adminConfiguration->get('routing_name_pattern')
128
        );
129
    }
130
131
    /**
132
     * Return the field normalizer. It will transform null configuration into array to allow field type guessing
133
     * working.
134
     *
135
     * @return Closure
136
     */
137 4
    protected function getFieldsNormalizer()
138
    {
139
        return function (Options $options, $fields) {
140 2
            $normalizedFields = [];
141
142 2
            foreach ($fields as $name => $field) {
143
                if (null === $field) {
144
                    $field = [];
145
                }
146
147
                $normalizedFields[$name] = $field;
148
            }
149
150 2
            return $normalizedFields;
151 4
        };
152
    }
153
154
    /**
155
     * Return the order normalizer. It will check if the order value passed are valid.
156
     *
157
     * @return Closure
158
     */
159 4
    protected function getOrderNormalizer()
160
    {
161
        return function (Options $options, $order) {
162 2
            foreach ($order as $field => $sort) {
163
                if (!is_string($sort) || !is_string($field) || !in_array(strtolower($sort), ['asc', 'desc'])) {
164
                    throw new Exception('Order value should be an array of string (["field" => $key]), got '.gettype($sort));
165
                }
166
            }
167
168 2
            return $order;
169 4
        };
170
    }
171
172
    /**
173
     * Return the load strategy normalizer. It will set the default strategy according to the action name, if no value
174
     * is provided.
175
     *
176
     * @return Closure
177
     */
178 4
    protected function getLoadStrategyNormalizer()
179
    {
180
        return function (Options $options, $value) {
181 2
            if (!$value) {
182
                if ('create' == $this->actionName) {
183
                    $value = LAGAdminBundle::LOAD_STRATEGY_NONE;
184
                } elseif ('list' == $this->actionName) {
185
                    $value = LAGAdminBundle::LOAD_STRATEGY_MULTIPLE;
186
                } else {
187
                    $value = LAGAdminBundle::LOAD_STRATEGY_UNIQUE;
188
                }
189
            }
190
191 2
            return $value;
192 4
        };
193
    }
194
195
    /**
196
     * Return the criteria normalizer. It will add the id parameters for the edit and delete actions if no value is
197
     * provided.
198
     *
199
     * @return Closure
200
     */
201 4
    protected function getCriteriaNormalizer()
202
    {
203
        return function (Options $options, $value) {
204 2
            if (!$value) {
205
                $idActions = [
206 2
                    'edit',
207
                    'delete',
208
                ];
209
210 2
                if (in_array($this->actionName, $idActions)) {
211
                    $value = [
212
                        'id',
213
                    ];
214
                }
215
            }
216
217 2
            return $value;
218 4
        };
219
    }
220
221
    public function getAdminName(): string
222
    {
223
        return $this->adminName;
224
    }
225
226
    public function getActionName(): string
227
    {
228
        return $this->actionName;
229
    }
230
231
    public function getAdminConfiguration(): AdminConfiguration
232
    {
233
        return $this->adminConfiguration;
234
    }
235
236
    /**
237
     * Return the filters normalizer.
238
     */
239 4
    protected function getFiltersNormalizer(): Closure
240
    {
241
        return function (Options $options, $data) {
242 2
            $normalizedData = [];
243
244 2
            foreach ($data as $name => $field) {
245
                if (is_string($field)) {
246
                    $field = [
247
                        'type' => $field,
248
                        'options' => [],
249
                    ];
250
                }
251
                $field['name'] = $name;
252
253
                $resolver = new OptionsResolver();
254
                $filterConfiguration = new FilterConfiguration();
255
                $filterConfiguration->configureOptions($resolver);
256
                $filterConfiguration->setParameters($resolver->resolve($field));
257
258
                $normalizedData[$name] = $filterConfiguration->getParameters();
259
            }
260
261 2
            return $normalizedData;
262 4
        };
263
    }
264
265 4
    protected function getFormNormalizer(): Closure
266
    {
267
        return function (Options $options, $value) {
268 2
            if (null !== $value) {
269
                return $value;
270
            }
271
            $mapping = [
272 2
                'create' => $this->adminConfiguration->get('form'),
273 2
                'edit' => $this->adminConfiguration->get('form'),
274
                'delete' => DeleteType::class,
275
            ];
276
277 2
            if (key_exists($this->actionName, $mapping)) {
278
                $value = $mapping[$this->actionName];
279
            }
280
281 2
            return $value;
282 4
        };
283
    }
284
285 4
    protected function getTitleNormalizer(): Closure
286
    {
287
        return function (Options $options, $value) {
288
            // If the translation system is not used, return the provided value as is
289 2
            if (!$this->adminConfiguration->isTranslationEnabled()) {
290 2
                if (null === $value) {
291 2
                    return StringUtils::camelize($this->actionName);
292
                }
293
294
                return $value;
295
            }
296
297
            // If a value is defined, we should return the value
298
            if (null !== $value) {
299
                return $value;
300
            }
301
            // By default, the action title is action name using the configured translation pattern
302
            $value = TranslationUtils::getTranslationKey(
303
                $this->adminConfiguration->getTranslationPattern(),
304
                $this->adminName,
305
                $this->actionName
306
            );
307
308
            return $value;
309 4
        };
310
    }
311
312
    /**
313
     * Return the default route path according to the action name.
314
     */
315 2
    protected function getDefaultRoutePath(): string
316
    {
317
        $pattern = $this
318 2
            ->adminConfiguration
319 2
            ->get('routing_url_pattern')
320
        ;
321 2
        $path = str_replace('{admin}', $this->adminName, $pattern);
322 2
        $path = str_replace('{action}', $this->actionName, $path);
323
324 2
        if (in_array($this->actionName, ['edit', 'delete'])) {
325
            $path .= '/{id}';
326
        }
327
328 2
        return $path;
329
    }
330
331 4
    protected function getDefaultTemplate(): ?string
332
    {
333
        $mapping = [
334 4
            'list' => $this->adminConfiguration->get('list_template'),
335 4
            'edit' => $this->adminConfiguration->get('edit_template'),
336 4
            'create' => $this->adminConfiguration->get('create_template'),
337 4
            'delete' => $this->adminConfiguration->get('delete_template'),
338
        ];
339
340 4
        if (!$this->isActionInMapping($mapping)) {
341 4
            return null;
342
        }
343
344
        return $mapping[$this->actionName];
345
    }
346
347 4
    protected function isActionInMapping(array $mapping): bool
348
    {
349 4
        return array_key_exists($this->actionName, $mapping);
350
    }
351
352 4
    protected function configureMenu(OptionsResolver $resolver): void
353
    {
354
        $resolver
355 4
            ->setDefaults([
356 4
                'add_return' => true,
357
                'menus' => [],
358
            ])
359 4
            ->setAllowedTypes('add_return', 'boolean')
360
            ->setNormalizer('menus', function (Options $options, $value) {
361 2
                if (false === $value) {
362
                    return $value;
363
                }
364
365 2
                if (!is_array($value)) {
366
                    $value = [];
367
                }
368
369 2
                if (!key_exists('top', $value)) {
370 2
                    $value['top'] = [];
371
                }
372
373 2
                if (!key_exists('items', $value['top'])) {
374 2
                    $value['top']['items'] = [];
375
                }
376
377
                // Auto return button should be optional
378 2
                if ($options->offsetGet('add_return')) {
379 2
                    $text = ucfirst('Return');
380
381 2
                    if ($this->adminConfiguration->isTranslationEnabled()) {
382
                        $text = TranslationUtils::getTranslationKey(
383
                            $this->adminConfiguration->getTranslationPattern(),
384
                            $this->adminName,
385
                            'return'
386
                        );
387
                    }
388
389
                    // Use array_unshift() to put the items in first in menu
390 2
                    array_unshift($value['top']['items'], [
391 2
                        'admin' => $this->adminName,
392 2
                        'action' => 'list',
393
                        // Do not use the translation trait as the action configuration is not configured yet, so
394
                        // translation parameters are not available
395 2
                        'text' => $text,
396 2
                        'icon' => 'arrow-left',
397
                    ]);
398
                }
399
400 2
                return $value;
401 4
            })
402
        ;
403 4
    }
404
405 4
    protected function configureRepository(OptionsResolver $resolver): void
406
    {
407
        $resolver
408 4
            ->setDefaults([
409 4
                'repository_method' => null,
410
            ])
411
        ;
412 4
    }
413
414 4
    protected function configureRouting(OptionsResolver $resolver): void
415
    {
416
        $resolver
417 4
            ->setDefaults([
418 4
                'controller' => $this->adminConfiguration->get('controller'),
419 4
                'route' => $this->generateRouteName(),
420
                'route_parameters' => [],
421 2
                'route_path' => $this->getDefaultRoutePath(),
422
                'route_requirements' => [],
423
                'route_defaults' => [],
424
            ])
425 2
            ->setAllowedTypes('route', 'string')
426 2
            ->setAllowedTypes('route_parameters', 'array')
427 2
            ->setAllowedTypes('route_path', 'string')
428 2
            ->setAllowedTypes('route_defaults', 'array')
429 2
            ->setAllowedTypes('route_requirements', 'array')
430
            ->setNormalizer('route_defaults', function (Options $options, $value) {
431 2
                if (!$value || is_array($value)) {
432 2
                    $value = [];
433
                }
434
435 2
                if (!key_exists('_controller', $value) || !$value['_controller']) {
436 2
                    $value['_controller'] = $options->offsetGet('controller');
437
                }
438 2
                $value['_admin'] = $this->adminName;
439 2
                $value['_action'] = $this->actionName;
440
441 2
                return $value;
442 2
            })
443
        ;
444 2
    }
445
}
446