Completed
Pull Request — master (#90)
by Arnaud
16:42
created

ActionConfiguration::getDefaultTitle()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 0
cts 10
cp 0
rs 9.2
c 0
b 0
f 0
cc 2
eloc 12
nc 2
nop 0
crap 6
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\Admin\Configuration\AdminConfiguration;
10
use LAG\AdminBundle\Filter\Configuration\FilterConfiguration;
11
use LAG\AdminBundle\Form\Type\DeleteType;
12
use LAG\AdminBundle\Form\Type\ListType;
13
use LAG\AdminBundle\LAGAdminBundle;
14
use LAG\AdminBundle\Menu\Configuration\MenuConfiguration;
15
use LAG\AdminBundle\Routing\RouteNameGenerator;
16
use Symfony\Component\DependencyInjection\Container;
17
use Symfony\Component\OptionsResolver\Options;
18
use Symfony\Component\OptionsResolver\OptionsResolver;
19
20
class ActionConfiguration extends Configuration
21
{
22
    use TranslationKeyTrait;
23
    
24
    /**
25
     * Related Action name.
26
     *
27
     * @var string
28
     */
29
    private $actionName;
30
    
31
    /**
32
     * @var AdminConfiguration
33
     */
34
    private $adminConfiguration;
35
    
36
    /**
37
     * @var string
38
     */
39
    private $adminName;
40
    
41
    /**
42
     * ActionConfiguration constructor.
43
     *
44
     * @param string             $actionName
45
     * @param                    $adminName
46
     * @param AdminConfiguration $adminConfiguration
47
     */
48
    public function __construct($actionName, $adminName, AdminConfiguration $adminConfiguration)
49
    {
50
        parent::__construct();
51
        
52
        $this->actionName = $actionName;
53
        $this->adminConfiguration = $adminConfiguration;
54
        $this->adminName = $adminName;
55
    }
56
    
57
    /**
58
     * Define allowed parameters and values for this configuration, using optionsResolver component.
59
     *
60
     * @param OptionsResolver $resolver
61
     */
62
    public function configureOptions(OptionsResolver $resolver)
63
    {
64
        $this->configureDefaultOptions($resolver);
65
        $this->configureNormalizers($resolver);
66
        
67
        $this->setAllowedTypes($resolver);
68
        $this->setAllowedValues($resolver);
69
    }
70
    
71
    /**
72
     * Configure the default options for an Action.
73
     *
74
     * @param OptionsResolver $resolver
75
     */
76
    private function configureDefaultOptions(OptionsResolver $resolver)
77
    {
78
        $resolver
79
            ->setDefaults([
80
                // the default action title is a translation key using the translation pattern and the action name
81
                'title' => $this->getDefaultTitle(),
82
                'service' => $this->getDefaultServiceId(),
83
                'fields' => [
84
                    'id' => [],
85
                ],
86
                // by default, only administrator can access the admin
87
                'permissions' => [
88
                    'ROLE_ADMIN',
89
                ],
90
                // all export are activated
91
                'export' => [
92
                    'json',
93
                    'html',
94
                    'csv',
95
                    'xls',
96
                ],
97
                // no order
98
                'order' => [],
99
                // route will be generated if empty string or null or false is provided
100
                'route' => '',
101
                // route parameters will be used when using the route (for links...)
102
                'route_parameters' => [],
103
                'route_path' => $this->getDefaultRoutePath(),
104
                'route_defaults' => $this->getDefaultRouteDefaults(),
105
                'route_requirements' => $this->getDefaultRouteRequirements(),
106
                // icon in the menu
107
                'icon' => '',
108
                // entities loading strategy
109
                'load_strategy' => null,
110
                // pager interface, only null or pagerfanta are allowed
111
                'pager' => 'pagerfanta',
112
                'max_per_page' => $this->adminConfiguration->getParameter('max_per_page'),
113
                // default criteria used to load entities
114
                'criteria' => [],
115
                // filters, should be an array of string (field name => filter options)
116
                'filters' => [],
117
                'menus' => [],
118
                'batch' => false,
119
                // form configuration
120
                'form_handler' => $this->getDefaultFormHandler(),
121
                'form' => $this->getDefaultForm(),
122
                'form_options' => $this->getDefaultFormOptions(),
123
                // twig template
124
                'template' => $this->getDefaultTemplate(),
125
                'sortable' => $this->getDefaultSortable(),
126
                // responder
127
                'responder' => $this->getDefaultResponder(),
128
            ]);
129
    }
130
    
131
    /**
132
     * Define the allowed values.
133
     *
134
     * @param OptionsResolver $resolver
135
     */
136
    private function setAllowedValues(OptionsResolver $resolver)
137
    {
138
        $resolver
139
            ->setAllowedValues('load_strategy', [
140
                AdminInterface::LOAD_STRATEGY_NONE,
141
                AdminInterface::LOAD_STRATEGY_UNIQUE,
142
                AdminInterface::LOAD_STRATEGY_MULTIPLE,
143
                null,
144
            ])
145
            ->setAllowedValues('pager', [
146
                'pagerfanta',
147
                false,
148
            ])
149
        ;
150
    }
151
    
152
    /**
153
     * Define the allowed types.
154
     *
155
     * @param OptionsResolver $resolver
156
     */
157
    private function setAllowedTypes(OptionsResolver $resolver)
158
    {
159
        $resolver
160
            ->setAllowedTypes('title', 'string')
161
            ->setAllowedTypes('fields', 'array')
162
            ->setAllowedTypes('order', 'array')
163
            ->setAllowedTypes('route', 'string')
164
            ->setAllowedTypes('route_parameters', 'array')
165
            ->setAllowedTypes('icon', 'string')
166
            ->setAllowedTypes('criteria', 'array')
167
            ->setAllowedTypes('filters', 'array')
168
            ->setAllowedTypes('menus', [
169
                'array',
170
                'boolean',
171
            ])
172
            ->setAllowedTypes('route_path', 'string')
173
            ->setAllowedTypes('route_defaults', 'array')
174
            ->setAllowedTypes('route_requirements', 'array')
175
            ->setAllowedTypes('form', 'string')
176
            ->setAllowedTypes('form_options', 'array')
177
        ;
178
    }
179
    
180
    /**
181
     * Configure the normalizers.
182
     *
183
     * @param OptionsResolver $resolver
184
     */
185
    private function configureNormalizers(OptionsResolver $resolver)
186
    {
187
        $resolver
188
            ->setNormalizer('fields', $this->getFieldsNormalizer())
189
            ->setNormalizer('order', $this->getOrderNormalizer())
190
            ->setNormalizer('route', $this->getRouteNormalizer())
191
            ->setNormalizer('load_strategy', $this->getLoadStrategyNormalizer())
192
            ->setNormalizer('criteria', $this->getCriteriaNormalizer())
193
            ->setNormalizer('menus', $this->getMenuNormalizer())
194
            ->setNormalizer('batch', $this->getBatchNormalizer())
195
            ->setNormalizer('filters', $this->getFiltersNormalizer())
196
            ->setNormalizer('route_defaults', $this->getRouteDefaultNormalizer())
197
        ;
198
    }
199
    
200
    /**
201
     * Return the field normalizer. It will transform null configuration into array to allow field type guessing
202
     * working.
203
     *
204
     * @return Closure
205
     */
206
    private function getFieldsNormalizer()
207
    {
208
        return function(Options $options, $fields) {
209
            $normalizedFields = [];
210
            
211
            foreach ($fields as $name => $field) {
212
                
213
                if ($field === null) {
214
                    $field = [];
215
                }
216
                
217
                $normalizedFields[$name] = $field;
218
            }
219
            
220
            return $normalizedFields;
221
        };
222
    }
223
    
224
    /**
225
     * Return the order normalizer. It will check if the order value passed are valid.
226
     *
227
     * @return Closure
228
     */
229
    private function getOrderNormalizer()
230
    {
231
        return function(Options $options, $order) {
232
            foreach ($order as $field => $sort) {
233
                
234
                if (!is_string($sort) || !is_string($field) || !in_array(strtolower($sort), ['asc', 'desc'])) {
235
                    throw new ConfigurationException(
236
                        'Order value should be an array of string (["field" => $key]), got '.gettype($sort),
237
                        $this->actionName,
238
                        $this->adminName
0 ignored issues
show
Documentation introduced by
$this->adminName 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...
239
                    );
240
                }
241
            }
242
            
243
            return $order;
244
        };
245
    }
246
    
247
    /**
248
     * Return the route normalizer. If an empty value or null or false, it will generate the route using the Admin.
249
     *
250
     * @return Closure
251
     */
252
    private function getRouteNormalizer()
253
    {
254
        return function(Options $options, $value) {
255
            $generator = new RouteNameGenerator();
256
            
257
            if (!$value) {
258
                // generate default route from admin
259
                return $generator->generate($this->actionName, $this->adminName, $this->adminConfiguration);
260
            }
261
            
262
            return $value;
263
        };
264
    }
265
    
266
    /**
267
     * Return the load strategy normalizer. It will set the default strategy according to the action name, if no value
268
     * is provided.
269
     *
270
     * @return Closure
271
     */
272
    private function getLoadStrategyNormalizer()
273
    {
274
        return function(Options $options, $value) {
275
            if (!$value) {
276
                if ($this->actionName == 'create') {
277
                    $value = AdminInterface::LOAD_STRATEGY_NONE;
278
                } else if ($this->actionName == 'list') {
279
                    $value = AdminInterface::LOAD_STRATEGY_MULTIPLE;
280
                } else {
281
                    $value = AdminInterface::LOAD_STRATEGY_UNIQUE;
282
                }
283
            }
284
            
285
            return $value;
286
        };
287
    }
288
    
289
    /**
290
     * Return the menu normalizer. It will transform false values into an empty array to allow default menu
291
     * configuration working.
292
     *
293
     * @return Closure
294
     */
295
    private function getMenuNormalizer()
296
    {
297
        return function(Options $options, $menus) {
298
            // set default to an array
299
            if ($menus === false) {
300
                $menus = [];
301
            }
302
            
303
            return $menus;
304
        };
305
    }
306
    
307
    /**
308
     * Return the criteria normalizer. It will add the id parameters for the edit and delete actions if no value is
309
     * provided.
310
     *
311
     * @return Closure
312
     */
313
    private function getCriteriaNormalizer()
314
    {
315
        return function(Options $options, $value) {
316
            if (!$value) {
317
                $idActions = [
318
                    'edit',
319
                    'delete',
320
                ];
321
                
322
                if (in_array($this->actionName, $idActions)) {
323
                    $value = [
324
                        'id',
325
                    ];
326
                }
327
            }
328
            
329
            return $value;
330
        };
331
    }
332
    
333
    /**
334
     * Return the batch normalizer. If null value is provided, it will add the delete batch action if the delete
335
     * actions is allowed .
336
     *
337
     * @return Closure
338
     */
339
    private function getBatchNormalizer()
340
    {
341
        return function(Options $options, $batch) {
342
            // if batch is not activated, no more checks should be done
343
            if ($batch === false) {
344
                return $batch;
345
            }
346
            // for list actions, we add a default configuration
347
            if ($batch === null) {
348
                // delete action should be allowed in order to be place in batch actions
349
                $actionConfigurations = $this
350
                    ->adminConfiguration
351
                    ->getParameter('actions')
352
                ;
353
                $allowedActions = array_keys($actionConfigurations);
354
                
355
                if ($this->actionName == 'list' && in_array('delete', $allowedActions)) {
356
                    $pattern = $this
357
                        ->adminConfiguration
358
                        ->getParameter('translation_pattern')
359
                    ;
360
                    
361
                    $batch = [
362
                        'items' => [
363
                            'delete' => [
364
                                'admin' => $this->adminName,
365
                                'action' => 'delete',
366
                                'text' => $this->getTranslationKey($pattern, 'delete', $this->adminName),
367
                            ],
368
                        ],
369
                    ];
370
                } else {
371
                    return $batch;
372
                }
373
            }
374
            $resolver = new OptionsResolver();
375
            $configuration = new MenuConfiguration();
376
            $configuration->configureOptions($resolver);
377
            $batch = $resolver->resolve($batch);
378
            
379
            return $batch;
380
        };
381
    }
382
    
383
    /**
384
     * Return the filters normalizer.
385
     *
386
     * @return Closure
387
     */
388
    private function getFiltersNormalizer()
389
    {
390
        return function(Options $options, $filters) {
391
            if (!is_array($filters)) {
392
                return [];
393
            }
394
            $normalizedData = [];
395
            $resolver = new OptionsResolver();
396
    
397
            foreach ($filters as $filter => $filterOptions) {
398
                // the filter name should be a string
399
                if (!is_string($filter)) {
400
                    throw new ConfigurationException(
401
                        'Invalid filter name "'.$filter.'"',
402
                        $this->actionName
403
                    );
404
                }
405
                // normalize string notation
406
                // 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...
407
                if (is_string($filterOptions)) {
408
                    $filterOptions = [
409
                        'type' => $filterOptions,
410
                    ];
411
                }
412
    
413
                if (null === $filterOptions) {
414
                    $filterOptions = [];
415
                }
416
                $configuration = new FilterConfiguration();
417
                $configuration->configureOptions($resolver);
418
                $filterOptions = $resolver->resolve($filterOptions);
419
                $resolver->clear();
420
    
421
                // set the normalized data
422
                $normalizedData[$filter] = $filterOptions;
423
            }
424
            
425
            return $normalizedData;
426
        };
427
    }
428
    
429
    private function getRouteDefaultNormalizer()
430
    {
431
        return function (Options $options, $value) {
432
            if (!is_array($value)) {
433
                $value = [];
434
            }
435
            $value['_admin'] = $this->adminName;
436
            $value['_action'] = $this->actionName;
437
    
438
            return $value;
439
        };
440
    }
441
    
442
    /**
443
     * Return the default title using the configured translation pattern.
444
     *
445
     * @return string
446
     */
447
    private function getDefaultTitle()
448
    {
449
        $translationPattern = $this
450
            ->adminConfiguration
451
            ->getParameter('translation_pattern')
452
        ;
453
        
454
        if (false !== $translationPattern) {
455
            // by default, the action title is action name using the configured translation pattern
456
            
457
            $actionTitle = $this->getTranslationKey(
458
                $translationPattern,
459
                $this->actionName,
460
                $this->adminName
461
            );
462
        } else {
463
            // no admin was provided, we camelize the action name
464
            $actionTitle = Container::camelize($this->actionName);
465
        }
466
        
467
        return $actionTitle;
468
    }
469
    
470
    /**
471
     * Return the default route path according to the action name.
472
     *
473
     * @return string
474
     */
475
    private function getDefaultRoutePath()
476
    {
477
        $pattern = $this
478
            ->adminConfiguration
479
            ->getParameter('routing_url_pattern')
480
        ;
481
        
482
        $path = str_replace('{admin}', $this->adminName, $pattern);
483
        $path = str_replace('{action}', $this->actionName, $path);
484
        
485
        if (in_array($this->actionName, ['edit', 'delete'])) {
486
            $path .= '/{id}';
487
        }
488
        
489
        return $path;
490
    }
491
    
492
    /**
493
     * Return the defaults route parameters according to a mapping based on the action name.
494
     *
495
     * @return array
496
     */
497
    private function getDefaultRouteDefaults()
498
    {
499
        $mapping = [
500
            'list' => [
501
                '_controller' => LAGAdminBundle::SERVICE_ID_LIST_ACTION,
502
                '_admin' => $this->adminName,
503
                '_action' => $this->actionName,
504
            ],
505
            'create' => [
506
                '_controller' => LAGAdminBundle::SERVICE_ID_CREATE_ACTION,
507
                '_admin' => $this->adminName,
508
                '_action' => $this->actionName,
509
            ],
510
            'edit' => [
511
                '_controller' => LAGAdminBundle::SERVICE_ID_EDIT_ACTION,
512
                '_admin' => $this->adminName,
513
                '_action' => $this->actionName,
514
            ],
515
            'delete' => [
516
                '_controller' => LAGAdminBundle::SERVICE_ID_DELETE_ACTION,
517
                '_admin' => $this->adminName,
518
                '_action' => $this->actionName,
519
            ],
520
        ];
521
        $defaults = [];
522
        
523
        if (array_key_exists($this->actionName, $mapping)) {
524
            $defaults = $mapping[$this->actionName];
525
        }
526
        
527
        return $defaults;
528
    }
529
    
530
    /**
531
     * Return the default route requirements according to the action name.
532
     *
533
     * @return array
534
     */
535
    private function getDefaultRouteRequirements()
536
    {
537
        $mapping = [
538
            'edit' => [
539
                'id' => '\d+',
540
            ],
541
            'delete' => [
542
                'id' => '\d+',
543
            ],
544
        ];
545
        $requirements = [];
546
        
547
        if (array_key_exists($this->actionName, $mapping)) {
548
            $requirements = $mapping[$this->actionName];
549
        }
550
        
551
        return $requirements;
552
    }
553
    
554
    /**
555
     * Return the default service id according to the action name.
556
     *
557
     * @return string|null
558
     */
559
    private function getDefaultServiceId()
560
    {
561
        $mapping = LAGAdminBundle::getDefaultActionServiceMapping();
562
        
563
        if (!array_key_exists($this->actionName, $mapping)) {
564
            return null;
565
        }
566
        
567
        return $mapping[$this->actionName];
568
    }
569
    
570
    /**
571
     * Return the default form according to the action name.
572
     *
573
     * @return string|null
574
     */
575
    private function getDefaultForm()
576
    {
577
        $mapping = [
578
            'list' => ListType::class,
579
            'delete' => DeleteType::class,
580
        ];
581
        
582
        if (!array_key_exists($this->actionName, $mapping)) {
583
            // try to get an admin globally configured form
584
            $adminForm = $this
585
                ->adminConfiguration
586
                ->getParameter('form')
587
            ;
588
            
589
            if (null !== $adminForm) {
590
                return $adminForm;
591
            }
592
            
593
            return null;
594
        }
595
        
596
        return $mapping[$this->actionName];
597
    }
598
    
599
    /**
600
     * Return a default form handler service id, or null, according to to the action name.
601
     *
602
     * @return mixed|null
603
     */
604
    private function getDefaultFormHandler()
605
    {
606
        $mapping = [
607
            'edit' => LAGAdminBundle::SERVICE_ID_EDIT_FORM_HANDLER,
608
            'list' => LAGAdminBundle::SERVICE_ID_LIST_FORM_HANDLER,
609
        ];
610
        
611
        if (!array_key_exists($this->actionName, $mapping)) {
612
            return null;
613
        }
614
        
615
        return $mapping[$this->actionName];
616
    }
617
    
618
    private function getDefaultFormOptions()
619
    {
620
        $mapping = [
621
            'list' => [
622
                'actions' => [
623
                    'lag.admin.delete' => 'delete',
624
                ]
625
            ],
626
        ];
627
    
628
        if (!$this->isActionInMapping($mapping)) {
629
            return [];
630
        }
631
    
632
        return $mapping[$this->actionName];
633
    }
634
    
635
    private function getDefaultTemplate()
636
    {
637
        $mapping = [
638
            'list' => '@LAGAdmin/CRUD/list.html.twig',
639
            'edit' => '@LAGAdmin/CRUD/edit.html.twig',
640
            'create' => '@LAGAdmin/CRUD/create.html.twig',
641
            'delete' => '@LAGAdmin/CRUD/delete.html.twig',
642
        ];
643
        
644
        if (!$this->isActionInMapping($mapping)) {
645
            return null;
646
        }
647
        
648
        return $mapping[$this->actionName];
649
    }
650
    
651
    private function isActionInMapping(array $mapping)
652
    {
653
        return array_key_exists($this->actionName, $mapping);
654
    }
655
    
656
    private function getDefaultSortable()
657
    {
658
        $mapping = [
659
            'list' => true,
660
        ];
661
    
662
        if (!$this->isActionInMapping($mapping)) {
663
            return false;
664
        }
665
    
666
        return $mapping[$this->actionName];
667
    }
668
    
669
    private function getDefaultResponder()
670
    {
671
        $mapping = [
672
            'list' => 'lag.admin.action.list_responder',
673
            'create' => 'lag.admin.action.create_responder',
674
            'edit' => 'lag.admin.action.edit_responder',
675
            'delete' => 'lag.admin.action.delete_responder',
676
        ];
677
    
678
        if (!$this->isActionInMapping($mapping)) {
679
            return null;
680
        }
681
    
682
        return $mapping[$this->actionName];
683
    }
684
}
685