Completed
Pull Request — dev (#61)
by Arnaud
02:53
created

Admin::getAction()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 2
cts 2
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
crap 2
1
<?php
2
3
namespace LAG\AdminBundle\Admin;
4
5
use Doctrine\Common\Collections\Collection;
6
use Doctrine\ORM\EntityManagerInterface;
7
use LAG\AdminBundle\Action\ActionInterface;
8
use LAG\AdminBundle\Admin\Behaviors\AdminTrait;
9
use LAG\AdminBundle\Admin\Configuration\AdminConfiguration;
10
use Doctrine\Common\Collections\ArrayCollection;
11
use Exception;
12
use LAG\AdminBundle\DataProvider\DataProviderInterface;
13
use LAG\AdminBundle\Exception\AdminException;
14
use LAG\AdminBundle\Filter\PagerfantaFilter;
15
use LAG\AdminBundle\Filter\RequestFilter;
16
use LAG\AdminBundle\Filter\RequestFilterInterface;
17
use LAG\AdminBundle\Message\MessageHandlerInterface;
18
use LAG\AdminBundle\Pager\PagerFantaAdminAdapter;
19
use Pagerfanta\Pagerfanta;
20
use Symfony\Component\DependencyInjection\Container;
21
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
22
use Symfony\Component\HttpFoundation\ParameterBag;
23
use Symfony\Component\HttpFoundation\Request;
24
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
25
use Symfony\Component\Security\Core\Role\Role;
26
use Symfony\Component\Security\Core\User\UserInterface;
27
28
class Admin implements AdminInterface
29
{
30
    use AdminTrait;
31
32
    /**
33
     * Entities collection.
34
     *
35
     * @var ArrayCollection
36
     */
37
    protected $entities;
38
39
    /**
40
     * @var MessageHandlerInterface
41
     */
42
    protected $messageHandler;
43
44
    /**
45
     * @var EntityManagerInterface
46
     */
47
    protected $entityManager;
48
49
    /**
50
     * @var DataProviderInterface
51
     */
52
    protected $dataProvider;
53
54
    /**
55
     * Admin configuration object
56
     *
57
     * @var AdminConfiguration
58
     */
59
    protected $configuration;
60
61
    /**
62
     * Admin configured actions
63
     *
64
     * @var ActionInterface[]
65
     */
66
    protected $actions = [];
67
68
    /**
69
     * Admin current action. It will be set after calling the handleRequest()
70
     *
71
     * @var ActionInterface
72
     */
73
    protected $currentAction;
74
75
    /**
76
     * Admin name
77
     *
78
     * @var string
79
     */
80
    protected $name;
81
82
    /**
83
     * @var EventDispatcherInterface
84
     */
85
    protected $eventDispatcher;
86
87
    /**
88
     * @var RequestFilterInterface
89
     */
90
    protected $requestFilter;
91
92
    /**
93
     * Admin constructor.
94
     *
95 26
     * @param string $name
96
     * @param DataProviderInterface $dataProvider
97
     * @param AdminConfiguration $configuration
98
     * @param MessageHandlerInterface $messageHandler
99
     * @param EventDispatcherInterface $eventDispatcher
100
     * @param RequestFilterInterface $requestFilter
101
     */
102 26
    public function __construct(
103 26
        $name,
104 26
        DataProviderInterface $dataProvider,
105 26
        AdminConfiguration $configuration,
106 26
        MessageHandlerInterface $messageHandler,
107 26
        EventDispatcherInterface $eventDispatcher,
108 26
        RequestFilterInterface $requestFilter
109
    ) {
110
        $this->name = $name;
111
        $this->dataProvider = $dataProvider;
112
        $this->configuration = $configuration;
113
        $this->messageHandler = $messageHandler;
114
        $this->eventDispatcher = $eventDispatcher;
115
        $this->entities = new ArrayCollection();
116
        $this->requestFilter = $requestFilter;
117
    }
118 9
119
    /**
120
     * Load entities and set current action according to request.
121 9
     *
122
     * @param Request $request
123 9
     * @param null $user
124
     * @return void
125
     * @throws AdminException
126 9
     */
127 9
    public function handleRequest(Request $request, $user = null)
128
    {
129
        // set current action
130 9
        $this->currentAction = $this->getAction($request->get('_route_params')['_action']);
131 8
132 8
        // check if user is logged have required permissions to get current action
133 8
        $this->checkPermissions($user);
134
135 1
        $actionConfiguration = $this
136
            ->currentAction
137
            ->getConfiguration();
138
139 9
        // configure the request filter with the action and admin configured parameters
140 1
        $this
141
            ->requestFilter
142
            ->configure(
143
                $actionConfiguration->getParameter('criteria'),
144 9
                $actionConfiguration->getParameter('order'),
145 9
                $this->configuration->getParameter('max_per_page')
146 9
            );
147 9
148 9
        // filter the request with the configured criteria, order and max_per_page parameter
149 9
        $this
150 8
            ->requestFilter
151
            ->filter($request);
152
153
        // load entities according to action and request
154
        $this->load(
155
            $this->requestFilter->getCriteria(),
156
            $this->requestFilter->getOrder(),
157
            $this->requestFilter->getMaxPerPage(),
158 9
            $this->requestFilter->getCurrentPage()
159
        );
160 9
    }
161 9
162
    /**
163 1
     * Check if user is allowed to be here
164 1
     *
165
     * @param UserInterface|string $user
166 1
     * @throws Exception
167 1
     */
168 1
    public function checkPermissions($user)
169 1
    {
170
        if (!($user instanceof UserInterface)) {
171 1
            return;
172 1
        }
173
        if ($this->currentAction === null) {
174 1
            throw new Exception('Current action should be set before checking the permissions');
175
        }
176 1
        $roles = $user->getRoles();
177 1
        $actionName = $this
178 1
            ->getCurrentAction()
179 1
            ->getName();
180
181 1
        if (!$this->isActionGranted($actionName, $roles)) {
182
            $rolesStringArray = [];
183 1
184 1
            foreach ($roles as $role) {
185
186 1
                if ($role instanceof Role) {
187 1
                    $rolesStringArray[] = $role->getRole();
188
                } else {
189 1
                    $rolesStringArray[] = $role;
190
                }
191
            }
192
193
            $message = sprintf('User with roles %s not allowed for action "%s"',
194
                implode(', ', $rolesStringArray),
195
                $actionName
196 5
            );
197
            throw new NotFoundHttpException($message);
198
        }
199 5
    }
200
201 5
    /**
202
     * Create and return a new entity.
203
     *
204 5
     * @return object
205
     */
206 5
    public function create()
207
    {
208
        // create an entity from the data provider
209 5
        $entity = $this
210
            ->dataProvider
211 5
            ->create();
212
213 5
        // add it to the collection
214
        $this
215
            ->entities
216
            ->add($entity);
217
218
        return $entity;
219
    }
220
221 2
    /**
222
     * Save entity via admin manager. Error are catch, logged and a flash message is added to session
223
     *
224 2
     * @return bool true if the entity was saved without errors
225 2
     */
226 View Code Duplication
    public function save()
227 2
    {
228 1
        try {
229
            foreach ($this->entities as $entity) {
230 1
                $this
231
                    ->dataProvider
232 1
                    ->save($entity);
233 1
            }
234 2
            // inform the user that the entity is saved
235 1
            $this
236
                ->messageHandler
237 1
                ->handleSuccess($this->generateMessageTranslationKey('saved'));
238 1
            $success = true;
239 1
        } catch (Exception $e) {
240 1
            $this
241 1
                ->messageHandler
242
                ->handleError(
243 2
                    $this->generateMessageTranslationKey('lag.admin.saved_errors'),
244
                    "An error has occurred while saving an entity : {$e->getMessage()}, stackTrace: {$e->getTraceAsString()}"
245
                );
246
            $success = false;
247
        }
248
        return $success;
249
    }
250
251 2
    /**
252
     * Remove an entity with data provider
253
     *
254 2
     * @return bool true if the entity was saved without errors
255 2
     */
256 View Code Duplication
    public function remove()
257 2
    {
258 1
        try {
259
            foreach ($this->entities as $entity) {
260 1
                $this
261
                    ->dataProvider
262 1
                    ->remove($entity);
263 1
            }
264 2
            // inform the user that the entity is removed
265 1
            $this
266
                ->messageHandler
267 1
                ->handleSuccess($this->generateMessageTranslationKey('deleted'));
268 1
            $success = true;
269 1
        } catch (Exception $e) {
270 1
            $this
271 1
                ->messageHandler
272
                ->handleError(
273 2
                    $this->generateMessageTranslationKey('lag.admin.deleted_errors'),
274
                    "An error has occurred while deleting an entity : {$e->getMessage()}, stackTrace: {$e->getTraceAsString()} "
275
                );
276
            $success = false;
277
        }
278
        return $success;
279
    }
280
281
    /**
282
     * Generate a route for admin and action name (like lag.admin.my_admin)
283
     *
284
     * @param $actionName
285 15
     *
286
     * @return string
287 15
     *
288 2
     * @throws Exception
289 2
     */
290 2
    public function generateRouteName($actionName)
291 2
    {
292 2
        if (!array_key_exists($actionName, $this->getConfiguration()->getParameter('actions'))) {
293 2
            throw new Exception(
294
                sprintf('Invalid action name %s for admin %s (available action are: %s)',
295
                    $actionName,
296 14
                    $this->getName(),
297
                    implode(', ', $this->getActionNames()))
298 14
            );
299 14
        }
300
        // get routing name pattern
301 14
        $routingPattern = $this->getConfiguration()->getParameter('routing_name_pattern');
302
        // replace admin and action name in pattern
303
        $routeName = str_replace('{admin}', Container::underscore($this->getName()), $routingPattern);
304
        $routeName = str_replace('{action}', $actionName, $routeName);
305
306
        return $routeName;
307
    }
308
309
    /**
310
     * Load entities manually according to criteria.
311
     *
312
     * @param array $criteria
313 9
     * @param array $orderBy
314
     * @param int $limit
315 9
     * @param int $offset
316 9
     * @throws Exception
317 9
     */
318 9
    public function load(array $criteria, $orderBy = [], $limit = 25, $offset = 1)
319 9
    {
320 9
        $actionConfiguration = $this
321 9
            ->getCurrentAction()
322
            ->getConfiguration();
323 9
        $pager = $actionConfiguration->getParameter('pager');
324
        $requirePagination = $this
325 1
            ->getCurrentAction()
326
            ->isPaginationRequired();
327 1
328 1
        if ($pager == 'pagerfanta' && $requirePagination) {
329 1
            // adapter to pagerfanta
330
            $adapter = new PagerFantaAdminAdapter($this->dataProvider, $criteria, $orderBy);
331 1
            // create pager
332
            $this->pager = new Pagerfanta($adapter);
333 1
            $this->pager->setMaxPerPage($limit);
334 1
            $this->pager->setCurrentPage($offset);
335
336 8
            $entities = $this
337 7
                ->pager
338 7
                ->getCurrentPageResults();
339 8
        } else {
340
            // if the current action should retrieve only one entity, the offset should be zero
341 8
            if ($actionConfiguration->getParameter('load_strategy') !== AdminInterface::LOAD_STRATEGY_MULTIPLE) {
342
                $offset = 0;
343 9
            }
344 1
            $entities = $this
345
                ->dataProvider
346
                ->findBy($criteria, $orderBy, $limit, $offset);
347 8
        }
348 8
        if (!is_array($entities) && !($entities instanceof Collection)) {
349 8
            throw new Exception('The data provider should return either a collection or an array. Got '.gettype($entities).' instead');
350 8
        }
351 8
352
        if (is_array($entities)) {
353
            $entities = new ArrayCollection($entities);
354
        }
355
        $this->entities = $entities;
356
    }
357
358 2
    /**
359
     * Return loaded entities
360 2
     *
361
     * @return Collection
362
     */
363
    public function getEntities()
364
    {
365
        return $this->entities;
366
    }
367
368
    /**
369
     * Return entity for current admin. If entity does not exist, it throws an exception.
370 1
     *
371
     * @return mixed
372 1
     *
373 1
     * @throws Exception
374
     */
375 1
    public function getUniqueEntity()
376 1
    {
377
        if ($this->entities->count() == 0) {
378 1
            throw new Exception("Entity not found in admin \"{$this->getName()}\".");
379
        }
380
        if ($this->entities->count() > 1) {
381
            throw new Exception("Too much entities found in admin \"{$this->getName()}\".");
382
        }
383
        return $this->entities->first();
384
    }
385
386 19
    /**
387
     * Return admin name
388 19
     *
389
     * @return string
390
     */
391
    public function getName()
392
    {
393
        return $this->name;
394
    }
395
396
    /**
397
     * Return true if current action is granted for user.
398
     *
399 2
     * @param string $actionName Le plus grand de tous les héros
400
     * @param array $roles
401 2
     *
402
     * @return bool
403
     */
404 2
    public function isActionGranted($actionName, array $roles)
405 2
    {
406
        $isGranted = array_key_exists($actionName, $this->actions);
407 2
408
        // if action exists
409 2
        if ($isGranted) {
410
            $isGranted = false;
411 2
            /** @var ActionInterface $action */
412 2
            $action = $this->actions[$actionName];
413 2
            // checking roles permissions
414 2
            foreach ($roles as $role) {
415 2
416 2
                if ($role instanceof Role) {
417 2
                    $role = $role->getRole();
418 2
                }
419
                if (in_array($role, $action->getPermissions())) {
420 2
                    $isGranted = true;
421
                }
422
            }
423
        }
424
425
        return $isGranted;
426 10
    }
427
428 10
    /**
429
     * @return ActionInterface[]
430
     */
431
    public function getActions()
432
    {
433
        return $this->actions;
434 2
    }
435
436 2
    /**
437
     * @return integer[]
438
     */
439
    public function getActionNames()
440
    {
441
        return array_keys($this->actions);
442
    }
443
444 9
    /**
445
     * @param $name
446 9
     * @return ActionInterface
447 1
     * @throws Exception
448 1
     */
449 1
    public function getAction($name)
450
    {
451
        if (!array_key_exists($name, $this->getActions())) {
452 9
            throw new Exception(
453
                "Invalid action name \"{$name}\" for admin '{$this->getName()}'. Check your configuration"
454
            );
455
        }
456
457
        return $this->actions[$name];
458
    }
459
460
    /**
461 1
     * Return if an action with specified name exists form this admin.
462
     *
463 1
     * @param $name
464
     * @return bool
465
     */
466
    public function hasAction($name)
467
    {
468
        return array_key_exists($name, $this->actions);
469
    }
470 15
471
    /**
472 15
     * @param ActionInterface $action
473 15
     * @return void
474
     */
475
    public function addAction(ActionInterface $action)
476
    {
477
        $this->actions[$action->getName()] = $action;
478
    }
479
480
    /**
481 10
     * Return the current action or an exception if it is not set.
482
     *
483 10
     * @return ActionInterface
484
     * @throws Exception
485 1
     */
486
    public function getCurrentAction()
487 1
    {
488
        if ($this->currentAction === null) {
489
            // current action should be defined
490 9
            throw new Exception(
491
                'Current action is null. You should initialize it (with handleRequest method for example)'
492
            );
493
        }
494
495
        return $this->currentAction;
496
    }
497
498 1
    /**
499
     * Return if the current action has been initialized and set.
500 1
     *
501
     * @return boolean
502
     */
503
    public function isCurrentActionDefined()
504
    {
505
        return ($this->currentAction instanceof ActionInterface);
506
    }
507
508 19
    /**
509
     * Return admin configuration object.
510 19
     *
511
     * @return AdminConfiguration
512
     */
513
    public function getConfiguration()
514
    {
515
        return $this->configuration;
516
    }
517
518
    /**
519 5
     * Return a translation key for a message according to the Admin's translation pattern.
520
     *
521 5
     * @param string $message
522 5
     * @return string
523 5
     */
524 5
    protected function generateMessageTranslationKey($message)
525 5
    {
526
        return $this->getTranslationKey(
527
            $this->configuration->getParameter('translation_pattern'),
528
            $message,
529
            $this->name
530
        );
531
    }
532
}
533