Completed
Pull Request — dev (#9)
by Arnaud
02:46
created

Admin::load()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 5.0187

Importance

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