Completed
Pull Request — master (#40)
by Eric
13:22 queued 11s
created

Controller::getFactory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
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'));
0 ignored issues
show
Bug introduced by
It seems like $this->find('update') targeting Lug\Bundle\ResourceBundl...ller\Controller::find() can also be of type array<integer,object>; however, Lug\Bundle\ResourceBundl...Controller::buildForm() does only seem to accept object|null, maybe add an additional type check?

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.

Loading history...
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'));
0 ignored issues
show
Bug introduced by
It seems like $this->find('delete') targeting Lug\Bundle\ResourceBundl...ller\Controller::find() can also be of type array<integer,object>; however, Lug\Component\Resource\D...agerInterface::delete() does only seem to accept object, maybe add an additional type check?

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.

Loading history...
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($this->resource, $form, $object, $options);
0 ignored issues
show
Bug introduced by
It seems like $form defined by parameter $form on line 226 can also be of type object; however, Lug\Bundle\ResourceBundl...toryInterface::create() does only seem to accept string|object<Symfony\Co...FormTypeInterface>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and 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.

Loading history...
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->getRestEventDispatcher()->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)
0 ignored issues
show
Bug introduced by
It seems like $view defined by $event->getView() on line 290 can be null; however, FOS\RestBundle\Controlle...llerTrait::handleView() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
295
            : $this->processView($view);
0 ignored issues
show
Bug introduced by
It seems like $view defined by $event->getView() on line 290 can be null; however, Lug\Bundle\ResourceBundl...ntroller::processView() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
296
    }
297
298
    /**
299
     * @param View $view
300
     *
301
     * @return Response
302
     */
303
    private function processView(View $view)
304
    {
305
        $this->getRestEventDispatcher()->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 FormFactoryInterface
326
     */
327
    private function getFormFactory()
328
    {
329
        return $this->get('lug.resource.form.factory');
330
    }
331
332
    /**
333
     * @return FactoryInterface
334
     */
335
    private function getFactory()
336
    {
337
        return $this->get('lug.resource.registry.factory')[$this->resource->getName()];
338
    }
339
340
    /**
341
     * @return DomainManagerInterface
342
     */
343
    private function getDomainManager()
344
    {
345
        return $this->get('lug.resource.registry.domain_manager')[$this->resource->getName()];
346
    }
347
348
    /**
349
     * @return SecurityCheckerInterface
350
     */
351
    private function getSecurityChecker()
352
    {
353
        return $this->get('lug.resource.security.checker');
354
    }
355
356
    /**
357
     * @return ParameterResolverInterface
358
     */
359
    private function getParameterResolver()
360
    {
361
        return $this->get('lug.resource.routing.parameter_resolver');
362
    }
363
364
    /**
365
     * @return GridBuilderInterface
366
     */
367
    private function getGridBuilder()
368
    {
369
        return $this->get('lug.grid.builder');
370
    }
371
372
    /**
373
     * @return GridHandlerInterface
374
     */
375
    private function getGridHandler()
376
    {
377
        return $this->get('lug.grid.handler');
378
    }
379
380
    /**
381
     * @return BatcherInterface
382
     */
383
    private function getGridBatcher()
384
    {
385
        return $this->get('lug.grid.batcher');
386
    }
387
388
    /**
389
     * @return EventDispatcherInterface
390
     */
391
    private function getRestEventDispatcher()
392
    {
393
        return $this->get('lug.resource.rest.event_dispatcher');
394
    }
395
}
396