Completed
Pull Request — master (#90)
by Arnaud
03:09
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
        // retrieve the criteria to find one or more entities (from the request for sorting, pagination... and from
162
        // the filter form
163 4
        $loader = new LoadParameterExtractor($actionConfiguration);
164 4
        $loader->load($request);
165
    
166
        // load entities according to action and request
167
        $this
168 4
            ->entityLoader
169 4
            ->load(
170 4
                $loader->getCriteria(),
171 4
                $loader->getOrder(),
172 4
                $loader->getMaxPerPage(),
173 4
                $loader->getPage()
174
            )
175
        ;
176 4
    }
177
    
178
    /**
179
     * Check if user is allowed to be here.
180
     *
181
     * @throws LogicException|AccessDeniedException
182
     */
183 24
    public function checkPermissions()
184
    {
185
        $user = $this
186 24
            ->tokenStorage
187 24
            ->getToken()
188 24
            ->getUser()
189
        ;
190
        
191
        // must be authenticated to access to an Admin
192 24
        if (!($user instanceof UserInterface)) {
193 4
            throw new AccessDeniedException();
194
        }
195
        
196
        // the current Action has to be defined
197 20
        if (null === $this->currentAction) {
198 4
            throw new LogicException(
199 4
                'Current action should be set before checking the permissions. Maybe you forget to call handleRequest()'
200
            );
201
        }
202
        // check if the current User is granted in Symfony security configuration
203 16
        if (!$this->authorizationChecker->isGranted($user->getRoles(), $user)) {
204 4
            throw new AccessDeniedException();
205
        }
206
        $permissions = $this
207 12
            ->currentAction
208 12
            ->getConfiguration()
209 12
            ->getParameter('permissions')
210
        ;
211
        
212
        // check if the User is granted according to Admin configuration
213 12
        if (!$this->authorizationChecker->isGranted($permissions, $user)) {
214 4
            throw new AccessDeniedException();
215
        }
216 8
    }
217
    
218
    /**
219
     * Create and return a new entity.
220
     *
221
     * @return object
222
     */
223 12
    public function create()
224
    {
225
        // create an entity from the data provider
226
        $entity = $this
227 12
            ->dataProvider
228 12
            ->create();
229
        
230
        // add it to the collection
231
        $this
232 12
            ->entities
233 12
            ->add($entity);
234
        
235 12
        return $entity;
236
    }
237
    
238
    /**
239
     * Save entity via admin manager.
240
     */
241 4
    public function save()
242
    {
243 4
        foreach ($this->entities as $entity) {
244
            $this
245 4
                ->dataProvider
246 4
                ->save($entity)
247
            ;
248
        }
249
        // inform the user that the entity is saved
250
        $this
251 4
            ->messageHandler
252 4
            ->handleSuccess($this->generateMessageTranslationKey('saved'))
253
        ;
254 4
    }
255
    
256
    /**
257
     * Remove an entity with data provider.
258
     */
259 4
    public function remove()
260
    {
261 4
        foreach ($this->entities as $entity) {
262
            $this
263 4
                ->dataProvider
264 4
                ->remove($entity);
265
        }
266
        // inform the user that the entity is removed
267
        $this
268 4
            ->messageHandler
269 4
            ->handleSuccess($this->generateMessageTranslationKey('deleted'))
270
        ;
271 4
    }
272
    
273
    /**
274
     * Return the number of entities managed by the Admin.
275
     *
276
     * @return int
277
     */
278
    public function count()
279
    {
280
        $count = $this
281
            ->dataProvider
282
            ->count()
283
        ;
284
    
285
        if (!is_integer($count)) {
286
            throw new LogicException(
287
                'The data provider should return an integer for the "count()" method, given : '.gettype($count)
288
            );
289
        }
290
    
291
        return $count;
292
    }
293
    
294
    /**
295
     * Generate a route for admin and action name (like lag.admin.my_admin)
296
     *
297
     * @param $actionName
298
     *
299
     * @return string
300
     *
301
     * @throws Exception
302
     */
303 4
    public function generateRouteName($actionName)
304
    {
305
        $actions = $this
306 4
            ->configuration
307 4
            ->getParameter('actions')
308
        ;
309
        
310 4
        if (!array_key_exists($actionName, $actions)) {
311 4
            throw new Exception(
312 4
                sprintf('Invalid action name %s for admin %s (available action are: %s)',
313
                    $actionName,
314 4
                    $this->getName(),
315 4
                    implode(', ', $this->getActionNames()))
316
            );
317
        }
318
        // get routing name pattern
319
        $routingPattern = $this
320 4
            ->configuration
321 4
            ->getParameter('routing_name_pattern')
322
        ;
323
        
324
        // replace admin and action name in pattern
325 4
        $routeName = str_replace('{admin}', strtolower($this->name), $routingPattern);
326 4
        $routeName = str_replace('{action}', $actionName, $routeName);
327
        
328 4
        return $routeName;
329
    }
330
    
331
    /**
332
     * Load entities according to the given criteria and the current action configuration.
333
     *
334
     * @param array $criteria
335
     * @param array $orderBy
336
     * @param int   $limit
337
     * @param int   $offset
338
     */
339 4
    public function load(array $criteria, array $orderBy = [], $limit = 25, $offset = 1)
340
    {
341
        // retrieve the data using the data provider via the entity loader
342
        $entities = $this
343 4
            ->entityLoader
344 4
            ->load($criteria, $orderBy, $limit, $offset)
345
        ;
346
    
347
        // either, we have an instance of Pagerfanta, either we should have an array or a collection
348 4
        if ($entities instanceof Pagerfanta) {
349
            // if the entities are inside a pager, we get the result and we set the pager for the view
350
            $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...
351
            $this->pager = $entities;
352
        } else {
353
            // the data provider should return an array or a collection of entities.
354 4
            if (!is_array($entities) && !$entities instanceof Collection) {
355
                throw new LogicException(
356
                    'The data provider should return either a collection or an array. Got '.gettype($entities).' instead'
357
                );
358
            }
359
    
360
            // if an array is provided, transform it to a collection to be more convenient
361 4
            if (is_array($entities)) {
362 4
                $entities = new ArrayCollection($entities);
363
            }
364 4
            $this->entities = $entities;
365
        }
366 4
    }
367
    
368
    /**
369
     * Return loaded entities
370
     *
371
     * @return Collection
372
     */
373 4
    public function getEntities()
374
    {
375 4
        return $this->entities;
376
    }
377
    
378
    /**
379
     * Return entity for current admin. If entity does not exist, it throws an exception.
380
     *
381
     * @return mixed
382
     *
383
     * @throws Exception
384
     */
385
    public function getUniqueEntity()
386
    {
387
        if ($this->entities->count() == 0) {
388
            throw new Exception('Entity not found in admin "'.$this->getName().'""');
389
        }
390
        
391
        if ($this->entities->count() > 1) {
392
            throw new Exception(
393
                'Too much entities found in admin '.$this->getName().' ('.$this->entities->count().' entities found, '
394
                .'expected one). Check the load strategy configuration'
395
            );
396
        }
397
        
398
        return $this->entities->first();
399
    }
400
    
401
    /**
402
     * Return admin name
403
     *
404
     * @return string
405
     */
406 4
    public function getName()
407
    {
408 4
        return $this->name;
409
    }
410
    
411
    /**
412
     * @return ActionInterface[]
413
     */
414 20
    public function getActions()
415
    {
416 20
        return $this->actions;
417
    }
418
    
419
    /**
420
     * @return integer[]
421
     */
422 4
    public function getActionNames()
423
    {
424 4
        return array_keys($this->actions);
425
    }
426
    
427
    /**
428
     * @param $name
429
     * @return ActionInterface
430
     * @throws Exception
431
     */
432 20
    public function getAction($name)
433
    {
434 20
        if (!array_key_exists($name, $this->getActions())) {
435
            throw new Exception(
436
                "Invalid action name \"{$name}\" for admin '{$this->getName()}'. Check your configuration"
437
            );
438
        }
439
        
440 20
        return $this->actions[$name];
441
    }
442
    
443
    /**
444
     * Return true if the Action with name $name exists in the Admin. If the method return true, it does not necessarily
445
     * means that the action is allowed in the current context.
446
     *
447
     * @param string $name
448
     *
449
     * @return boolean
450
     */
451
    public function hasAction($name)
452
    {
453
        return array_key_exists($name, $this->actions);
454
    }
455
    
456
    /**
457
     * @param ActionInterface $action
458
     * @return void
459
     */
460 24
    public function addAction(ActionInterface $action)
461
    {
462 24
        $this->actions[$action->getName()] = $action;
463 24
    }
464
    
465
    /**
466
     * Return the current action or an exception if it is not set.
467
     *
468
     * @return ActionInterface
469
     * @throws Exception
470
     */
471
    public function getCurrentAction()
472
    {
473
        if ($this->currentAction === null) {
474
            // current action should be defined
475
            throw new Exception(
476
                'Current action is null. You should initialize it (with handleRequest method for example)'
477
            );
478
        }
479
        
480
        return $this->currentAction;
481
    }
482
    
483
    /**
484
     * Return if the current action has been initialized and set.
485
     *
486
     * @return boolean
487
     */
488
    public function isCurrentActionDefined()
489
    {
490
        return ($this->currentAction instanceof ActionInterface);
491
    }
492
    
493
    /**
494
     * Return admin configuration object.
495
     *
496
     * @return AdminConfiguration
497
     */
498
    public function getConfiguration()
499
    {
500
        return $this->configuration;
501
    }
502
    
503
    /**
504
     * Return the filter form if it was initialized.
505
     *
506
     * @return FormInterface
507
     *
508
     * @throws Exception
509
     */
510
    public function getFilterForm()
511
    {
512
        if (null === $this->filterForm) {
513
            throw new Exception(
514
                'The filter form is null. Check you have configured filters. You should initialize the filter form 
515
                (with $admin->handleRequest() method for example)'
516
            );
517
        }
518
        
519
        return $this->filterForm;
520
    }
521
    
522
    /**
523
     * Return true if the filter form has been set.
524
     *
525
     * @return bool
526
     */
527
    public function hasFilterForm()
528
    {
529
        return null !== $this->filterForm;
530
    }
531
    
532
    /**
533
     * Return a translation key for a message according to the Admin's translation pattern.
534
     *
535
     * @param string $message
536
     * @return string
537
     */
538 8
    protected function generateMessageTranslationKey($message)
539
    {
540 8
        return $this->getTranslationKey(
541 8
            $this->configuration->getParameter('translation_pattern'),
542
            $message,
543 8
            $this->name
544
        );
545
    }
546
}
547