Completed
Push — dev ( dcd39b...53a432 )
by Arnaud
09:34
created

AdminFactory::getAdmins()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 5
Bugs 1 Features 0
Metric Value
c 5
b 1
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace LAG\AdminBundle\Admin\Factory;
4
5
use Doctrine\ORM\EntityManager;
6
use LAG\AdminBundle\Admin\Admin;
7
use LAG\AdminBundle\Admin\AdminInterface;
8
use LAG\AdminBundle\Admin\Configuration\AdminConfiguration;
9
use LAG\AdminBundle\Admin\Configuration\ApplicationConfiguration;
10
use LAG\AdminBundle\DataProvider\DataProvider;
11
use LAG\AdminBundle\DataProvider\DataProviderInterface;
12
use LAG\AdminBundle\Event\AdminEvent;
13
use LAG\AdminBundle\Event\AdminFactoryEvent;
14
use Exception;
15
use LAG\AdminBundle\Message\MessageHandlerInterface;
16
use LAG\DoctrineRepositoryBundle\Repository\RepositoryInterface;
17
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
18
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
19
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\OptionsResolver\OptionsResolver;
22
23
/**
24
 * AdminFactory.
25
 *
26
 * Create admin from configuration
27
 */
28
class AdminFactory
29
{
30
    /**
31
     * @var array
32
     */
33
    protected $admins = [];
34
35
    /**
36
     * @var bool
37
     */
38
    protected $isInit = false;
39
40
    /**
41
     * @var EventDispatcherInterface
42
     */
43
    protected $eventDispatcher;
44
45
    /**
46
     * @var EntityManager
47
     */
48
    protected $entityManager;
49
50
    /**
51
     * @var ApplicationConfiguration
52
     */
53
    protected $application;
54
55
    /**
56
     * User custom data provider, indexed by service id
57
     *
58
     * @var ParameterBagInterface
59
     */
60
    protected $dataProviders;
61
62
    /**
63
     * @var array
64
     */
65
    protected $adminConfiguration;
66
67
    /**
68
     * @var ActionFactory
69
     */
70
    protected $actionFactory;
71
72
    /**
73
     * @var MessageHandlerInterface
74
     */
75
    protected $messageHandler;
76
77
    /**
78
     * AdminFactory constructor.
79
     *
80
     * @param EventDispatcherInterface $eventDispatcher
81
     * @param EntityManager $entityManager
82
     * @param ApplicationConfiguration $application
83
     * @param array $adminConfiguration
84
     * @param ActionFactory $actionFactory
85
     * @param MessageHandlerInterface $messageHandler
86
     */
87 6
    public function __construct(
88
        EventDispatcherInterface $eventDispatcher,
89
        EntityManager $entityManager,
1 ignored issue
show
Bug introduced by
You have injected the EntityManager via parameter $entityManager. This is generally not recommended as it might get closed and become unusable. Instead, it is recommended to inject the ManagerRegistry and retrieve the EntityManager via getManager() each time you need it.

The EntityManager might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:

function someFunction(ManagerRegistry $registry) {
    $em = $registry->getManager();
    $em->getConnection()->beginTransaction();
    try {
        // Do something.
        $em->getConnection()->commit();
    } catch (\Exception $ex) {
        $em->getConnection()->rollback();
        $em->close();

        throw $ex;
    }
}

If that code throws an exception and the EntityManager is closed. Any other code which depends on the same instance of the EntityManager during this request will fail.

On the other hand, if you instead inject the ManagerRegistry, the getManager() method guarantees that you will always get a usable manager instance.

Loading history...
90
        ApplicationConfiguration $application,
91
        array $adminConfiguration,
92
        ActionFactory $actionFactory,
93
        MessageHandlerInterface $messageHandler
94
    ) {
95 6
        $this->eventDispatcher = $eventDispatcher;
96 6
        $this->entityManager = $entityManager;
97 6
        $this->application = $application;
98 6
        $this->adminConfiguration = $adminConfiguration;
99 6
        $this->actionFactory = $actionFactory;
100 6
        $this->messageHandler = $messageHandler;
101 6
        $this->dataProviders = new ParameterBag();
102 6
    }
103
104
    /**
105
     * Create admins from configuration and load them into the pool. Dispatch ADMIN_CREATE event.
106
     */
107 4
    public function init()
108
    {
109 4
        if ($this->isInit) {
110
            return;
111
        }
112 4
        $event = new AdminFactoryEvent();
113 4
        $event->setAdminsConfiguration($this->adminConfiguration);
114 4
        $this->eventDispatcher->dispatch(AdminFactoryEvent::ADMIN_CREATION, $event);
115 4
        $this->adminConfiguration = $event->getAdminsConfiguration();
116
117 4
        foreach ($this->adminConfiguration as $name => $configuration) {
118 4
            $event = new AdminEvent();
119 4
            $event->setConfiguration($configuration);
120 4
            $this->eventDispatcher->dispatch(AdminEvent::ADMIN_CREATE, $event);
121 4
            $this->admins[$name] = $this->create($name, $event->getConfiguration());
122 4
        }
123 4
        $this->isInit = true;
124 4
    }
125
126
    /**
127
     * Create an Admin from configuration values. It will be added to AdminFactory admin's list.
128
     *
129
     * @param string $name
130
     * @param array $configuration
131
     * @return Admin
132
     * @throws Exception
133
     */
134 5
    public function create($name, array $configuration)
135
    {
136
        // resolve admin configuration
137 5
        $configuration = $this->resolveConfiguration($configuration);
138
        // retrieve metadata from entity manager
139 5
        $configuration['metadata'] = $this
140
            ->entityManager
141 5
            ->getClassMetadata($configuration['entity']);
142
        // create AdminConfiguration object
143 5
        $adminConfiguration = new AdminConfiguration($configuration);
144
145
        // retrieve a data provider
146 5
        $dataProvider = $this->getDataProvider(
147 5
            $adminConfiguration->getEntityName(),
148 5
            $adminConfiguration->getDataProvider()
149 5
        );
150
151
        // create Admin object
152 5
        $admin = new Admin(
153 5
            $name,
154 5
            $dataProvider,
155 5
            $adminConfiguration,
156 5
            $this->messageHandler
157 5
        );
158
        // adding actions
159 5
        foreach ($adminConfiguration->getActions() as $actionName => $actionConfiguration) {
160
            // dispatching action create event for dynamic action creation
161 5
            $event = new AdminEvent();
162 5
            $event->setConfiguration($actionConfiguration);
163 5
            $event->setAdmin($admin);
164 5
            $event->setActionName($actionName);
165 5
            $this->eventDispatcher->dispatch(AdminEvent::ACTION_CREATE, $event);
166
            // creating action from configuration
167 5
            $action = $this
168
                ->actionFactory
169 5
                ->create($actionName, $event->getConfiguration(), $admin);
170
            // adding action to admin
171 5
            $admin->addAction($action);
172 5
        }
173 5
        return $admin;
174
    }
175
176
    /**
177
     * Return an admin from a Symfony request.
178
     *
179
     * @param Request $request
180
     * @return AdminInterface
181
     * @throws Exception
182
     */
183 1
    public function getAdminFromRequest(Request $request)
184
    {
185 1
        $routeParameters = $request->get('_route_params');
186
187 1
        if (!$routeParameters) {
188
            throw new Exception('Cannot find admin _route_params parameters for request');
189
        }
190 1
        if (!array_key_exists('_admin', $routeParameters)) {
191
            throw new Exception('Cannot find admin from request. "_admin" route parameter is missing');
192
        }
193 1
        if (!array_key_exists('_action', $routeParameters)) {
194
            throw new Exception('Cannot find admin action from request. "_action" route parameter is missing');
195
        }
196 1
        $admin = $this->getAdmin($routeParameters['_admin']);
197
198 1
        return $admin;
199
    }
200
201
    /**
202
     * Return a admin by its name.
203
     *
204
     * @param $name
205
     * @return Admin
206
     * @throws Exception
207
     */
208 5
    public function getAdmin($name)
209
    {
210 5
        if (!array_key_exists($name, $this->admins)) {
211 3
            throw new Exception(sprintf('Admin with name "%s" not found. Check your admin configuration', $name));
212
        }
213
214 3
        return $this->admins[$name];
215
    }
216
217
    /**
218
     * Return all admins.
219
     *
220
     * @return Admin[]
221
     */
222 1
    public function getAdmins()
223
    {
224 1
        return $this->admins;
225
    }
226
227
    /**
228
     * Add user custom repositories (called in the repository compiler pass), to avoid injecting the service container
229
     *
230
     * @param string $name
231
     * @param DataProviderInterface $dataProvider
232
     */
233 1
    public function addDataProvider($name, DataProviderInterface $dataProvider)
234
    {
235 1
        $this
236
            ->dataProviders
237 1
            ->set($name, $dataProvider);
238 1
    }
239
240
    /**
241
     * Return a configured data provider or create an new instance of the default one.
242
     *
243
     * @param string $entityClass
244
     * @param string|null $name
245
     * @return DataProvider|mixed
246
     * @throws Exception
247
     */
248 5
    protected function getDataProvider($entityClass, $name = null)
249
    {
250
        // create or get repository according to the configuration
251 5
        if ($name) {
1 ignored issue
show
Bug Best Practice introduced by
The expression $name of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
252
            // custom data provider class must be loaded by the compiler pass
253
            if (!$this->dataProviders->has($name)) {
254
                throw new Exception(sprintf(
255
                    'Data provider %s not found. Did you add the @data_provider tag in your service',
256
                    $name
257
                ));
258
            }
259
            $dataProvider = $this
260
                ->dataProviders
261
                ->get($name);
262
        } else {
263
            // no data provider is configured, we create a new one
264 5
            $repository = $this
265
                ->entityManager
266 5
                ->getRepository($entityClass);
267
268 5
            if (!($repository instanceof RepositoryInterface)) {
269
                $repositoryClass = get_class($repository);
270
                throw new Exception("Repository {$repositoryClass} should implements " . RepositoryInterface::class);
271
            }
272
273 5
            $dataProvider = new DataProvider($repository);
274
        }
275 5
        return $dataProvider;
276
    }
277
278
    /**
279
     * Resolve admin configuration.
280
     *
281
     * @param array $configuration
282
     * @return array
283
     */
284 5
    protected function resolveConfiguration(array $configuration)
285
    {
286 5
        $resolver = new OptionsResolver();
287
        // optional options
288 5
        $resolver->setDefaults([
289
            'actions' => [
290 5
                'list' => [],
291 5
                'create' => [],
292 5
                'edit' => [],
293 5
                'delete' => [],
294 5
            ],
295 5
            'batch' => true,
296 5
            'manager' => 'LAG\AdminBundle\Manager\GenericManager',
297 5
            'routing_url_pattern' => $this->application->getRoutingUrlPattern(),
298 5
            'routing_name_pattern' => $this->application->getRoutingNamePattern(),
299 5
            'controller' => 'LAGAdminBundle:CRUD',
300 5
            'max_per_page' => $this->application->getMaxPerPage(),
301
            'data_provider' => null
302 5
        ]);
303
        // required options
304 5
        $resolver->setRequired([
305 5
            'entity',
306 5
            'form',
307 5
        ]);
308
309 5
        return $resolver->resolve($configuration);
310
    }
311
}
312