Completed
Pull Request — master (#90)
by Arnaud
17:45
created

Admin::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 12
cts 12
cp 1
rs 9.3142
c 0
b 0
f 0
cc 1
eloc 19
nc 1
nop 8
crap 1

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace LAG\AdminBundle\Admin;
4
5
use Doctrine\Common\Collections\Collection;
6
use LAG\AdminBundle\Action\ActionInterface;
7
use LAG\AdminBundle\Admin\Behaviors\AdminTrait;
8
use LAG\AdminBundle\Admin\Configuration\AdminConfiguration;
9
use Doctrine\Common\Collections\ArrayCollection;
10
use Exception;
11
use LAG\AdminBundle\Admin\Request\LoadParameterExtractor;
12
use LAG\AdminBundle\DataProvider\DataProviderInterface;
13
use LAG\AdminBundle\DataProvider\Loader\EntityLoaderInterface;
14
use LAG\AdminBundle\LAGAdminBundle;
15
use LAG\AdminBundle\Message\MessageHandlerInterface;
16
use LogicException;
17
use Pagerfanta\Pagerfanta;
18
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
19
use Symfony\Component\Form\Test\FormInterface;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
22
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
23
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
24
use Symfony\Component\Security\Core\User\UserInterface;
25
26
class Admin implements AdminInterface
27
{
28
    use AdminTrait;
29
    
30
    /**
31
     * Entities collection.
32
     *
33
     * @var ArrayCollection
34
     */
35
    protected $entities;
36
    
37
    /**
38
     * @var MessageHandlerInterface
39
     */
40
    protected $messageHandler;
41
    
42
    /**
43
     * @var DataProviderInterface
44
     */
45
    protected $dataProvider;
46
    
47
    /**
48
     * Admin configuration object
49
     *
50
     * @var AdminConfiguration
51
     */
52
    protected $configuration;
53
    
54
    /**
55
     * Admin configured actions
56
     *
57
     * @var ActionInterface[]
58
     */
59
    protected $actions = [];
60
    
61
    /**
62
     * Admin current action. It will be set after calling the handleRequest()
63
     *
64
     * @var ActionInterface
65
     */
66
    protected $currentAction;
67
    
68
    /**
69
     * Admin name
70
     *
71
     * @var string
72
     */
73
    protected $name;
74
    
75
    /**
76
     * @var EventDispatcherInterface
77
     */
78
    protected $eventDispatcher;
79
    
80
    /**
81
     * @var AuthorizationCheckerInterface
82
     */
83
    protected $authorizationChecker;
84
    
85
    /**
86
     * @var TokenStorageInterface
87
     */
88
    protected $tokenStorage;
89
    
90
    /**
91
     * @var FormInterface|null
92
     */
93
    protected $filterForm;
94
    
95
    /**
96
     * @var EntityLoaderInterface
97
     */
98
    protected $entityLoader;
99
    
100
    /**
101
     * Admin constructor.
102
     *
103
     * @param string                        $name
104
     * @param EntityLoaderInterface         $entityLoader
105
     * @param AdminConfiguration            $configuration
106
     * @param MessageHandlerInterface       $messageHandler
107
     * @param EventDispatcherInterface      $eventDispatcher
108
     * @param AuthorizationCheckerInterface $authorizationChecker
109
     * @param TokenStorageInterface         $tokenStorage
110
     * @param array                         $actions
111
     */
112 41
    public function __construct(
113
        $name,
114
        EntityLoaderInterface $entityLoader,
115
        AdminConfiguration $configuration,
116
        MessageHandlerInterface $messageHandler,
117
        EventDispatcherInterface $eventDispatcher,
118
        AuthorizationCheckerInterface $authorizationChecker,
119
        TokenStorageInterface $tokenStorage,
120
        $actions = []
121
    ) {
122 41
        $this->name = $name;
123 41
        $this->entities = new ArrayCollection();
124 41
        $this->configuration = $configuration;
125 41
        $this->messageHandler = $messageHandler;
126 41
        $this->eventDispatcher = $eventDispatcher;
127 41
        $this->authorizationChecker = $authorizationChecker;
128 41
        $this->tokenStorage = $tokenStorage;
129 41
        $this->actions = $actions;
130 41
        $this->entityLoader = $entityLoader;
131 41
        $this->dataProvider = $entityLoader->getDataProvider();
132 41
    }
133
    
134
    /**
135
     * Load entities and set current action according to request and the optional filters.
136
     *
137
     * @param Request $request
138
     * @param array   $filters
139
     */
140 20
    public function handleRequest(Request $request, array $filters = [])
141
    {
142
        // set current action
143 20
        $this->currentAction = $this->getAction(
144 20
            $request->get('_route_params')[LAGAdminBundle::REQUEST_PARAMETER_ACTION]
145
        );
146
        
147
        // check if user is logged have required permissions to get current action
148 20
        $this->checkPermissions();
149
        
150
        // get the current action configuration bag
151
        $actionConfiguration = $this
152 8
            ->currentAction
153 8
            ->getConfiguration()
154
        ;
155
    
156
        // if no loading is required, no more thing to do. Some actions do not require to load entities from
157
        // the DataProvider
158 8
        if (true !== $this->currentAction->isLoadingRequired()) {
159 4
            return;
160
        }
161
        
162
        // retrieve the criteria to find one or more entities (from the request for sorting, pagination... and from
163
        // the filter form
164 4
        $loader = new LoadParameterExtractor($actionConfiguration, $filters);
165 4
        $loader->load($request);
166
    
167
        // load entities according to action and request
168
        $this
169 4
            ->entityLoader
170 4
            ->configure($actionConfiguration)
171
        ;
172
        
173
        $this
174 4
            ->load(
175 4
                $loader->getCriteria(),
176 4
                $loader->getOrder(),
177 4
                $loader->getMaxPerPage(),
178 4
                $loader->getPage()
179
            )
180
        ;
181
    }
182
    
183
    /**
184
     * Check if user is allowed to be here.
185
     *
186
     * @throws LogicException|AccessDeniedException
187
     */
188 24
    public function checkPermissions()
189
    {
190
        $user = $this
191 24
            ->tokenStorage
192 24
            ->getToken()
193 24
            ->getUser()
194
        ;
195
        
196
        // must be authenticated to access to an Admin
197 24
        if (!($user instanceof UserInterface)) {
198 4
            throw new AccessDeniedException();
199
        }
200
        
201
        // the current Action has to be defined
202 20
        if (null === $this->currentAction) {
203 4
            throw new LogicException(
204 4
                'Current action should be set before checking the permissions. Maybe you forget to call handleRequest()'
205
            );
206
        }
207
        // check if the current User is granted in Symfony security configuration
208 16
        if (!$this->authorizationChecker->isGranted($user->getRoles(), $user)) {
209 4
            throw new AccessDeniedException();
210
        }
211
        $permissions = $this
212 12
            ->currentAction
213 12
            ->getConfiguration()
214 12
            ->getParameter('permissions')
215
        ;
216
        
217
        // check if the User is granted according to Admin configuration
218 12
        if (!$this->authorizationChecker->isGranted($permissions, $user)) {
219 4
            throw new AccessDeniedException();
220
        }
221 8
    }
222
    
223
    /**
224
     * Create and return a new entity.
225
     *
226
     * @return object
227
     */
228 12
    public function create()
229
    {
230
        // create an entity from the data provider
231
        $entity = $this
232 12
            ->dataProvider
233 12
            ->create();
234
        
235
        // add it to the collection
236
        $this
237 12
            ->entities
238 12
            ->add($entity);
239
        
240 12
        return $entity;
241
    }
242
    
243
    /**
244
     * Save entity via admin manager.
245
     */
246 4
    public function save()
247
    {
248 4
        foreach ($this->entities as $entity) {
249
            $this
250 4
                ->dataProvider
251 4
                ->save($entity)
252
            ;
253
        }
254
        // inform the user that the entity is saved
255
        $this
256 4
            ->messageHandler
257 4
            ->handleSuccess($this->generateMessageTranslationKey('saved'))
258
        ;
259 4
    }
260
    
261
    /**
262
     * Remove an entity with data provider.
263
     */
264 4
    public function remove()
265
    {
266 4
        foreach ($this->entities as $entity) {
267
            $this
268 4
                ->dataProvider
269 4
                ->remove($entity);
270
        }
271
        // inform the user that the entity is removed
272
        $this
273 4
            ->messageHandler
274 4
            ->handleSuccess($this->generateMessageTranslationKey('deleted'))
275
        ;
276 4
    }
277
    
278
    /**
279
     * Return the number of entities managed by the Admin.
280
     *
281
     * @return int
282
     */
283
    public function count()
284
    {
285
        $count = $this
286
            ->dataProvider
287
            ->count()
288
        ;
289
    
290
        if (!is_integer($count)) {
291
            throw new LogicException(
292
                'The data provider should return an integer for the "count()" method, given : '.gettype($count)
293
            );
294
        }
295
    
296
        return $count;
297
    }
298
    
299
    /**
300
     * Generate a route for admin and action name (like lag.admin.my_admin)
301
     *
302
     * @param $actionName
303
     *
304
     * @return string
305
     *
306
     * @throws Exception
307
     */
308 4
    public function generateRouteName($actionName)
309
    {
310
        $actions = $this
311 4
            ->configuration
312 4
            ->getParameter('actions')
313
        ;
314
        
315 4
        if (!array_key_exists($actionName, $actions)) {
316 4
            throw new Exception(
317 4
                sprintf('Invalid action name %s for admin %s (available action are: %s)',
318 4
                    $actionName,
319 4
                    $this->getName(),
320 4
                    implode(', ', $this->getActionNames()))
321
            );
322
        }
323
        // get routing name pattern
324
        $routingPattern = $this
325 4
            ->configuration
326 4
            ->getParameter('routing_name_pattern')
327
        ;
328
        
329
        // replace admin and action name in pattern
330 4
        $routeName = str_replace('{admin}', strtolower($this->name), $routingPattern);
331 4
        $routeName = str_replace('{action}', $actionName, $routeName);
332
        
333 4
        return $routeName;
334
    }
335
    
336
    /**
337
     * Load entities according to the given criteria and the current action configuration.
338
     *
339
     * @param array $criteria
340
     * @param array $orderBy
341
     * @param int   $limit
342
     * @param int   $offset
343
     */
344 8
    public function load(array $criteria, array $orderBy = [], $limit = 25, $offset = 1)
345
    {
346
        // retrieve the data using the data provider via the entity loader
347
        $entities = $this
348 8
            ->entityLoader
349 8
            ->load($criteria, $orderBy, $limit, $offset)
350
        ;
351
    
352
        // either, we have an instance of Pagerfanta, either we should have an array or a collection
353 8
        if ($entities instanceof Pagerfanta) {
354
            // if the entities are inside a pager, we get the result and we set the pager for the view
355
            $this->entities = $entities->getCurrentPageResults();
0 ignored issues
show
Documentation Bug introduced by
It seems like $entities->getCurrentPageResults() of type array or object<Traversable> is incompatible with the declared type object<Doctrine\Common\C...ctions\ArrayCollection> of property $entities.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
356
            $this->pager = $entities;
357
        } else {
358
            // the data provider should return an array or a collection of entities.
359 8
            if (!is_array($entities) && !$entities instanceof Collection) {
360 4
                throw new LogicException(
361 4
                    'The data provider should return either a collection or an array. Got '.gettype($entities).' instead'
362
                );
363
            }
364
    
365
            // if an array is provided, transform it to a collection to be more convenient
366 4
            if (is_array($entities)) {
367 4
                $entities = new ArrayCollection($entities);
368
            }
369 4
            $this->entities = $entities;
370
        }
371 4
    }
372
    
373
    /**
374
     * Return loaded entities
375
     *
376
     * @return Collection
377
     */
378 4
    public function getEntities()
379
    {
380 4
        return $this->entities;
381
    }
382
    
383
    /**
384
     * Return entity for current admin. If entity does not exist, it throws an exception.
385
     *
386
     * @return mixed
387
     *
388
     * @throws Exception
389
     */
390
    public function getUniqueEntity()
391
    {
392
        if ($this->entities->count() == 0) {
393
            throw new Exception('Entity not found in admin "'.$this->getName().'""');
394
        }
395
        
396
        if ($this->entities->count() > 1) {
397
            throw new Exception(
398
                'Too much entities found in admin '.$this->getName().' ('.$this->entities->count().' entities found, '
399
                .'expected one). Check the load strategy configuration'
400
            );
401
        }
402
        
403
        return $this->entities->first();
404
    }
405
    
406
    /**
407
     * Return admin name
408
     *
409
     * @return string
410
     */
411 4
    public function getName()
412
    {
413 4
        return $this->name;
414
    }
415
    
416
    /**
417
     * @return ActionInterface[]
418
     */
419 20
    public function getActions()
420
    {
421 20
        return $this->actions;
422
    }
423
    
424
    /**
425
     * @return integer[]
426
     */
427 4
    public function getActionNames()
428
    {
429 4
        return array_keys($this->actions);
430
    }
431
    
432
    /**
433
     * @param $name
434
     * @return ActionInterface
435
     * @throws Exception
436
     */
437 20
    public function getAction($name)
438
    {
439 20
        if (!array_key_exists($name, $this->getActions())) {
440
            throw new Exception(
441
                "Invalid action name \"{$name}\" for admin '{$this->getName()}'. Check your configuration"
442
            );
443
        }
444
        
445 20
        return $this->actions[$name];
446
    }
447
    
448
    /**
449
     * Return true if the Action with name $name exists in the Admin. If the method return true, it does not necessarily
450
     * means that the action is allowed in the current context.
451
     *
452
     * @param string $name
453
     *
454
     * @return boolean
455
     */
456
    public function hasAction($name)
457
    {
458
        return array_key_exists($name, $this->actions);
459
    }
460
    
461
    /**
462
     * @param ActionInterface $action
463
     * @return void
464
     */
465 24
    public function addAction(ActionInterface $action)
466
    {
467 24
        $this->actions[$action->getName()] = $action;
468 24
    }
469
    
470
    /**
471
     * Return the current action or an exception if it is not set.
472
     *
473
     * @return ActionInterface
474
     * @throws Exception
475
     */
476
    public function getCurrentAction()
477
    {
478
        if ($this->currentAction === null) {
479
            // current action should be defined
480
            throw new Exception(
481
                'Current action is null. You should initialize it (with handleRequest method for example)'
482
            );
483
        }
484
        
485
        return $this->currentAction;
486
    }
487
    
488
    /**
489
     * Return if the current action has been initialized and set.
490
     *
491
     * @return boolean
492
     */
493
    public function isCurrentActionDefined()
494
    {
495
        return ($this->currentAction instanceof ActionInterface);
496
    }
497
    
498
    /**
499
     * Return admin configuration object.
500
     *
501
     * @return AdminConfiguration
502
     */
503
    public function getConfiguration()
504
    {
505
        return $this->configuration;
506
    }
507
    
508
    /**
509
     * Return the filter form if it was initialized.
510
     *
511
     * @return FormInterface
512
     *
513
     * @throws Exception
514
     */
515
    public function getFilterForm()
516
    {
517
        if (null === $this->filterForm) {
518
            throw new Exception(
519
                'The filter form is null. Check you have configured filters. You should initialize the filter form 
520
                (with $admin->handleRequest() method for example)'
521
            );
522
        }
523
        
524
        return $this->filterForm;
525
    }
526
    
527
    /**
528
     * Return true if the filter form has been set.
529
     *
530
     * @return bool
531
     */
532
    public function hasFilterForm()
533
    {
534
        return null !== $this->filterForm;
535
    }
536
    
537
    /**
538
     * Return a translation key for a message according to the Admin's translation pattern.
539
     *
540
     * @param string $message
541
     * @return string
542
     */
543 8
    protected function generateMessageTranslationKey($message)
544
    {
545 8
        return $this->getTranslationKey(
546 8
            $this->configuration->getParameter('translation_pattern'),
547 8
            $message,
548 8
            $this->name
549
        );
550
    }
551
}
552