Completed
Pull Request — master (#90)
by Arnaud
04:05
created

ActionConfiguration::getDefaultTemplate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 15
ccs 4
cts 5
cp 0.8
rs 9.4285
cc 2
eloc 9
nc 2
nop 0
crap 2.032
1
<?php
2
3
namespace LAG\AdminBundle\Action\Configuration;
4
5
use Closure;
6
use JK\Configuration\Configuration;
7
use LAG\AdminBundle\Admin\AdminInterface;
8
use LAG\AdminBundle\Admin\Behaviors\TranslationKeyTrait;
9
use LAG\AdminBundle\Form\Type\DeleteType;
10
use LAG\AdminBundle\Form\Type\ListType;
11
use LAG\AdminBundle\LAGAdminBundle;
12
use LAG\AdminBundle\Menu\Configuration\MenuConfiguration;
13
use Symfony\Component\DependencyInjection\Container;
14
use Symfony\Component\OptionsResolver\Options;
15
use Symfony\Component\OptionsResolver\OptionsResolver;
16
17
class ActionConfiguration extends Configuration
18
{
19
    use TranslationKeyTrait;
20
    
21
    /**
22
     * Related Action name.
23
     *
24
     * @var string
25
     */
26
    private $actionName;
27
    
28
    /**
29
     * Related Admin (optional)
30
     *
31
     * @var AdminInterface
32
     */
33
    private $admin = null;
34
    
35
    /**
36
     * ActionConfiguration constructor.
37
     *
38
     * @param string              $actionName
39
     * @param AdminInterface|null $admin
40
     */
41 1
    public function __construct($actionName, AdminInterface $admin)
42
    {
43 1
        parent::__construct();
44
        
45 1
        $this->actionName = $actionName;
46 1
        $this->admin = $admin;
47 1
    }
48
    
49
    /**
50
     * Define allowed parameters and values for this configuration, using optionsResolver component.
51
     *
52
     * @param OptionsResolver $resolver
53
     */
54 1
    public function configureOptions(OptionsResolver $resolver)
55
    {
56 1
        $this->configureDefaultOptions($resolver);
57 1
        $this->configureNormalizers($resolver);
58
        
59 1
        $this->setAllowedTypes($resolver);
60 1
        $this->setAllowedValues($resolver);
61 1
    }
62
    
63
    /**
64
     * Configure the default options for an Action.
65
     *
66
     * @param OptionsResolver $resolver
67
     */
68 1
    private function configureDefaultOptions(OptionsResolver $resolver)
69
    {
70
        $resolver
71 1
            ->setDefaults([
72
                // the default action title is a translation key using the translation pattern and the action name
73 1
                'title' => $this->getDefaultTitle(),
74 1
                'service' => $this->getDefaultServiceId(),
75
                'fields' => [
76
                    'id' => [],
77
                ],
78
                // by default, only administrator can access the admin
79
                'permissions' => [
80
                    'ROLE_ADMIN',
81
                ],
82
                // all export are activated
83
                'export' => [
84
                    'json',
85
                    'html',
86
                    'csv',
87
                    'xls',
88
                ],
89
                // no order
90
                'order' => [],
91
                // route will be generated if empty string or null or false is provided
92 1
                'route' => '',
93
                // route parameters will be used when using the route (for links...)
94
                'route_parameters' => [],
95 1
                'route_path' => $this->getDefaultRoutePath(),
96 1
                'route_defaults' => $this->getDefaultRouteDefaults(),
97 1
                'route_requirements' => $this->getDefaultRouteRequirements(),
98
                // icon in the menu
99 1
                'icon' => '',
100
                // entities loading strategy
101
                'load_strategy' => null,
102
                // pager interface, only null or pagerfanta are allowed
103 1
                'pager' => 'pagerfanta',
104 1
                'max_per_page' => $this->admin->getConfiguration()->getParameter('max_per_page'),
105
                // default criteria used to load entities
106
                'criteria' => [],
107
                // filters, should be an array of string (field name => filter options)
108
                'filters' => [],
109
                'menus' => [],
110
                'batch' => false,
111
                // form configuration
112 1
                'form_handler' => $this->getDefaultFormHandler(),
113 1
                'form' => $this->getDefaultForm(),
114 1
                'form_options' => $this->getDefaultFormOptions(),
115
                // twig template
116 1
                'template' => $this->getDefaultTemplate(),
117 1
                'sortable' => $this->getDefaultSortable(),
118
            ]);
119 1
    }
120
    
121
    /**
122
     * Define the allowed values.
123
     *
124
     * @param OptionsResolver $resolver
125
     */
126 1
    private function setAllowedValues(OptionsResolver $resolver)
127
    {
128
        $resolver
129 1
            ->setAllowedValues('load_strategy', [
130 1
                AdminInterface::LOAD_STRATEGY_NONE,
131 1
                AdminInterface::LOAD_STRATEGY_UNIQUE,
132 1
                AdminInterface::LOAD_STRATEGY_MULTIPLE,
133
                null,
134
            ])
135 1
            ->setAllowedValues('pager', [
136 1
                'pagerfanta',
137
                false,
138
            ])
139
        ;
140 1
    }
141
    
142
    /**
143
     * Define the allowed types.
144
     *
145
     * @param OptionsResolver $resolver
146
     */
147 1
    private function setAllowedTypes(OptionsResolver $resolver)
148
    {
149
        $resolver
150 1
            ->setAllowedTypes('title', 'string')
151 1
            ->setAllowedTypes('fields', 'array')
152 1
            ->setAllowedTypes('order', 'array')
153 1
            ->setAllowedTypes('route', 'string')
154 1
            ->setAllowedTypes('route_parameters', 'array')
155 1
            ->setAllowedTypes('icon', 'string')
156 1
            ->setAllowedTypes('criteria', 'array')
157 1
            ->setAllowedTypes('filters', 'array')
158 1
            ->setAllowedTypes('menus', [
159 1
                'array',
160
                'boolean',
161
            ])
162 1
            ->setAllowedTypes('route_path', 'string')
163 1
            ->setAllowedTypes('route_defaults', 'array')
164 1
            ->setAllowedTypes('route_requirements', 'array')
165 1
            ->setAllowedTypes('form', 'string')
166 1
            ->setAllowedTypes('form_options', 'array')
167
        ;
168 1
    }
169
    
170
    /**
171
     * Configure the normalizers.
172
     *
173
     * @param OptionsResolver $resolver
174
     */
175 1
    private function configureNormalizers(OptionsResolver $resolver)
176
    {
177
        $resolver
178 1
            ->setNormalizer('fields', $this->getFieldsNormalizer())
179 1
            ->setNormalizer('order', $this->getOrderNormalizer())
180 1
            ->setNormalizer('route', $this->getRouteNormalizer())
181 1
            ->setNormalizer('load_strategy', $this->getLoadStrategyNormalizer())
182 1
            ->setNormalizer('criteria', $this->getCriteriaNormalizer())
183 1
            ->setNormalizer('menus', $this->getMenuNormalizer())
184 1
            ->setNormalizer('batch', $this->getBatchNormalizer())
185 1
            ->setNormalizer('filters', $this->getFiltersNormalizer())
186
        ;
187 1
    }
188
    
189
    /**
190
     * Return the field normalizer. It will transform null configuration into array to allow field type guessing
191
     * working.
192
     *
193
     * @return Closure
194
     */
195 1
    private function getFieldsNormalizer()
196
    {
197
        return function(Options $options, $fields) {
198 1
            $normalizedFields = [];
199
            
200 1
            foreach ($fields as $name => $field) {
201
                
202 1
                if ($field === null) {
203
                    $field = [];
204
                }
205
                
206 1
                $normalizedFields[$name] = $field;
207
            }
208
            
209 1
            return $normalizedFields;
210 1
        };
211
    }
212
    
213
    /**
214
     * Return the order normalizer. It will check if the order value passed are valid.
215
     *
216
     * @return Closure
217
     */
218 1
    private function getOrderNormalizer()
219
    {
220
        return function(Options $options, $order) {
221 1
            foreach ($order as $field => $sort) {
222
                
223
                if (!is_string($sort) || !is_string($field) || !in_array(strtolower($sort), ['asc', 'desc'])) {
224
                    throw new ConfigurationException(
225
                        'Order value should be an array of string (["field" => $key]), got '.gettype($sort),
226
                        $this->actionName,
227
                        $this->admin
228
                    );
229
                }
230
            }
231
            
232 1
            return $order;
233 1
        };
234
    }
235
    
236
    /**
237
     * Return the route normalizer. If an empty value or null or false, it will generate the route using the Admin.
238
     *
239
     * @return Closure
240
     */
241 1
    private function getRouteNormalizer()
242
    {
243
        return function(Options $options, $value) {
244 1
            if (!$value) {
245
                // generate default route from admin
246
                return $this
247 1
                    ->admin
248 1
                    ->generateRouteName($this->actionName);
249
            }
250
            
251
            return $value;
252 1
        };
253
    }
254
    
255
    /**
256
     * Return the load strategy normalizer. It will set the default strategy according to the action name, if no value
257
     * is provided.
258
     *
259
     * @return Closure
260
     */
261 1
    private function getLoadStrategyNormalizer()
262
    {
263
        return function(Options $options, $value) {
264 1
            if (!$value) {
265 1
                if ($this->actionName == 'create') {
266
                    $value = AdminInterface::LOAD_STRATEGY_NONE;
267 1
                } else if ($this->actionName == 'list') {
268 1
                    $value = AdminInterface::LOAD_STRATEGY_MULTIPLE;
269
                } else {
270
                    $value = AdminInterface::LOAD_STRATEGY_UNIQUE;
271
                }
272
            }
273
            
274 1
            return $value;
275 1
        };
276
    }
277
    
278
    /**
279
     * Return the menu normalizer. It will transform false values into an empty array to allow default menu
280
     * configuration working.
281
     *
282
     * @return Closure
283
     */
284 1
    private function getMenuNormalizer()
285
    {
286
        return function(Options $options, $menus) {
287
            // set default to an array
288 1
            if ($menus === false) {
289
                $menus = [];
290
            }
291
            
292 1
            return $menus;
293 1
        };
294
    }
295
    
296
    /**
297
     * Return the criteria normalizer. It will add the id parameters for the edit and delete actions if no value is
298
     * provided.
299
     *
300
     * @return Closure
301
     */
302 1
    private function getCriteriaNormalizer()
303
    {
304
        return function(Options $options, $value) {
305 1
            if (!$value) {
306
                $idActions = [
307 1
                    'edit',
308
                    'delete',
309
                ];
310
                
311 1
                if (in_array($this->actionName, $idActions)) {
312
                    $value = [
313
                        'id',
314
                    ];
315
                }
316
            }
317
            
318 1
            return $value;
319 1
        };
320
    }
321
    
322
    /**
323
     * Return the batch normalizer. If null value is provided, it will add the delete batch action if the delete
324
     * actions is allowed .
325
     *
326
     * @return Closure
327
     */
328 1
    private function getBatchNormalizer()
329
    {
330
        return function(Options $options, $batch) {
331
            // if batch is not activated, no more checks should be done
332 1
            if ($batch === false) {
333 1
                return $batch;
334
            }
335
            // for list actions, we add a default configuration
336
            if ($batch === null) {
337
                // delete action should be allowed in order to be place in batch actions
338
                $allowedActions = array_keys($this
339
                    ->admin
340
                    ->getConfiguration()
341
                    ->getParameter('actions'));
342
                
343
                if ($this->actionName == 'list' && in_array('delete', $allowedActions)) {
344
                    $pattern = $this
345
                        ->admin
346
                        ->getConfiguration()
347
                        ->getParameter('translation_pattern')
348
                    ;
349
                    
350
                    $batch = [
351
                        'items' => [
352
                            'delete' => [
353
                                'admin' => $this->admin->getName(),
354
                                'action' => 'delete',
355
                                'text' => $this->getTranslationKey($pattern, 'delete', $this->admin->getName()),
356
                            ],
357
                        ],
358
                    ];
359
                } else {
360
                    return $batch;
361
                }
362
            }
363
            $resolver = new OptionsResolver();
364
            $configuration = new MenuConfiguration();
365
            $configuration->configureOptions($resolver);
366
            $batch = $resolver->resolve($batch);
367
            
368
            return $batch;
369 1
        };
370
    }
371
    
372
    /**
373
     * Return the filters normalizer.
374
     *
375
     * @return Closure
376
     */
377
    private function getFiltersNormalizer()
378
    {
379 1
        return function(Options $options, $filters) {
380 1
            if (!is_array($filters)) {
381
                return null;
382
            }
383 1
            $normalizedData = [];
384
            
385 1
            foreach ($filters as $filter => $filterOptions) {
386
                
387
                // the filter name should be a string
388
                if (!is_string($filter)) {
389
                    throw new ConfigurationException(
390
                        'Invalid filter name "'.$filter.'"',
391
                        $this->actionName,
392
                        $this->admin->getName())
0 ignored issues
show
Documentation introduced by
$this->admin->getName() is of type string, but the function expects a null|object<LAG\AdminBundle\Admin\AdminInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
393
                    ;
394
                }
395
                
396
                // normalize string notation
397
                // transform "name" => 'string' into "name" => ['type' => 'string']
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
398
                if (is_string($filterOptions)) {
399
                    $filterOptions = [
400
                        'type' => $filterOptions,
401
                    ];
402
                }
403
                
404
                // set the normalized data
405
                $normalizedData[$filter] = $filterOptions;
406
            }
407
            
408 1
            return $normalizedData;
409 1
        };
410
    }
411
    
412
    /**
413
     * Return the default title using the configured translation pattern.
414
     *
415
     * @return string
416
     */
417 1
    private function getDefaultTitle()
418
    {
419
        $translationPattern = $this
420 1
            ->admin
421 1
            ->getConfiguration()
422 1
            ->getParameter('translation_pattern')
423
        ;
424
        
425 1
        if ($this->admin && $translationPattern) {
426
            // by default, the action title is action name using the configured translation pattern
427
            
428
            $actionTitle = $this->getTranslationKey(
429
                $translationPattern,
430
                $this->actionName,
431
                $this->admin->getName()
432
            );
433
        } else {
434
            // no admin was provided, we camelize the action name
435 1
            $actionTitle = Container::camelize($this->actionName);
436
        }
437
        
438 1
        return $actionTitle;
439
    }
440
    
441
    /**
442
     * Return the default route path according to the action name.
443
     *
444
     * @return string
445
     */
446 1
    private function getDefaultRoutePath()
447
    {
448
        $pattern = $this
449 1
            ->admin
450 1
            ->getConfiguration()
451 1
            ->getParameter('routing_url_pattern')
452
        ;
453
        
454 1
        $path = str_replace('{admin}', $this->admin->getName(), $pattern);
455 1
        $path = str_replace('{action}', $this->actionName, $path);
456
        
457 1
        if (in_array($this->actionName, ['edit', 'delete'])) {
458
            $path .= '/{id}';
459
        }
460
        
461 1
        return $path;
462
    }
463
    
464
    /**
465
     * Return the defaults route parameters according to a mapping based on the action name.
466
     *
467
     * @return array
468
     */
469 1
    private function getDefaultRouteDefaults()
470
    {
471
        $mapping = [
472
            'list' => [
473 1
                '_controller' => LAGAdminBundle::SERVICE_ID_LIST_ACTION,
474 1
                '_admin' => $this->admin->getName(),
475 1
                '_action' => $this->actionName,
476
            ],
477
            'create' => [
478 1
                '_controller' => LAGAdminBundle::SERVICE_ID_CREATE_ACTION,
479 1
                '_admin' => $this->admin->getName(),
480 1
                '_action' => $this->actionName,
481
            ],
482
            'edit' => [
483 1
                '_controller' => LAGAdminBundle::SERVICE_ID_EDIT_ACTION,
484 1
                '_admin' => $this->admin->getName(),
485 1
                '_action' => $this->actionName,
486
            ],
487
            'delete' => [
488 1
                '_controller' => LAGAdminBundle::SERVICE_ID_DELETE_ACTION,
489 1
                '_admin' => $this->admin->getName(),
490 1
                '_action' => $this->actionName,
491
            ],
492
        ];
493 1
        $defaults = [];
494
        
495 1
        if (array_key_exists($this->actionName, $mapping)) {
496 1
            $defaults = $mapping[$this->actionName];
497
        }
498
        
499 1
        return $defaults;
500
    }
501
    
502
    /**
503
     * Return the default route requirements according to the action name.
504
     *
505
     * @return array
506
     */
507 1
    private function getDefaultRouteRequirements()
508
    {
509
        $mapping = [
510 1
            'edit' => [
511
                'id' => '\d+',
512
            ],
513
            'delete' => [
514
                'id' => '\d+',
515
            ],
516
        ];
517 1
        $requirements = [];
518
        
519 1
        if (array_key_exists($this->actionName, $mapping)) {
520
            $requirements = $mapping[$this->actionName];
521
        }
522
        
523 1
        return $requirements;
524
    }
525
    
526
    /**
527
     * Return the default service id according to the action name.
528
     *
529
     * @return string|null
530
     */
531 1
    private function getDefaultServiceId()
532
    {
533 1
        $mapping = LAGAdminBundle::getDefaultActionServiceMapping();
534
        
535 1
        if (!array_key_exists($this->actionName, $mapping)) {
536
            return null;
537
        }
538
        
539 1
        return $mapping[$this->actionName];
540
    }
541
    
542
    /**
543
     * Return the default form according to the action name.
544
     *
545
     * @return string|null
546
     */
547 1
    private function getDefaultForm()
548
    {
549
        $mapping = [
550 1
            'list' => ListType::class,
551
            'delete' => DeleteType::class,
552
        ];
553
        
554 1
        if (!array_key_exists($this->actionName, $mapping)) {
555
            // try to get an admin globally configured form
556
            $adminForm = $this
557
                ->admin
558
                ->getConfiguration()
559
                ->getParameter('form')
560
            ;
561
            
562
            if (null !== $adminForm) {
563
                return $adminForm;
564
            }
565
            
566
            return null;
567
        }
568
        
569 1
        return $mapping[$this->actionName];
570
    }
571
    
572
    /**
573
     * Return a default form handler service id, or null, according to to the action name.
574
     *
575
     * @return mixed|null
576
     */
577 1
    private function getDefaultFormHandler()
578
    {
579
        $mapping = [
580 1
            'edit' => LAGAdminBundle::SERVICE_ID_EDIT_FORM_HANDLER,
581 1
            'list' => LAGAdminBundle::SERVICE_ID_LIST_FORM_HANDLER,
582
        ];
583
        
584 1
        if (!array_key_exists($this->actionName, $mapping)) {
585
            return null;
586
        }
587
        
588 1
        return $mapping[$this->actionName];
589
    }
590
    
591 1
    private function getDefaultFormOptions()
592
    {
593
        $mapping = [
594 1
            'list' => [
595
                'actions' => [
596
                    'lag.admin.delete' => 'delete',
597
                ]
598
            ],
599
        ];
600
    
601 1
        if (!$this->isActionInMapping($mapping)) {
602
            return [];
603
        }
604
    
605 1
        return $mapping[$this->actionName];
606
    }
607
    
608 1
    private function getDefaultTemplate()
609
    {
610
        $mapping = [
611 1
            'list' => '@LAGAdmin/CRUD/list.html.twig',
612
            'edit' => '@LAGAdmin/CRUD/edit.html.twig',
613
            'create' => '@LAGAdmin/CRUD/create.html.twig',
614
            'delete' => '@LAGAdmin/CRUD/delete.html.twig',
615
        ];
616
        
617 1
        if (!$this->isActionInMapping($mapping)) {
618
            return null;
619
        }
620
        
621 1
        return $mapping[$this->actionName];
622
    }
623
    
624 1
    private function isActionInMapping(array $mapping)
625
    {
626 1
        return array_key_exists($this->actionName, $mapping);
627
    }
628
    
629 1
    private function getDefaultSortable()
630
    {
631
        $mapping = [
632 1
            'list' => true,
633
        ];
634
    
635 1
        if (!$this->isActionInMapping($mapping)) {
636
            return false;
637
        }
638
    
639 1
        return $mapping[$this->actionName];
640
    }
641
}
642