Completed
Pull Request — master (#90)
by Arnaud
05:26
created

Admin::generateMessageTranslationKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 1
crap 1
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 11
    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 11
        $this->name = $name;
123 11
        $this->entities = new ArrayCollection();
124 11
        $this->configuration = $configuration;
125 11
        $this->messageHandler = $messageHandler;
126 11
        $this->eventDispatcher = $eventDispatcher;
127 11
        $this->authorizationChecker = $authorizationChecker;
128 11
        $this->tokenStorage = $tokenStorage;
129 11
        $this->actions = $actions;
130 11
        $this->entityLoader = $entityLoader;
131 11
        $this->dataProvider = $entityLoader->getDataProvider();
132 11
    }
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 5
    public function handleRequest(Request $request, array $filters = [])
141
    {
142
        // set current action
143 5
        $this->currentAction = $this->getAction(
144 5
            $request->get('_route_params')[LAGAdminBundle::REQUEST_PARAMETER_ACTION]
145
        );
146
        
147
        // check if user is logged have required permissions to get current action
148 5
        $this->checkPermissions();
149
        
150
        // get the current action configuration bag
151
        $actionConfiguration = $this
152 2
            ->currentAction
153 2
            ->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 2
        if (true !== $this->currentAction->isLoadingRequired()) {
159 1
            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 1
        $loader = new LoadParameterExtractor($actionConfiguration);
164 1
        $loader->load($request);
165
    
166
        // load entities according to action and request
167
        $this
168 1
            ->entityLoader
169 1
            ->load(
170 1
                $loader->getCriteria(),
171 1
                $loader->getOrder(),
172 1
                $loader->getMaxPerPage(),
173 1
                $loader->getPage()
174
            )
175
        ;
176 1
    }
177
    
178
    /**
179
     * Check if user is allowed to be here.
180
     *
181
     * @throws LogicException|AccessDeniedException
182
     */
183 6
    public function checkPermissions()
184
    {
185
        $user = $this
186 6
            ->tokenStorage
187 6
            ->getToken()
188 6
            ->getUser()
189
        ;
190
        
191
        // must be authenticated to access to an Admin
192 6
        if (!($user instanceof UserInterface)) {
193 1
            throw new AccessDeniedException();
194
        }
195
        
196
        // the current Action has to be defined
197 5
        if (null === $this->currentAction) {
198 1
            throw new LogicException(
199 1
                '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 4
        if (!$this->authorizationChecker->isGranted($user->getRoles(), $user)) {
204 1
            throw new AccessDeniedException();
205
        }
206
        $permissions = $this
207 3
            ->currentAction
208 3
            ->getConfiguration()
209 3
            ->getParameter('permissions')
210
        ;
211
        
212
        // check if the User is granted according to Admin configuration
213 3
        if (!$this->authorizationChecker->isGranted($permissions, $user)) {
214 1
            throw new AccessDeniedException();
215
        }
216 2
    }
217
    
218
    /**
219
     * Create and return a new entity.
220
     *
221
     * @return object
222
     */
223 3
    public function create()
224
    {
225
        // create an entity from the data provider
226
        $entity = $this
227 3
            ->dataProvider
228 3
            ->create();
229
        
230
        // add it to the collection
231
        $this
232 3
            ->entities
233 3
            ->add($entity);
234
        
235 3
        return $entity;
236
    }
237
    
238
    /**
239
     * Save entity via admin manager.
240
     */
241 1
    public function save()
242
    {
243 1
        foreach ($this->entities as $entity) {
244
            $this
245 1
                ->dataProvider
246 1
                ->save($entity)
247
            ;
248
        }
249
        // inform the user that the entity is saved
250
        $this
251 1
            ->messageHandler
252 1
            ->handleSuccess($this->generateMessageTranslationKey('saved'))
253
        ;
254 1
    }
255
    
256
    /**
257
     * Remove an entity with data provider.
258
     */
259 1
    public function remove()
260
    {
261 1
        foreach ($this->entities as $entity) {
262
            $this
263 1
                ->dataProvider
264 1
                ->remove($entity);
265
        }
266
        // inform the user that the entity is removed
267
        $this
268 1
            ->messageHandler
269 1
            ->handleSuccess($this->generateMessageTranslationKey('deleted'))
270
        ;
271 1
    }
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 1
    public function generateRouteName($actionName)
304
    {
305
        $actions = $this
306 1
            ->configuration
307 1
            ->getParameter('actions')
308
        ;
309
        
310 1
        if (!array_key_exists($actionName, $actions)) {
311 1
            throw new Exception(
312 1
                sprintf('Invalid action name %s for admin %s (available action are: %s)',
313
                    $actionName,
314 1
                    $this->getName(),
315 1
                    implode(', ', $this->getActionNames()))
316
            );
317
        }
318
        // get routing name pattern
319
        $routingPattern = $this
320 1
            ->configuration
321 1
            ->getParameter('routing_name_pattern')
322
        ;
323
        
324
        // replace admin and action name in pattern
325 1
        $routeName = str_replace('{admin}', strtolower($this->name), $routingPattern);
326 1
        $routeName = str_replace('{action}', $actionName, $routeName);
327
        
328 1
        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 1
    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 1
            ->entityLoader
344 1
            ->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 1
        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 1
            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 1
            if (is_array($entities)) {
362 1
                $entities = new ArrayCollection($entities);
363
            }
364 1
            $this->entities = $entities;
365
        }
366 1
    }
367
    
368
    /**
369
     * Return loaded entities
370
     *
371
     * @return Collection
372
     */
373 1
    public function getEntities()
374
    {
375 1
        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 1
    public function getName()
407
    {
408 1
        return $this->name;
409
    }
410
    
411
    /**
412
     * @return ActionInterface[]
413
     */
414 5
    public function getActions()
415
    {
416 5
        return $this->actions;
417
    }
418
    
419
    /**
420
     * @return integer[]
421
     */
422 1
    public function getActionNames()
423
    {
424 1
        return array_keys($this->actions);
425
    }
426
    
427
    /**
428
     * @param $name
429
     * @return ActionInterface
430
     * @throws Exception
431
     */
432 5
    public function getAction($name)
433
    {
434 5
        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 5
        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 6
    public function addAction(ActionInterface $action)
461
    {
462 6
        $this->actions[$action->getName()] = $action;
463 6
    }
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 2
    protected function generateMessageTranslationKey($message)
539
    {
540 2
        return $this->getTranslationKey(
541 2
            $this->configuration->getParameter('translation_pattern'),
542
            $message,
543 2
            $this->name
544
        );
545
    }
546
}
547