|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace LAG\AdminBundle\Controller; |
|
4
|
|
|
|
|
5
|
|
|
use LAG\AdminBundle\Admin\AdminInterface; |
|
6
|
|
|
use LAG\AdminBundle\Form\Handler\ListFormHandler; |
|
7
|
|
|
use LAG\AdminBundle\Form\Type\AdminListType; |
|
8
|
|
|
use LAG\AdminBundle\Form\Type\BatchActionType; |
|
9
|
|
|
use BlueBear\BaseBundle\Behavior\ControllerTrait; |
|
10
|
|
|
use DateTime; |
|
11
|
|
|
use Doctrine\ORM\Mapping\MappingException; |
|
12
|
|
|
use EE\DataExporterBundle\Service\DataExporter; |
|
13
|
|
|
use Exception; |
|
14
|
|
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; |
|
15
|
|
|
use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
|
16
|
|
|
use Symfony\Component\HttpFoundation\RedirectResponse; |
|
17
|
|
|
use Symfony\Component\HttpFoundation\Request; |
|
18
|
|
|
use Symfony\Component\HttpFoundation\Response; |
|
19
|
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; |
|
20
|
|
|
use Symfony\Component\PropertyAccess\PropertyAccess; |
|
21
|
|
|
use Symfony\Component\Security\Core\Role\Role; |
|
22
|
|
|
|
|
23
|
|
|
/** |
|
24
|
|
|
* Class CRUDController |
|
25
|
|
|
* |
|
26
|
|
|
* Generic CRUD controller |
|
27
|
|
|
*/ |
|
28
|
|
|
class CRUDController extends Controller |
|
29
|
|
|
{ |
|
30
|
|
|
use ControllerTrait; |
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* Generic list action |
|
34
|
|
|
* |
|
35
|
|
|
* @Template("LAGAdminBundle:CRUD:list.html.twig") |
|
36
|
|
|
* @param Request $request |
|
37
|
|
|
* @return array |
|
38
|
|
|
*/ |
|
39
|
|
|
public function listAction(Request $request) |
|
40
|
|
|
{ |
|
41
|
|
|
// retrieve admin from request route parameters |
|
42
|
|
|
$admin = $this->getAdminFromRequest($request); |
|
43
|
|
|
$admin->handleRequest($request, $this->getUser()); |
|
44
|
|
|
// creating list form |
|
45
|
|
|
$form = $this->createForm(AdminListType::class, [ |
|
46
|
|
|
'entities' => $admin->getEntities() |
|
47
|
|
|
], [ |
|
48
|
|
|
'batch_actions' => $admin |
|
49
|
|
|
->getCurrentAction() |
|
50
|
|
|
->getBatchActions() |
|
51
|
|
|
]); |
|
52
|
|
|
$form->handleRequest($request); |
|
53
|
|
|
|
|
54
|
|
|
if ($request->get('export')) { |
|
55
|
|
|
return $this->exportEntities($admin, $request->get('export')); |
|
56
|
|
|
} |
|
57
|
|
|
if ($form->isValid()) { |
|
58
|
|
|
// get ids and batch action from list form data |
|
59
|
|
|
$formHandler = new ListFormHandler(); |
|
60
|
|
|
$data = $formHandler->handle($form); |
|
61
|
|
|
$batchForm = $this->createForm(BatchActionType::class, [ |
|
62
|
|
|
'batch_action' => $data['batch_action'], |
|
63
|
|
|
'entity_ids' => $data['ids'] |
|
64
|
|
|
], [ |
|
65
|
|
|
'labels' => $data['labels'] |
|
66
|
|
|
]); |
|
67
|
|
|
|
|
68
|
|
|
// render batch view |
|
69
|
|
|
return $this->render('LAGAdminBundle:CRUD:batch.html.twig', [ |
|
70
|
|
|
'admin' => $admin, |
|
71
|
|
|
'form' => $batchForm->createView() |
|
72
|
|
|
]); |
|
73
|
|
|
} |
|
74
|
|
|
return [ |
|
75
|
|
|
'admin' => $admin, |
|
76
|
|
|
'action' => $admin->getCurrentAction(), |
|
77
|
|
|
'form' => $form->createView() |
|
78
|
|
|
]; |
|
79
|
|
|
} |
|
80
|
|
|
|
|
81
|
|
|
/** |
|
82
|
|
|
* @param Request $request |
|
83
|
|
|
* @return RedirectResponse |
|
84
|
|
|
*/ |
|
85
|
|
|
public function batchAction(Request $request) |
|
86
|
|
|
{ |
|
87
|
|
|
$admin = $this->getAdminFromRequest($request); |
|
88
|
|
|
$admin->handleRequest($request, $this->getUser()); |
|
89
|
|
|
// create batch action form |
|
90
|
|
|
$form = $this->createForm(BatchActionType::class, [ |
|
91
|
|
|
'batch_action' => [], |
|
92
|
|
|
'entity_ids' => [] |
|
93
|
|
|
]); |
|
94
|
|
|
$form->handleRequest($request); |
|
95
|
|
|
|
|
96
|
|
|
if ($form->isValid()) { |
|
97
|
|
|
$data = $form->getData(); |
|
98
|
|
|
$admin->load([ |
|
99
|
|
|
'id' => $data['entity_ids'] |
|
100
|
|
|
]); |
|
101
|
|
|
|
|
102
|
|
|
if ($data['batch_action'] == 'delete') { |
|
103
|
|
|
$admin->remove(); |
|
104
|
|
|
} |
|
105
|
|
|
} else { |
|
106
|
|
|
throw new NotFoundHttpException('Invalid batch parameters'); |
|
107
|
|
|
} |
|
108
|
|
|
// redirect to list view |
|
109
|
|
|
return $this->redirectToRoute($admin->generateRouteName('list')); |
|
110
|
|
|
} |
|
111
|
|
|
|
|
112
|
|
|
/** |
|
113
|
|
|
* Generic create action |
|
114
|
|
|
* |
|
115
|
|
|
* @Template("LAGAdminBundle:CRUD:edit.html.twig") |
|
116
|
|
|
* @param Request $request |
|
117
|
|
|
* @return array |
|
118
|
|
|
*/ |
|
119
|
|
|
public function createAction(Request $request) |
|
120
|
|
|
{ |
|
121
|
|
|
$admin = $this->getAdminFromRequest($request); |
|
122
|
|
|
$admin->handleRequest($request, $this->getUser()); |
|
123
|
|
|
// check permissions |
|
124
|
|
|
$this->forward404IfNotAllowed($admin); |
|
125
|
|
|
// create form |
|
126
|
|
|
$form = $this->createForm($admin->getConfiguration()->getFormType()); |
|
127
|
|
|
$form->handleRequest($request); |
|
128
|
|
|
|
|
129
|
|
View Code Duplication |
if ($form->isValid()) { |
|
|
|
|
|
|
130
|
|
|
// save entity |
|
131
|
|
|
$admin->save(); |
|
132
|
|
|
|
|
133
|
|
|
// if save is pressed, user stay on the edit view |
|
134
|
|
|
if ($request->request->get('submit') == 'save') { |
|
135
|
|
|
$editRoute = $admin->generateRouteName('edit'); |
|
136
|
|
|
|
|
137
|
|
|
return $this->redirectToRoute($editRoute, [ |
|
138
|
|
|
'id' => $admin->getUniqueEntity()->getId(), |
|
139
|
|
|
]); |
|
140
|
|
|
} else { |
|
141
|
|
|
// otherwise user is redirected to list view |
|
142
|
|
|
$listRoute = $admin->generateRouteName('list'); |
|
143
|
|
|
|
|
144
|
|
|
return $this->redirectToRoute($listRoute); |
|
145
|
|
|
} |
|
146
|
|
|
} |
|
147
|
|
|
return [ |
|
148
|
|
|
'admin' => $admin, |
|
149
|
|
|
'form' => $form->createView(), |
|
150
|
|
|
]; |
|
151
|
|
|
} |
|
152
|
|
|
|
|
153
|
|
|
/** |
|
154
|
|
|
* Generic edit action. |
|
155
|
|
|
* |
|
156
|
|
|
* @Template("LAGAdminBundle:CRUD:edit.html.twig") |
|
157
|
|
|
* |
|
158
|
|
|
* @param Request $request |
|
159
|
|
|
* |
|
160
|
|
|
* @return array|RedirectResponse |
|
161
|
|
|
*/ |
|
162
|
|
|
public function editAction(Request $request) |
|
163
|
|
|
{ |
|
164
|
|
|
$admin = $this->getAdminFromRequest($request); |
|
165
|
|
|
$admin->handleRequest($request, $this->getUser()); |
|
166
|
|
|
// check permissions |
|
167
|
|
|
$this->forward404IfNotAllowed($admin); |
|
168
|
|
|
// create form |
|
169
|
|
|
$form = $this->createForm($admin->getConfiguration()->getFormType(), $admin->getUniqueEntity()); |
|
170
|
|
|
$form->handleRequest($request); |
|
171
|
|
|
$accessor = PropertyAccess::createPropertyAccessor(); |
|
172
|
|
|
|
|
173
|
|
View Code Duplication |
if ($form->isValid()) { |
|
|
|
|
|
|
174
|
|
|
$admin->save(); |
|
175
|
|
|
|
|
176
|
|
|
if ($request->request->get('submit') == 'save') { |
|
177
|
|
|
$saveRoute = $admin->generateRouteName('edit'); |
|
178
|
|
|
|
|
179
|
|
|
return $this->redirectToRoute($saveRoute, [ |
|
180
|
|
|
'id' => $accessor->getValue($admin->getUniqueEntity(), 'id'), |
|
181
|
|
|
]); |
|
182
|
|
|
} else { |
|
183
|
|
|
$listRoute = $admin->generateRouteName('list'); |
|
184
|
|
|
// redirect to list |
|
185
|
|
|
return $this->redirectToRoute($listRoute); |
|
186
|
|
|
} |
|
187
|
|
|
} |
|
188
|
|
|
|
|
189
|
|
|
return [ |
|
190
|
|
|
'admin' => $admin, |
|
191
|
|
|
'form' => $form->createView(), |
|
192
|
|
|
]; |
|
193
|
|
|
} |
|
194
|
|
|
|
|
195
|
|
|
/** |
|
196
|
|
|
* Generic delete action |
|
197
|
|
|
* |
|
198
|
|
|
* @Template("LAGAdminBundle:CRUD:delete.html.twig") |
|
199
|
|
|
* |
|
200
|
|
|
* @param Request $request |
|
201
|
|
|
* |
|
202
|
|
|
* @return RedirectResponse |
|
203
|
|
|
*/ |
|
204
|
|
|
public function deleteAction(Request $request) |
|
205
|
|
|
{ |
|
206
|
|
|
$admin = $this->getAdminFromRequest($request); |
|
207
|
|
|
$admin->handleRequest($request, $this->getUser()); |
|
208
|
|
|
// check permissions |
|
209
|
|
|
$this->forward404IfNotAllowed($admin); |
|
210
|
|
|
// create form to avoid deletion by url |
|
211
|
|
|
$form = $this->createForm('delete', $admin->getUniqueEntity()); |
|
212
|
|
|
$form->handleRequest($request); |
|
213
|
|
|
|
|
214
|
|
|
if ($form->isValid()) { |
|
215
|
|
|
$admin->remove(); |
|
216
|
|
|
// redirect to list |
|
217
|
|
|
$listRoute = $admin->generateRouteName('list'); |
|
218
|
|
|
|
|
219
|
|
|
return $this->redirectToRoute($listRoute); |
|
220
|
|
|
} |
|
221
|
|
|
|
|
222
|
|
|
return [ |
|
|
|
|
|
|
223
|
|
|
'admin' => $admin, |
|
224
|
|
|
'form' => $form->createView(), |
|
225
|
|
|
]; |
|
226
|
|
|
} |
|
227
|
|
|
|
|
228
|
|
|
/** |
|
229
|
|
|
* Export entities according to a type (json, csv, xls...) |
|
230
|
|
|
* |
|
231
|
|
|
* @param AdminInterface $admin |
|
232
|
|
|
* @param $exportType |
|
233
|
|
|
* |
|
234
|
|
|
* @return Response |
|
235
|
|
|
* |
|
236
|
|
|
* @throws MappingException |
|
237
|
|
|
*/ |
|
238
|
|
|
protected function exportEntities(AdminInterface $admin, $exportType) |
|
239
|
|
|
{ |
|
240
|
|
|
// check allowed export types |
|
241
|
|
|
$this->forward404Unless(in_array($exportType, ['json', 'html', 'xls', 'csv', 'xml'])); |
|
242
|
|
|
/** @var DataExporter $exporter */ |
|
243
|
|
|
$exporter = $this->get('ee.dataexporter'); |
|
244
|
|
|
$metadata = $this |
|
245
|
|
|
->getEntityManager() |
|
246
|
|
|
->getClassMetadata($admin->getConfiguration()->getEntityName()); |
|
247
|
|
|
$exportColumns = []; |
|
248
|
|
|
$fields = $metadata->getFieldNames(); |
|
249
|
|
|
$hooks = []; |
|
250
|
|
|
|
|
251
|
|
|
foreach ($fields as $fieldName) { |
|
252
|
|
|
$exporter->addHook(function ($fieldValue) { |
|
253
|
|
|
// if field is an array |
|
254
|
|
|
if (is_array($fieldValue)) { |
|
255
|
|
|
$value = recursiveImplode(', ', $fieldValue); |
|
256
|
|
|
} elseif ($fieldValue instanceof DateTime) { |
|
257
|
|
|
// format date in string |
|
258
|
|
|
$value = $fieldValue->format('c'); |
|
259
|
|
|
} else { |
|
260
|
|
|
$value = $fieldValue; |
|
261
|
|
|
} |
|
262
|
|
|
|
|
263
|
|
|
return $value; |
|
264
|
|
|
}, "{$fieldName}"); |
|
265
|
|
|
// add field column to export |
|
266
|
|
|
$exportColumns[$fieldName] = $fieldName; |
|
267
|
|
|
} |
|
268
|
|
|
$exporter |
|
269
|
|
|
->setOptions($exportType, [ |
|
270
|
|
|
'fileName' => $admin->getName() . '-export-' . date('Y-m-d'), |
|
271
|
|
|
]) |
|
272
|
|
|
->setColumns($exportColumns) |
|
273
|
|
|
->setData($admin->getEntities()); |
|
274
|
|
|
foreach ($hooks as $hookName => $hook) { |
|
275
|
|
|
$exporter->addHook($hook, $hookName); |
|
276
|
|
|
} |
|
277
|
|
|
|
|
278
|
|
|
return $exporter->render(); |
|
279
|
|
|
} |
|
280
|
|
|
|
|
281
|
|
|
/** |
|
282
|
|
|
* Forward to 404 if user is not allowed by configuration for an action. |
|
283
|
|
|
* |
|
284
|
|
|
* @param AdminInterface $admin |
|
285
|
|
|
*/ |
|
286
|
|
|
protected function forward404IfNotAllowed(AdminInterface $admin) |
|
287
|
|
|
{ |
|
288
|
|
|
// TODO move authorizations logic into kernel.request event |
|
289
|
|
|
$this->forward404Unless($this->getUser(), 'You must be logged to access to this url'); |
|
290
|
|
|
$roles = $this |
|
291
|
|
|
->getUser() |
|
292
|
|
|
->getRoles(); |
|
293
|
|
|
// check permissions and actions |
|
294
|
|
|
$this->forward404Unless( |
|
295
|
|
|
$admin->isActionGranted($admin->getCurrentAction()->getName(), $roles), |
|
296
|
|
|
sprintf('User with roles %s not allowed for action "%s"', |
|
297
|
|
|
implode(', ', array_map(function (Role $role) { |
|
298
|
|
|
return $role->getRole(); |
|
299
|
|
|
}, $roles)), |
|
300
|
|
|
$admin->getCurrentAction()->getName() |
|
301
|
|
|
) |
|
302
|
|
|
); |
|
303
|
|
|
} |
|
304
|
|
|
|
|
305
|
|
|
/** |
|
306
|
|
|
* Return an Admin object according to the request route parameters. |
|
307
|
|
|
* |
|
308
|
|
|
* @param Request $request |
|
309
|
|
|
* @return AdminInterface |
|
310
|
|
|
* @throws Exception |
|
311
|
|
|
*/ |
|
312
|
|
|
protected function getAdminFromRequest(Request $request) |
|
313
|
|
|
{ |
|
314
|
|
|
return $this |
|
315
|
|
|
->get('lag.admin.factory') |
|
316
|
|
|
->getAdminFromRequest($request); |
|
317
|
|
|
} |
|
318
|
|
|
} |
|
319
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.