Completed
Push — refonte ( 24809b...225502 )
by Arnaud
22:40 queued 20:32
created

isExtraConfigurationEnabled()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
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 Symfony\Component\EventDispatcher\EventSubscriberInterface;
16
17
/**
18
 * Add extra default configuration for actions and fields.
19
 */
20
class ExtraConfigurationSubscriber implements EventSubscriberInterface
21
{
22
    /**
23
     * @var ApplicationConfiguration
24
     */
25
    private $applicationConfiguration;
26
27
    /**
28
     * @var EntityManagerInterface
29
     */
30
    private $entityManager;
31
32
    /**
33
     * @var ResourceCollection
34
     */
35
    private $resourceCollection;
36
37
    /**
38
     * @var ConfigurationFactory
39
     */
40
    private $configurationFactory;
41
42
    /**
43
     * @return array
44
     */
45
    public static function getSubscribedEvents()
46
    {
47
        return [
48
            AdminEvents::ADMIN_CONFIGURATION => 'enrichAdminConfiguration',
49
            AdminEvents::MENU_CONFIGURATION => 'enrichMenuConfiguration',
50
        ];
51
    }
52
53
    /**
54
     * ExtraConfigurationSubscriber constructor.
55
     *
56
     * @param ApplicationConfigurationStorage $applicationConfigurationStorage
57
     * @param EntityManagerInterface          $entityManager
58
     * @param ResourceCollection              $resourceCollection
59
     * @param ConfigurationFactory            $configurationFactory
60
     */
61 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...
62
        ApplicationConfigurationStorage $applicationConfigurationStorage,
63
        EntityManagerInterface $entityManager,
64
        ResourceCollection $resourceCollection,
65
        ConfigurationFactory $configurationFactory
66
    ) {
67
        $this->applicationConfiguration = $applicationConfigurationStorage->getConfiguration();
68
        $this->entityManager = $entityManager;
69
        $this->resourceCollection = $resourceCollection;
70
        $this->configurationFactory = $configurationFactory;
71
    }
72
73
    public function enrichAdminConfiguration(ConfigurationEvent $event)
74
    {
75
        if (!$this->isExtraConfigurationEnabled()) {
76
            return;
77
        }
78
        $configuration = $event->getConfiguration();
79
80
        // Actions
81
        $this->addDefaultActions($configuration);
82
83
        // Fields
84
        $this->addDefaultFields($configuration, $event->getEntityClass(), $event->getAdminName());
85
        $this->addDefaultStrategy($configuration);
86
        $this->addDefaultRouteParameters($configuration);
87
        $this->addDefaultFormUse($configuration);
88
89
        // Menus
90
        $this->addDefaultRightMenu($configuration);
91
        $this->addDefaultLeftMenu($configuration);
92
93
        // Filters
94
        $this->addDefaultFilters($configuration);
95
96
        $event->setConfiguration($configuration);
97
    }
98
99
    public function enrichMenuConfiguration(MenuConfigurationEvent $event)
100
    {
101
        if (!$this->isExtraConfigurationEnabled()) {
102
            return;
103
        }
104
        $configuration = $event->getMenuConfigurations();
105
106
        if (!key_exists('top', $configuration) || [] === $configuration['top']) {
107
            $configuration['top'] = [
108
                'brand' => $this->applicationConfiguration->getParameter('title'),
109
            ];
110
        }
111
112
        if (!key_exists('left', $configuration) || [] === $configuration['left']) {
113
            $configuration['left'] = $this->configurationFactory->createResourceMenuConfiguration();
114
        }
115
116
        $event->setMenuConfigurations($configuration);
117
    }
118
119
    /**
120
     * Defines the default CRUD actions if no action was configured.
121
     *
122
     * @param array $configuration
123
     */
124
    private function addDefaultActions(array &$configuration)
125
    {
126
        if (!key_exists('actions', $configuration) || !is_array($configuration['actions'])) {
127
            $configuration['actions'] = [];
128
        }
129
130
        if (0 !== count($configuration['actions'])) {
131
            return;
132
        }
133
        $configuration['actions'] = [
134
            'create' => [],
135
            'list' => [],
136
            'edit' => [],
137
            'delete' => [],
138
        ];
139
    }
140
141
    private function addDefaultFields(array &$configuration, $entityClass, $adminName)
142
    {
143
        $fieldsMapping = [
144
            'string' => [
145
                'type' => 'string',
146
                'options' => [
147
                    'length' => 100,
148
                ],
149
            ],
150
            'boolean' => [
151
                'type' => 'boolean',
152
                'options' => [],
153
            ],
154
            'datetime' => [
155
                'type' => 'date',
156
                'options' => [],
157
            ],
158
        ];
159
160
        foreach ($configuration['actions'] as $actionName => $action) {
161
            if (null === $action) {
162
                $action = [];
163
            }
164
            $metadata = $this->findMetadata($entityClass);
165
166
            if (null === $metadata) {
167
                continue;
168
            }
169
170
            // If fields are already defined, nothing to do
171
            if (key_exists('fields', $action) && is_array($action['fields']) && count($action['fields'])) {
172
                $fields = $action['fields'];
173
            } else {
174
                $fields = [];
175
176
                // Get fields names from the metadata if no configuration is defined
177
                foreach ($metadata->getFieldNames() as $fieldName) {
178
                    $fields[$fieldName] = null;
179
                }
180
            }
181
            $actionField = $this->getActionField($metadata->getFieldNames());
182
183
            foreach ($fields as $fieldName => $fieldConfiguration) {
184
                $fieldType = $metadata->getTypeOfField($fieldName);
185
186
                if (
187
                    'list' === $actionName &&
188
                    $fieldName === $actionField &&
189
                    key_exists('edit', $configuration['actions'])
190
                ) {
191
                    $fieldConfiguration = [
192
                        'type' => 'action',
193
                        'options' => [
194
                            'admin' => $adminName,
195
                            'action' => 'edit',
196
                            'parameters' => [
197
                                'id' => null,
198
                            ],
199
                        ]
200
                    ];
201
202
                } else if (
203
                    '_delete' === $fieldName &&
204
                    !$metadata->hasField('_delete') &&
205
                    null === $fieldConfiguration &&
206
                    key_exists('delete', $configuration['actions'])
207
                ) {
208
                    // If a "delete" field is declared, and if it is not configured in the metadata, and if no
209
                    // configuration is declared for this field, and if the "delete" action is allowed, we add a default
210
                    // "button" configuration
211
                    $fieldConfiguration = [
212
                        'type' => 'link',
213
                        'options' => [
214
                            'admin' => $adminName,
215
                            'action' => 'delete',
216
                            'parameters' => [
217
                                'id' => null,
218
                            ],
219
                            'text' => 'Delete',
220
                            'class' => 'btn btn-sm btn-danger',
221
                            'icon' => 'remove',
222
                        ],
223
                    ];
224
225
                } else if (key_exists($fieldType, $fieldsMapping)) {
226
                    $fieldConfiguration = $fieldsMapping[$metadata->getTypeOfField($fieldName)];
227
                }
228
                $configuration['actions'][$actionName]['fields'][$fieldName] = $fieldConfiguration;
229
            }
230
        }
231
    }
232
233
    private function addDefaultStrategy(array &$configuration)
234
    {
235
        $mapping = [
236
            'list' => LAGAdminBundle::LOAD_STRATEGY_MULTIPLE,
237
            'create' => LAGAdminBundle::LOAD_STRATEGY_NONE,
238
            'delete' => LAGAdminBundle::LOAD_STRATEGY_UNIQUE,
239
            'edit' => LAGAdminBundle::LOAD_STRATEGY_UNIQUE,
240
        ];
241
242
        foreach ($configuration['actions'] as $name => $action) {
243
            if (null === $action) {
244
                continue;
245
            }
246
247
            if (key_exists('load_strategy', $action)) {
248
                continue;
249
            }
250
251
            if (!key_exists($name, $mapping)) {
252
                continue;
253
            }
254
            $configuration['actions'][$name]['load_strategy'] = $mapping[$name];
255
        }
256
    }
257
258
    private function addDefaultRouteParameters(array &$configuration)
259
    {
260
        $mapping = [
261
            'edit' => [
262
                'id' => '\d+',
263
            ],
264
            'delete' => [
265
                'id' => '\d+',
266
            ],
267
        ];
268
269 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...
270
            if (key_exists($name, $mapping) && !key_exists('route_requirements', $actionConfiguration)) {
271
                $configuration['actions'][$name]['route_requirements'] = [
272
                    'id' => '\d+',
273
                ];
274
            }
275
        }
276
    }
277
278
    private function addDefaultFormUse(array &$configuration)
279
    {
280
        $mapping = [
281
            'edit',
282
            'create',
283
            'delete',
284
        ];
285
286
        foreach ($configuration['actions'] as $name => $action) {
287
            if (!in_array($name, $mapping) && !isset($action['use_form'])) {
288
                continue;
289
            }
290
            $configuration['actions'][$name]['use_form'] = true;
291
        }
292
    }
293
294
    /**
295
     * Add the default left menu configuration. One item for each Admin.
296
     *
297
     * @param array $configuration
298
     */
299
    private function addDefaultLeftMenu(array &$configuration)
300
    {
301
        if (!$this->applicationConfiguration->getParameter('enable_menus')) {
302
            return;
303
        }
304
        $menus = $this->configurationFactory->createResourceMenuConfiguration();
305
306
        // Add the resources menu for each action of the admin
307 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...
308
            if (key_exists('menus', $action) && key_exists('left', $action)) {
309
                continue;
310
            }
311
312
            $configuration['actions'][$name]['menus']['left'] = $menus;
313
        }
314
    }
315
316
    /**
317
     * Add the default right menu.
318
     *
319
     * @param array  $configuration
320
     */
321
    private function addDefaultRightMenu(array &$configuration)
322
    {
323
        if (!$this->applicationConfiguration->getParameter('enable_menus')) {
324
            return;
325
        }
326
327
        if (!key_exists('list', $configuration['actions'])) {
328
            return;
329
        }
330
331
        if (
332
            key_exists('menus', $configuration['actions']['list']) &&
333
            is_array($configuration['actions']['list']['menus']) &&
334
            key_exists('right', $configuration['actions']['list']['menus'])
335
        ) {
336
            return;
337
        }
338
339
        $configuration['actions']['list']['menus']['right'] = [
340
        ];
341
    }
342
343
    /**
344
     * Return the default action field if found.
345
     *
346
     * @param array $fields
347
     *
348
     * @return string|null
349
     */
350
    private function getActionField(array $fields)
351
    {
352
        $mapping = [
353
            'title',
354
            'name',
355
            'id',
356
        ];
357
358
        foreach ($mapping as $name) {
359
            if (in_array($name, $fields)) {
360
                return $name;
361
            }
362
        }
363
364
        return null;
365
    }
366
367
    /**
368
     * Add default filters for the list actions, guessed using the entity metadata.
369
     *
370
     * @param array $configuration
371
     */
372
    private function addDefaultFilters(array &$configuration)
373
    {
374
        // Add the filters only for the "list" action
375
        if (!key_exists('list', $configuration['actions'])) {
376
            return;
377
        }
378
379
        // If some filters are already configured, we do not add the default filters
380
        if (key_exists('filter', $configuration['actions']['list'])) {
381
            return;
382
        }
383
        $metadata = $this->findMetadata($configuration['entity']);
384
385
        if (null === $metadata) {
386
            return;
387
        }
388
        $filters = [];
389
390
        foreach ($metadata->getFieldNames() as $fieldName) {
391
            $type = $metadata->getTypeOfField($fieldName);
392
            $operator = $this->getOperatorFromFieldType($type);
393
394
            $filters[$fieldName] = [
395
                'type' => $type,
396
                'options' => [],
397
                'operator' => $operator,
398
            ];
399
        }
400
        $configuration['actions']['list']['filters'] = $filters;
401
    }
402
403
    /**
404
     * Return the Doctrine metadata of the given class.
405
     *
406
     * @param $class
407
     *
408
     * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata|null
409
     */
410
    private function findMetadata($class)
411
    {
412
        $metadata = null;
413
414
        try {
415
            // We could not use the hasMetadataFor() method as it is not working if the entity is not loaded. But
416
            // the getMetadataFor() method could throw an exception if the class is not found
417
            $metadata = $this->entityManager->getMetadataFactory()->getMetadataFor($class);
418
        } catch (Exception $exception) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
419
420
        return $metadata;
421
    }
422
423
    private function getOperatorFromFieldType($type)
424
    {
425
        $mapping = [
426
            'string' => 'like',
427
            'text' => 'like',
428
        ];
429
430
        if (key_exists($type, $mapping)) {
431
            return $mapping[$type];
432
        }
433
434
        return '=';
435
    }
436
437
    /**
438
     * @return bool
439
     */
440
    private function isExtraConfigurationEnabled(): bool
441
    {
442
        return $this->applicationConfiguration->getParameter('enable_extra_configuration');
443
    }
444
}
445