Completed
Push — refonte ( 322af8...8d7955 )
by Arnaud
02:48 queued 24s
created

ExtraConfigurationSubscriber::addDefaultStrategy()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 9.2248
c 0
b 0
f 0
cc 5
nc 5
nop 1
1
<?php
2
3
namespace LAG\AdminBundle\Event\Subscriber;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Exception;
7
use LAG\AdminBundle\Configuration\ApplicationConfiguration;
8
use LAG\AdminBundle\Configuration\ApplicationConfigurationStorage;
9
use LAG\AdminBundle\Event\AdminEvents;
10
use LAG\AdminBundle\Event\ConfigurationEvent;
11
use LAG\AdminBundle\Event\Menu\MenuConfigurationEvent;
12
use LAG\AdminBundle\Factory\ConfigurationFactory;
13
use LAG\AdminBundle\LAGAdminBundle;
14
use LAG\AdminBundle\Resource\ResourceCollection;
15
use LAG\AdminBundle\Utils\StringUtils;
16
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
17
18
/**
19
 * Add extra default configuration for actions and fields.
20
 */
21
class ExtraConfigurationSubscriber implements EventSubscriberInterface
22
{
23
    /**
24
     * @var ApplicationConfiguration
25
     */
26
    private $applicationConfiguration;
27
28
    /**
29
     * @var EntityManagerInterface
30
     */
31
    private $entityManager;
32
33
    /**
34
     * @var ResourceCollection
35
     */
36
    private $resourceCollection;
37
38
    /**
39
     * @var ConfigurationFactory
40
     */
41
    private $configurationFactory;
42
43
    /**
44
     * @return array
45
     */
46
    public static function getSubscribedEvents()
47
    {
48
        return [
49
            AdminEvents::ADMIN_CONFIGURATION => 'enrichAdminConfiguration',
50
            AdminEvents::MENU_CONFIGURATION => 'enrichMenuConfiguration',
51
        ];
52
    }
53
54
    /**
55
     * ExtraConfigurationSubscriber constructor.
56
     *
57
     * @param ApplicationConfigurationStorage $applicationConfigurationStorage
58
     * @param EntityManagerInterface          $entityManager
59
     * @param ResourceCollection              $resourceCollection
60
     * @param ConfigurationFactory            $configurationFactory
61
     */
62 View Code Duplication
    public function __construct(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
63
        ApplicationConfigurationStorage $applicationConfigurationStorage,
64
        EntityManagerInterface $entityManager,
65
        ResourceCollection $resourceCollection,
66
        ConfigurationFactory $configurationFactory
67
    ) {
68
        $this->applicationConfiguration = $applicationConfigurationStorage->getConfiguration();
69
        $this->entityManager = $entityManager;
70
        $this->resourceCollection = $resourceCollection;
71
        $this->configurationFactory = $configurationFactory;
72
    }
73
74
    public function enrichAdminConfiguration(ConfigurationEvent $event)
75
    {
76
        if (!$this->isExtraConfigurationEnabled()) {
77
            return;
78
        }
79
        $configuration = $event->getConfiguration();
80
81
        // Actions
82
        $this->addDefaultActions($configuration);
83
84
        // Fields
85
        $this->addDefaultFields($configuration, $event->getEntityClass(), $event->getAdminName());
86
        $this->addDefaultStrategy($configuration);
87
        $this->addDefaultRouteParameters($configuration);
88
        $this->addDefaultFormUse($configuration);
89
90
        // Menus
91
        $this->addDefaultRightMenu($configuration);
92
        $this->addDefaultLeftMenu($configuration);
93
        $this->addDefaultTopMenu($configuration, $event->getAdminName());
94
95
        // Filters
96
        $this->addDefaultFilters($configuration);
97
98
        $event->setConfiguration($configuration);
99
    }
100
101
    public function enrichMenuConfiguration(MenuConfigurationEvent $event)
102
    {
103
        if (!$this->isExtraConfigurationEnabled()) {
104
            return;
105
        }
106
        $configuration = $event->getMenuConfigurations();
107
108
        if (!key_exists('top', $configuration) || [] === $configuration['top']) {
109
            $configuration['top'] = [
110
                'brand' => $this->applicationConfiguration->getParameter('title'),
111
            ];
112
        }
113
114
        if (!key_exists('left', $configuration) || [] === $configuration['left']) {
115
            $configuration['left'] = $this->configurationFactory->createResourceMenuConfiguration();
116
        }
117
118
        $event->setMenuConfigurations($configuration);
119
    }
120
121
    /**
122
     * Defines the default CRUD actions if no action was configured.
123
     *
124
     * @param array $configuration
125
     */
126
    private function addDefaultActions(array &$configuration)
127
    {
128
        if (!key_exists('actions', $configuration) || !is_array($configuration['actions'])) {
129
            $configuration['actions'] = [];
130
        }
131
132
        if (0 !== count($configuration['actions'])) {
133
            return;
134
        }
135
        $configuration['actions'] = [
136
            'create' => [],
137
            'list' => [],
138
            'edit' => [],
139
            'delete' => [],
140
        ];
141
    }
142
143
    private function addDefaultFields(array &$configuration, $entityClass, $adminName)
144
    {
145
        $fieldsMapping = [
146
            'string' => [
147
                'type' => 'string',
148
                'options' => [
149
                    'length' => 100,
150
                ],
151
            ],
152
            'boolean' => [
153
                'type' => 'boolean',
154
                'options' => [],
155
            ],
156
            'datetime' => [
157
                'type' => 'date',
158
                'options' => [],
159
            ],
160
        ];
161
162
        foreach ($configuration['actions'] as $actionName => $action) {
163
            if (null === $action) {
164
                $action = [];
165
            }
166
            $metadata = $this->findMetadata($entityClass);
167
168
            if (null === $metadata) {
169
                continue;
170
            }
171
172
            // If fields are already defined, nothing to do
173
            if (key_exists('fields', $action) && is_array($action['fields']) && count($action['fields'])) {
174
                $fields = $action['fields'];
175
            } else {
176
                $fields = [];
177
178
                // Get fields names from the metadata if no configuration is defined
179
                foreach ($metadata->getFieldNames() as $fieldName) {
180
                    $fields[$fieldName] = null;
181
                }
182
            }
183
            $actionField = $this->getActionField($metadata->getFieldNames());
184
185
            foreach ($fields as $fieldName => $fieldConfiguration) {
186
                $fieldType = $metadata->getTypeOfField($fieldName);
187
188
                if (
189
                    'list' === $actionName &&
190
                    $fieldName === $actionField &&
191
                    key_exists('edit', $configuration['actions'])
192
                ) {
193
                    $fieldConfiguration = [
194
                        'type' => 'action',
195
                        'options' => [
196
                            'admin' => $adminName,
197
                            'action' => 'edit',
198
                            'parameters' => [
199
                                'id' => null,
200
                            ],
201
                        ]
202
                    ];
203
204
                } else if (
205
                    '_delete' === $fieldName &&
206
                    !$metadata->hasField('_delete') &&
207
                    null === $fieldConfiguration &&
208
                    key_exists('delete', $configuration['actions'])
209
                ) {
210
                    // If a "delete" field is declared, and if it is not configured in the metadata, and if no
211
                    // configuration is declared for this field, and if the "delete" action is allowed, we add a default
212
                    // "button" configuration
213
                    $fieldConfiguration = [
214
                        'type' => 'link',
215
                        'options' => [
216
                            'admin' => $adminName,
217
                            'action' => 'delete',
218
                            'parameters' => [
219
                                'id' => null,
220
                            ],
221
                            'text' => 'Delete',
222
                            'class' => 'btn btn-sm btn-danger',
223
                            'icon' => 'remove',
224
                        ],
225
                    ];
226
227
                } else if (key_exists($fieldType, $fieldsMapping)) {
228
                    $fieldConfiguration = $fieldsMapping[$metadata->getTypeOfField($fieldName)];
229
                }
230
                $configuration['actions'][$actionName]['fields'][$fieldName] = $fieldConfiguration;
231
            }
232
        }
233
    }
234
235
    private function addDefaultStrategy(array &$configuration)
236
    {
237
        $mapping = [
238
            'list' => LAGAdminBundle::LOAD_STRATEGY_MULTIPLE,
239
            'create' => LAGAdminBundle::LOAD_STRATEGY_NONE,
240
            'delete' => LAGAdminBundle::LOAD_STRATEGY_UNIQUE,
241
            'edit' => LAGAdminBundle::LOAD_STRATEGY_UNIQUE,
242
        ];
243
244
        foreach ($configuration['actions'] as $name => $action) {
245
            if (null === $action) {
246
                continue;
247
            }
248
249
            if (key_exists('load_strategy', $action)) {
250
                continue;
251
            }
252
253
            if (!key_exists($name, $mapping)) {
254
                continue;
255
            }
256
            $configuration['actions'][$name]['load_strategy'] = $mapping[$name];
257
        }
258
    }
259
260
    private function addDefaultRouteParameters(array &$configuration)
261
    {
262
        $mapping = [
263
            'edit' => [
264
                'id' => '\d+',
265
            ],
266
            'delete' => [
267
                'id' => '\d+',
268
            ],
269
        ];
270
271 View Code Duplication
        foreach ($configuration['actions'] as $name => $actionConfiguration) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
272
            if (key_exists($name, $mapping) && !key_exists('route_requirements', $actionConfiguration)) {
273
                $configuration['actions'][$name]['route_requirements'] = [
274
                    'id' => '\d+',
275
                ];
276
            }
277
        }
278
    }
279
280
    private function addDefaultFormUse(array &$configuration)
281
    {
282
        $mapping = [
283
            'edit',
284
            'create',
285
            'delete',
286
        ];
287
288
        foreach ($configuration['actions'] as $name => $action) {
289
            if (!in_array($name, $mapping) && !isset($action['use_form'])) {
290
                continue;
291
            }
292
            $configuration['actions'][$name]['use_form'] = true;
293
        }
294
    }
295
296
    /**
297
     * Add the default left menu configuration. One item for each Admin.
298
     *
299
     * @param array $configuration
300
     */
301
    private function addDefaultLeftMenu(array &$configuration)
302
    {
303
        if (!$this->applicationConfiguration->getParameter('enable_menus')) {
304
            return;
305
        }
306
        $menus = $this->configurationFactory->createResourceMenuConfiguration();
307
308
        // Add the resources menu for each action of the admin
309 View Code Duplication
        foreach ($configuration['actions'] as $name => $action) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
310
            if (key_exists('menus', $action) && key_exists('left', $action)) {
311
                continue;
312
            }
313
314
            $configuration['actions'][$name]['menus']['left'] = $menus;
315
        }
316
    }
317
318
    /**
319
     * Add the default right menu.
320
     *
321
     * @param array  $configuration
322
     */
323
    private function addDefaultRightMenu(array &$configuration)
324
    {
325
        if (!$this->applicationConfiguration->getParameter('enable_menus')) {
326
            return;
327
        }
328
329
        if (!key_exists('list', $configuration['actions'])) {
330
            return;
331
        }
332
333
        if (
334
            key_exists('menus', $configuration['actions']['list']) &&
335
            is_array($configuration['actions']['list']['menus']) &&
336
            key_exists('right', $configuration['actions']['list']['menus'])
337
        ) {
338
            return;
339
        }
340
341
        $configuration['actions']['list']['menus']['right'] = [];
342
    }
343
344
    private function addDefaultTopMenu(array &$configuration, string $adminName)
345
    {
346
        if (!$this->applicationConfiguration->getParameter('enable_menus')) {
347
            return;
348
        }
349
350 View Code Duplication
        if (key_exists('list', $configuration['actions'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
351
            // Add a "Create" link in the top bar if the create action is allowed
352
            if (!key_exists('create', $configuration['actions'])) {
353
                return;
354
            }
355
            $configuration['actions']['list']['menus']['top']['items'][] = [
356
                'admin' => $adminName,
357
                'action' => 'create',
358
                'text' => StringUtils::getTranslationKey(
359
                    $this->applicationConfiguration->getParameter('translation_pattern'),
360
                    $adminName,
361
                    'create'
362
                ),
363
                'icon' => 'plus',
364
            ];
365
        }
366
367 View Code Duplication
        if (key_exists('create', $configuration['actions'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
368
            // Add a "Return" link in the top bar if the list action is allowed
369
            if (!key_exists('list', $configuration['actions'])) {
370
                return;
371
            }
372
            $configuration['actions']['create']['menus']['top']['items'][] = [
373
                'admin' => $adminName,
374
                'action' => 'list',
375
                'text' => StringUtils::getTranslationKey(
376
                    $this->applicationConfiguration->getParameter('translation_pattern'),
377
                    $adminName,
378
                    'return'
379
                ),
380
                'icon' => 'arrow-left',
381
            ];
382
        }
383
384
        if (key_exists('edit', $configuration['actions'])) {
385
            // Add a "Return" link in the top bar if the list action is allowed
386
            if (!key_exists('list', $configuration['actions'])) {
387
                return;
388
            }
389
390
            if (!key_exists('menus', $configuration['actions']['edit'])) {
391
                $configuration['actions']['edit']['menus'] = [];
392
            }
393
394 View Code Duplication
            if (!key_exists('top', $configuration['actions']['edit']['menus'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
395
                $configuration['actions']['edit']['menus']['top'] = [];
396
            }
397
398 View Code Duplication
            if (!key_exists('items', $configuration['actions']['edit']['menus']['top'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
399
                $configuration['actions']['edit']['menus']['top']['items'] = [];
400
            }
401
            array_unshift($configuration['actions']['edit']['menus']['top']['items'], [
402
                'admin' => $adminName,
403
                'action' => 'list',
404
                'text' => StringUtils::getTranslationKey(
405
                    $this->applicationConfiguration->getParameter('translation_pattern'),
406
                    $adminName,
407
                    'return'
408
                ),
409
                'icon' => 'arrow-left',
410
            ]);
411
        }
412
    }
413
414
    /**
415
     * Return the default action field if found.
416
     *
417
     * @param array $fields
418
     *
419
     * @return string|null
420
     */
421
    private function getActionField(array $fields)
422
    {
423
        $mapping = [
424
            'title',
425
            'name',
426
            'id',
427
        ];
428
429
        foreach ($mapping as $name) {
430
            if (in_array($name, $fields)) {
431
                return $name;
432
            }
433
        }
434
435
        return null;
436
    }
437
438
    /**
439
     * Add default filters for the list actions, guessed using the entity metadata.
440
     *
441
     * @param array $configuration
442
     */
443
    private function addDefaultFilters(array &$configuration)
444
    {
445
        // Add the filters only for the "list" action
446
        if (!key_exists('list', $configuration['actions'])) {
447
            return;
448
        }
449
450
        // If some filters are already configured, we do not add the default filters
451
        if (key_exists('filter', $configuration['actions']['list'])) {
452
            return;
453
        }
454
        $metadata = $this->findMetadata($configuration['entity']);
455
456
        if (null === $metadata) {
457
            return;
458
        }
459
        $filters = [];
460
461
        foreach ($metadata->getFieldNames() as $fieldName) {
462
            $type = $metadata->getTypeOfField($fieldName);
463
            $operator = $this->getOperatorFromFieldType($type);
464
465
            $filters[$fieldName] = [
466
                'type' => $type,
467
                'options' => [],
468
                'operator' => $operator,
469
            ];
470
        }
471
        $configuration['actions']['list']['filters'] = $filters;
472
    }
473
474
    /**
475
     * Return the Doctrine metadata of the given class.
476
     *
477
     * @param $class
478
     *
479
     * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata|null
480
     */
481
    private function findMetadata($class)
482
    {
483
        $metadata = null;
484
485
        try {
486
            // We could not use the hasMetadataFor() method as it is not working if the entity is not loaded. But
487
            // the getMetadataFor() method could throw an exception if the class is not found
488
            $metadata = $this->entityManager->getMetadataFactory()->getMetadataFor($class);
489
        } catch (Exception $exception) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
490
491
        return $metadata;
492
    }
493
494
    private function getOperatorFromFieldType($type)
495
    {
496
        $mapping = [
497
            'string' => 'like',
498
            'text' => 'like',
499
        ];
500
501
        if (key_exists($type, $mapping)) {
502
            return $mapping[$type];
503
        }
504
505
        return '=';
506
    }
507
508
    /**
509
     * @return bool
510
     */
511
    private function isExtraConfigurationEnabled(): bool
512
    {
513
        return $this->applicationConfiguration->getParameter('enable_extra_configuration');
514
    }
515
}
516