Passed
Pull Request — master (#138)
by Arnaud
03:43
created

ActionConfiguration::getMenuNormalizer()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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