1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the Lug package. |
5
|
|
|
* |
6
|
|
|
* (c) Eric GELOEN <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please read the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Lug\Bundle\ResourceBundle\Controller; |
13
|
|
|
|
14
|
|
|
use FOS\RestBundle\Controller\FOSRestController; |
15
|
|
|
use FOS\RestBundle\View\View; |
16
|
|
|
use Lug\Bundle\GridBundle\Batch\BatcherInterface; |
17
|
|
|
use Lug\Bundle\GridBundle\Form\Type\Batch\GridBatchType; |
18
|
|
|
use Lug\Bundle\GridBundle\Form\Type\GridType; |
19
|
|
|
use Lug\Bundle\GridBundle\Handler\GridHandlerInterface; |
20
|
|
|
use Lug\Bundle\ResourceBundle\Form\FormFactoryInterface; |
21
|
|
|
use Lug\Bundle\ResourceBundle\Form\Type\CsrfProtectionType; |
22
|
|
|
use Lug\Bundle\ResourceBundle\Rest\Action\ActionEvent; |
23
|
|
|
use Lug\Bundle\ResourceBundle\Rest\RestEvents; |
24
|
|
|
use Lug\Bundle\ResourceBundle\Rest\View\ViewEvent; |
25
|
|
|
use Lug\Bundle\ResourceBundle\Routing\ParameterResolverInterface; |
26
|
|
|
use Lug\Bundle\ResourceBundle\Security\SecurityCheckerInterface; |
27
|
|
|
use Lug\Component\Grid\Model\Builder\GridBuilderInterface; |
28
|
|
|
use Lug\Component\Grid\Model\GridInterface; |
29
|
|
|
use Lug\Component\Resource\Controller\ControllerInterface; |
30
|
|
|
use Lug\Component\Resource\Domain\DomainManagerInterface; |
31
|
|
|
use Lug\Component\Resource\Exception\DomainException; |
32
|
|
|
use Lug\Component\Resource\Factory\FactoryInterface; |
33
|
|
|
use Lug\Component\Resource\Model\ResourceInterface; |
34
|
|
|
use Pagerfanta\Pagerfanta; |
35
|
|
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
36
|
|
|
use Symfony\Component\Form\FormInterface; |
37
|
|
|
use Symfony\Component\HttpFoundation\Request; |
38
|
|
|
use Symfony\Component\HttpFoundation\Response; |
39
|
|
|
use Symfony\Component\HttpKernel\Exception\HttpException; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @author GeLo <[email protected]> |
43
|
|
|
*/ |
44
|
|
|
class Controller extends FOSRestController implements ControllerInterface |
45
|
|
|
{ |
46
|
|
|
/** |
47
|
|
|
* @var ResourceInterface |
48
|
|
|
*/ |
49
|
|
|
private $resource; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @param ResourceInterface $resource |
53
|
|
|
*/ |
54
|
|
|
public function __construct(ResourceInterface $resource) |
55
|
|
|
{ |
56
|
|
|
$this->resource = $resource; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @param Request $request |
61
|
|
|
* |
62
|
|
|
* @return Response |
63
|
|
|
*/ |
64
|
|
|
public function indexAction(Request $request) |
65
|
|
|
{ |
66
|
|
|
return $this->processView($this->view($this->find('index', false))); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @param Request $request |
71
|
|
|
* |
72
|
|
|
* @return Response |
73
|
|
|
*/ |
74
|
|
|
public function gridAction(Request $request) |
75
|
|
|
{ |
76
|
|
|
return $this->processView($this->view([ |
77
|
|
|
'form' => $form = $this->submitGrid($grid = $this->buildGrid(), $request), |
78
|
|
|
'grid' => $this->getGridHandler()->handle($grid, $form), |
79
|
|
|
])); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* @param Request $request |
84
|
|
|
* |
85
|
|
|
* @return Response |
86
|
|
|
*/ |
87
|
|
|
public function batchAction(Request $request) |
88
|
|
|
{ |
89
|
|
|
$view = $this->getGridHandler()->handle( |
90
|
|
|
$grid = $this->buildGrid(), |
91
|
|
|
$form = $this->submitGrid($grid, $request) |
92
|
|
|
); |
93
|
|
|
|
94
|
|
|
if (($api = $this->getParameterResolver()->resolveApi()) && !$form->isValid()) { |
95
|
|
|
return $this->processAction($form); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
$batchForm = $this->buildForm(GridBatchType::class, null, ['grid' => $view]); |
99
|
|
|
|
100
|
|
|
if ($this->submitForm($batchForm, $request)->isValid() && $form->isValid()) { |
101
|
|
|
try { |
102
|
|
|
$this->getGridBatcher()->batch($grid, $batchForm); |
103
|
|
|
} catch (DomainException $e) { |
104
|
|
|
$this->processException($e); |
105
|
|
|
} |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
if (!$api && !$batchForm->isValid()) { |
109
|
|
|
return $this->processView($this->view([ |
110
|
|
|
'batch_form' => $batchForm, |
111
|
|
|
'form' => $form, |
112
|
|
|
'grid' => $view, |
113
|
|
|
])); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
return $this->processAction($batchForm); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* @param Request $request |
121
|
|
|
* |
122
|
|
|
* @return Response |
123
|
|
|
*/ |
124
|
|
|
public function showAction(Request $request) |
125
|
|
|
{ |
126
|
|
|
return $this->processView($this->view($this->find('show'))); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* @param Request $request |
131
|
|
|
* |
132
|
|
|
* @return Response |
133
|
|
|
*/ |
134
|
|
|
public function createAction(Request $request) |
135
|
|
|
{ |
136
|
|
|
$form = $this->buildForm(null, $this->getFactory()->create()); |
137
|
|
|
|
138
|
|
|
if ($request->isMethod('POST') && $this->submitForm($form, $request)->isValid()) { |
139
|
|
|
try { |
140
|
|
|
$this->getDomainManager()->create($form->getData()); |
141
|
|
|
} catch (DomainException $e) { |
142
|
|
|
$this->processException($e); |
143
|
|
|
} |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
return $this->processAction($form, Response::HTTP_CREATED); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* @param Request $request |
151
|
|
|
* |
152
|
|
|
* @return Response |
153
|
|
|
*/ |
154
|
|
|
public function updateAction(Request $request) |
155
|
|
|
{ |
156
|
|
|
$form = $this->buildForm(null, $this->find('update')); |
|
|
|
|
157
|
|
|
|
158
|
|
|
if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) |
159
|
|
|
&& $this->submitForm($form, $request)->isValid()) { |
160
|
|
|
try { |
161
|
|
|
$this->getDomainManager()->update($form->getData()); |
162
|
|
|
} catch (DomainException $e) { |
163
|
|
|
$this->processException($e); |
164
|
|
|
} |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
return $this->processAction($form); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* @param Request $request |
172
|
|
|
* |
173
|
|
|
* @return Response |
174
|
|
|
*/ |
175
|
|
|
public function deleteAction(Request $request) |
176
|
|
|
{ |
177
|
|
|
if ($this->submitForm($this->buildForm(CsrfProtectionType::class), $request)->isValid()) { |
178
|
|
|
try { |
179
|
|
|
$this->getDomainManager()->delete($this->find('delete')); |
|
|
|
|
180
|
|
|
} catch (DomainException $e) { |
181
|
|
|
$this->processException($e); |
182
|
|
|
} |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
return $this->processAction(); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* @param string $action |
190
|
|
|
* @param bool $mandatory |
191
|
|
|
* |
192
|
|
|
* @return object|object[] |
193
|
|
|
*/ |
194
|
|
|
private function find($action, $mandatory = true) |
195
|
|
|
{ |
196
|
|
|
$repositoryMethod = $this->getParameterResolver()->resolveRepositoryMethod($action); |
197
|
|
|
$criteria = $this->getParameterResolver()->resolveCriteria($mandatory); |
198
|
|
|
$sorting = $this->getParameterResolver()->resolveSorting(); |
199
|
|
|
|
200
|
|
|
if (($result = $this->getDomainManager()->find($action, $repositoryMethod, $criteria, $sorting)) === null) { |
201
|
|
|
throw $this->createNotFoundException(sprintf( |
202
|
|
|
'The %s does not exist (%s) (%s).', |
203
|
|
|
str_replace('_', ' ', $this->resource->getName()), |
204
|
|
|
json_encode($criteria), |
205
|
|
|
json_encode($sorting) |
206
|
|
|
)); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
if ($result instanceof Pagerfanta) { |
210
|
|
|
$result->setCurrentPage($this->getParameterResolver()->resolveCurrentPage()); |
211
|
|
|
$result->setMaxPerPage($this->getParameterResolver()->resolveMaxPerPage()); |
212
|
|
|
} elseif (!$this->getSecurityChecker()->isGranted($action, $result)) { |
213
|
|
|
throw $this->createAccessDeniedException(); |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
return $result; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* @param string|object|null $form |
221
|
|
|
* @param object|null $object |
222
|
|
|
* @param mixed[] $options |
223
|
|
|
* |
224
|
|
|
* @return FormInterface |
225
|
|
|
*/ |
226
|
|
|
private function buildForm($form = null, $object = null, array $options = []) |
227
|
|
|
{ |
228
|
|
|
return $this->getFormFactory()->create($form ?: $this->resource, $object, $options); |
|
|
|
|
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* @param FormInterface $form |
233
|
|
|
* @param Request $request |
234
|
|
|
* |
235
|
|
|
* @return FormInterface |
236
|
|
|
*/ |
237
|
|
|
private function submitForm(FormInterface $form, Request $request) |
238
|
|
|
{ |
239
|
|
|
$bag = $request->isMethod('GET') || $form->getConfig()->getMethod() === 'GET' |
240
|
|
|
? $request->query |
241
|
|
|
: $request->request; |
242
|
|
|
|
243
|
|
|
if ($this->getParameterResolver()->resolveApi()) { |
244
|
|
|
$data = $bag->all(); |
245
|
|
|
} else { |
246
|
|
|
$data = $bag->get($form->getName(), []); |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
array_walk_recursive($data, function (&$value) { |
250
|
|
|
if ($value === false) { |
251
|
|
|
$value = 'false'; |
252
|
|
|
} |
253
|
|
|
}); |
254
|
|
|
|
255
|
|
|
return $form->submit($data, !$request->isMethod('PATCH')); |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* @return GridInterface |
260
|
|
|
*/ |
261
|
|
|
private function buildGrid() |
262
|
|
|
{ |
263
|
|
|
return $this->getGridBuilder()->build($this->getParameterResolver()->resolveGrid($this->resource)); |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* @param GridInterface $grid |
268
|
|
|
* @param Request $request |
269
|
|
|
* |
270
|
|
|
* @return FormInterface |
271
|
|
|
*/ |
272
|
|
|
private function submitGrid(GridInterface $grid, Request $request) |
273
|
|
|
{ |
274
|
|
|
return $this->submitForm($this->buildForm(GridType::class, null, ['grid' => $grid]), $request); |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* @param FormInterface|null $form |
279
|
|
|
* @param int $statusCode |
280
|
|
|
* |
281
|
|
|
* @return Response |
282
|
|
|
*/ |
283
|
|
|
private function processAction(FormInterface $form = null, $statusCode = Response::HTTP_NO_CONTENT) |
284
|
|
|
{ |
285
|
|
|
$this->getEventDispatcher()->dispatch( |
286
|
|
|
RestEvents::ACTION, |
287
|
|
|
$event = new ActionEvent($this->resource, $form, $statusCode) |
288
|
|
|
); |
289
|
|
|
|
290
|
|
|
$view = $event->getView(); |
291
|
|
|
$statusCode = $view->getStatusCode(); |
292
|
|
|
|
293
|
|
|
return $statusCode >= 300 && $statusCode < 400 |
294
|
|
|
? $this->handleView($view) |
|
|
|
|
295
|
|
|
: $this->processView($view); |
|
|
|
|
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
/** |
299
|
|
|
* @param View $view |
300
|
|
|
* |
301
|
|
|
* @return Response |
302
|
|
|
*/ |
303
|
|
|
private function processView(View $view) |
304
|
|
|
{ |
305
|
|
|
$this->getEventDispatcher()->dispatch(RestEvents::VIEW, $event = new ViewEvent($this->resource, $view)); |
306
|
|
|
|
307
|
|
|
return $this->handleView($event->getView()); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* @param DomainException $domainException |
312
|
|
|
*/ |
313
|
|
|
private function processException(DomainException $domainException) |
314
|
|
|
{ |
315
|
|
|
if ($this->getParameterResolver()->resolveApi()) { |
316
|
|
|
throw new HttpException( |
317
|
|
|
$domainException->getStatusCode() ?: 500, |
318
|
|
|
$domainException->getMessage() ?: 'Internal Server Error', |
319
|
|
|
$domainException |
320
|
|
|
); |
321
|
|
|
} |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* @return EventDispatcherInterface |
326
|
|
|
*/ |
327
|
|
|
private function getEventDispatcher() |
328
|
|
|
{ |
329
|
|
|
return $this->get('event_dispatcher'); |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* @return FormFactoryInterface |
334
|
|
|
*/ |
335
|
|
|
private function getFormFactory() |
336
|
|
|
{ |
337
|
|
|
return $this->get('lug.resource.form.factory'); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* @return FactoryInterface |
342
|
|
|
*/ |
343
|
|
|
private function getFactory() |
344
|
|
|
{ |
345
|
|
|
return $this->get('lug.resource.registry.factory')[$this->resource->getName()]; |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
/** |
349
|
|
|
* @return DomainManagerInterface |
350
|
|
|
*/ |
351
|
|
|
private function getDomainManager() |
352
|
|
|
{ |
353
|
|
|
return $this->get('lug.resource.registry.domain_manager')[$this->resource->getName()]; |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* @return SecurityCheckerInterface |
358
|
|
|
*/ |
359
|
|
|
private function getSecurityChecker() |
360
|
|
|
{ |
361
|
|
|
return $this->get('lug.resource.security.checker'); |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* @return ParameterResolverInterface |
366
|
|
|
*/ |
367
|
|
|
private function getParameterResolver() |
368
|
|
|
{ |
369
|
|
|
return $this->get('lug.resource.routing.parameter_resolver'); |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* @return GridBuilderInterface |
374
|
|
|
*/ |
375
|
|
|
private function getGridBuilder() |
376
|
|
|
{ |
377
|
|
|
return $this->get('lug.grid.builder'); |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* @return GridHandlerInterface |
382
|
|
|
*/ |
383
|
|
|
private function getGridHandler() |
384
|
|
|
{ |
385
|
|
|
return $this->get('lug.grid.handler'); |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
/** |
389
|
|
|
* @return BatcherInterface |
390
|
|
|
*/ |
391
|
|
|
private function getGridBatcher() |
392
|
|
|
{ |
393
|
|
|
return $this->get('lug.grid.batcher'); |
394
|
|
|
} |
395
|
|
|
} |
396
|
|
|
|
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.