|
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, |
|
|
|
|
|
|
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) { |
|
|
|
|
|
|
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
|
|
|
|
The
EntityManagermight 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:If that code throws an exception and the
EntityManageris closed. Any other code which depends on the same instance of theEntityManagerduring this request will fail.On the other hand, if you instead inject the
ManagerRegistry, thegetManager()method guarantees that you will always get a usable manager instance.