Passed
Pull Request — master (#123)
by Arnaud
03:13
created

AdminSubscriber   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 300
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
eloc 125
c 3
b 0
f 1
dl 0
loc 300
rs 10
wmc 23

8 Methods

Rating   Name   Duplication   Size   Complexity  
A createView() 0 45 5
A saveEntity() 0 19 1
A getSubscribedEvents() 0 7 1
B loadEntities() 0 33 7
A translateMessage() 0 6 1
A handleRequest() 0 11 2
A getRedirectionUrl() 0 51 5
A __construct() 0 16 1
1
<?php
2
3
namespace LAG\AdminBundle\Event\Subscriber;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use LAG\AdminBundle\Admin\ActionInterface;
7
use LAG\AdminBundle\Admin\AdminInterface;
8
use LAG\AdminBundle\Configuration\AdminConfiguration;
9
use LAG\AdminBundle\Event\Events\AdminEvent;
10
use LAG\AdminBundle\Event\Events;
11
use LAG\AdminBundle\Event\Events\EntityEvent;
12
use LAG\AdminBundle\Event\Events\MenuEvent;
13
use LAG\AdminBundle\Event\Events\ViewEvent;
14
use LAG\AdminBundle\Exception\Exception;
15
use LAG\AdminBundle\Factory\ActionFactory;
16
use LAG\AdminBundle\Factory\DataProviderFactory;
17
use LAG\AdminBundle\Factory\ViewFactory;
18
use LAG\AdminBundle\LAGAdminBundle;
19
use LAG\AdminBundle\Routing\RoutingLoader;
20
use LAG\AdminBundle\Utils\TranslationUtils;
21
use LAG\AdminBundle\View\RedirectView;
22
use LAG\AdminBundle\View\ViewHelper;
23
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
24
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
25
use Symfony\Component\HttpFoundation\Request;
26
use Symfony\Component\HttpFoundation\Session\Session;
27
use Symfony\Component\HttpFoundation\Session\SessionInterface;
28
use Symfony\Component\Routing\RouterInterface;
29
use Symfony\Contracts\Translation\TranslatorInterface;
30
31
class AdminSubscriber implements EventSubscriberInterface
32
{
33
    /**
34
     * @var ActionFactory
35
     */
36
    private $actionFactory;
37
38
    /**
39
     * @var ViewFactory
40
     */
41
    private $viewFactory;
42
43
    /**
44
     * @var EventDispatcherInterface
45
     */
46
    private $eventDispatcher;
47
48
    /**
49
     * @var DataProviderFactory
50
     */
51
    private $dataProviderFactory;
52
53
    /**
54
     * @var Session|SessionInterface
55
     */
56
    private $session;
57
58
    /**
59
     * @var TranslatorInterface
60
     */
61
    private $translator;
62
63
    /**
64
     * @var RouterInterface
65
     */
66
    private $router;
67
68
    /**
69
     * @return array
70
     */
71
    public static function getSubscribedEvents()
72
    {
73
        return [
74
            Events::ADMIN_HANDLE_REQUEST => 'handleRequest',
75
            Events::ADMIN_VIEW => 'createView',
76
            Events::ENTITY_LOAD => 'loadEntities',
77
            Events::ENTITY_SAVE => 'saveEntity',
78
        ];
79
    }
80
81
    /**
82
     * AdminSubscriber constructor.
83
     *
84
     * @param ActionFactory            $actionFactory
85
     * @param ViewFactory              $viewFactory
86
     * @param DataProviderFactory      $dataProviderFactory
87
     * @param EventDispatcherInterface $eventDispatcher
88
     * @param SessionInterface         $session
89
     * @param TranslatorInterface      $translator
90
     * @param RouterInterface          $router
91
     */
92
    public function __construct(
93
        ActionFactory $actionFactory,
94
        ViewFactory $viewFactory,
95
        DataProviderFactory $dataProviderFactory,
96
        EventDispatcherInterface $eventDispatcher,
97
        SessionInterface $session,
98
        TranslatorInterface $translator,
99
        RouterInterface $router
100
    ) {
101
        $this->actionFactory = $actionFactory;
102
        $this->viewFactory = $viewFactory;
103
        $this->eventDispatcher = $eventDispatcher;
104
        $this->dataProviderFactory = $dataProviderFactory;
105
        $this->session = $session;
106
        $this->translator = $translator;
107
        $this->router = $router;
108
    }
109
110
    /**
111
     * Define the current action according to the routing configuration.
112
     *
113
     * @param AdminEvent $event
114
     *
115
     * @throws Exception
116
     */
117
    public function handleRequest(AdminEvent $event)
118
    {
119
        $actionName = $event->getRequest()->get('_action');
120
121
        if (null === $actionName) {
122
            throw new Exception('The _action was not found in the request');
123
        }
124
        $admin = $event->getAdmin();
125
        $action = $this->actionFactory->create($actionName, $admin->getName(), $admin->getConfiguration());
126
127
        $event->setAction($action);
128
    }
129
130
    /**
131
     * Create a view using the view factory.
132
     *
133
     * @param ViewEvent $event
134
     */
135
    public function createView(ViewEvent $event)
136
    {
137
        $admin = $event->getAdmin();
138
        $action = $admin->getAction();
139
        $menuEvent = new MenuEvent($admin->getAction()->getConfiguration()->getParameter('menus'));
140
        $this->eventDispatcher->dispatch(Events::MENU, $menuEvent);
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with $menuEvent. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

140
        $this->eventDispatcher->/** @scrutinizer ignore-call */ 
141
                                dispatch(Events::MENU, $menuEvent);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
LAG\AdminBundle\Event\Events::MENU of type string is incompatible with the type object expected by parameter $event of Symfony\Contracts\EventD...erInterface::dispatch(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

140
        $this->eventDispatcher->dispatch(/** @scrutinizer ignore-type */ Events::MENU, $menuEvent);
Loading history...
141
        $formName = '';
142
143
        // The form name is different according to the current action
144
        if ('edit' === $action->getName()) {
145
            $formName = 'entity';
146
        } elseif ('create' === $action->getName()) {
147
            $formName = 'entity';
148
        } elseif ('delete' === $action->getName()) {
149
            $formName = 'delete';
150
        }
151
        $viewHelper = new ViewHelper();
152
153
        if ($viewHelper->shouldRedirect($event->getRequest(), $admin->getConfiguration(), $admin, $action, $formName)) {
154
            $url = $this->getRedirectionUrl(
155
                $admin,
156
                $action,
157
                $admin->getConfiguration(),
158
                $event->getRequest()
159
            );
160
161
            $view = new RedirectView(
162
                $action->getName(),
163
                $admin->getName(),
164
                $action->getConfiguration(),
165
                $admin->getConfiguration()
166
            );
167
            $view->setUrl($url);
168
        } else {
169
            $view = $this->viewFactory->create(
170
                $event->getRequest(),
171
                $action->getName(),
172
                $admin->getName(),
173
                $admin->getConfiguration(),
174
                $action->getConfiguration(),
175
                $admin->getEntities(),
176
                $admin->getForms()
177
            );
178
        }
179
        $event->setView($view);
180
    }
181
182
    /**
183
     * Load entities into the event data to pass them to the Admin.
184
     *
185
     * @param EntityEvent $event
186
     *
187
     * @throws Exception
188
     */
189
    public function loadEntities(EntityEvent $event)
190
    {
191
        $admin = $event->getAdmin();
192
        $configuration = $admin->getConfiguration();
193
        $actionConfiguration = $admin->getAction()->getConfiguration();
194
195
        $dataProvider = $this->dataProviderFactory->get($configuration->getParameter('data_provider'));
196
        $strategy = $actionConfiguration->getParameter('load_strategy');
197
        $class = $configuration->getParameter('entity');
198
199
        if (LAGAdminBundle::LOAD_STRATEGY_NONE === $strategy) {
200
            return;
201
        } elseif (LAGAdminBundle::LOAD_STRATEGY_MULTIPLE === $strategy) {
202
            $entities = $dataProvider->getCollection($admin, $event->getFilters());
203
            $event->setEntities($entities);
204
        } elseif (LAGAdminBundle::LOAD_STRATEGY_UNIQUE === $strategy) {
205
            $requirements = $actionConfiguration->getParameter('route_requirements');
206
            $identifier = null;
207
208
            foreach ($requirements as $name => $requirement) {
209
                if (null !== $event->getRequest()->get($name)) {
210
                    $identifier = $event->getRequest()->get($name);
211
                    break;
212
                }
213
            }
214
215
            if (null === $identifier) {
216
                throw new Exception('Unable to find a identifier for the class "'.$class.'"');
217
            }
218
            $entity = $dataProvider->get($admin, $identifier);
219
220
            $event->setEntities(new ArrayCollection([
221
                $entity,
222
            ]));
223
        }
224
    }
225
226
    /**
227
     * Save an entity.
228
     *
229
     * @param EntityEvent $event
230
     */
231
    public function saveEntity(EntityEvent $event)
232
    {
233
        $admin = $event->getAdmin();
234
        $configuration = $admin->getConfiguration();
235
236
        // Save the entity changes using the configured data provider
237
        $dataProvider = $this
238
            ->dataProviderFactory
239
            ->get($configuration->getParameter('data_provider'))
240
        ;
241
        $dataProvider->save($admin);
242
243
        // Inform the user that the save is successful
244
        $message = $this->translateMessage('saved', $admin->getName(), $configuration);
245
246
        $this
247
            ->session
248
            ->getFlashBag()
249
            ->add('success', $message)
250
        ;
251
    }
252
253
    /**
254
     * @param string             $message
255
     * @param string             $adminName
256
     * @param AdminConfiguration $configuration
257
     *
258
     * @return string
259
     */
260
    private function translateMessage(string $message, string $adminName, AdminConfiguration $configuration): string
261
    {
262
        $pattern = $configuration->getParameter('translation_pattern');
263
        $message = TranslationUtils::getTranslationKey($pattern, $adminName, $message);
264
265
        return $this->translator->trans($message);
266
    }
267
268
    /**
269
     * Return the url where the user should be redirected to. An exception will be thrown if no url can be generated.
270
     *
271
     * @param AdminInterface     $admin
272
     * @param ActionInterface    $action
273
     * @param AdminConfiguration $configuration
274
     * @param Request            $request
275
     *
276
     * @return string
277
     *
278
     * @throws Exception
279
     */
280
    private function getRedirectionUrl(
281
        AdminInterface $admin,
282
        ActionInterface $action,
283
        AdminConfiguration $configuration,
284
        Request $request
285
    ): string {
286
        if ('edit' === $action->getName()) {
287
            $routeName = RoutingLoader::generateRouteName(
288
                $admin->getName(),
289
                'list',
290
                $configuration->get('routing_name_pattern')
291
            );
292
            $url = $this->router->generate($routeName);
293
294
            return $url;
295
        }
296
297
        // When the create form is submitted, the user should be redirected to the edit action after saving the form
298
        // data
299
        if ('create' === $action->getName()) {
300
            $targetAction = 'list';
301
            $routeParameters = [];
302
303
            if (!$request->get('submit_and_redirect')) {
304
                $targetAction = 'edit';
305
                $routeParameters = [
306
                    'id' => $admin->getEntities()->first()->getId(),
307
                ];
308
            }
309
            $routeName = RoutingLoader::generateRouteName(
310
                $admin->getName(),
311
                $targetAction,
312
                $configuration->get('routing_name_pattern')
313
            );
314
            $url = $this->router->generate($routeName, $routeParameters);
315
316
            return $url;
317
        }
318
319
        if ('delete' === $action->getName()) {
320
            $routeName = RoutingLoader::generateRouteName(
321
                $admin->getName(),
322
                'list',
323
                $configuration->get('routing_name_pattern')
324
            );
325
            $url = $this->router->generate($routeName);
326
327
            return $url;
328
        }
329
330
        throw new Exception('Unable to generate an redirection url for the current action');
331
    }
332
}
333