Completed
Pull Request — master (#71)
by Arnaud
02:47
created

ActionConfiguration::getOrderNormalizer()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 5.3906

Importance

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