Completed
Push — 3.x ( 5349d6...5ea965 )
by Grégoire
04:11
created

CRUDController::__call()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 2
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 51 and the first side effect is on line 45.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
/*
4
 * This file is part of the Sonata Project package.
5
 *
6
 * (c) Thomas Rabaix <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sonata\AdminBundle\Controller;
13
14
use Doctrine\Common\Inflector\Inflector;
15
use Psr\Log\LoggerInterface;
16
use Psr\Log\NullLogger;
17
use Sonata\AdminBundle\Admin\AdminInterface;
18
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
19
use Sonata\AdminBundle\Exception\LockException;
20
use Sonata\AdminBundle\Exception\ModelManagerException;
21
use Sonata\AdminBundle\Util\AdminObjectAclData;
22
use Sonata\AdminBundle\Util\AdminObjectAclManipulator;
23
use Symfony\Bridge\Twig\AppVariable;
24
use Symfony\Bridge\Twig\Command\DebugCommand;
25
use Symfony\Bridge\Twig\Extension\FormExtension;
26
use Symfony\Bridge\Twig\Form\TwigRenderer;
27
use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
28
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
29
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
30
use Symfony\Component\DependencyInjection\ContainerInterface;
31
use Symfony\Component\Form\FormRenderer;
32
use Symfony\Component\Form\FormView;
33
use Symfony\Component\HttpFoundation\JsonResponse;
34
use Symfony\Component\HttpFoundation\RedirectResponse;
35
use Symfony\Component\HttpFoundation\Request;
36
use Symfony\Component\HttpFoundation\Response;
37
use Symfony\Component\HttpKernel\Exception\HttpException;
38
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
39
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
40
use Symfony\Component\Security\Csrf\CsrfToken;
41
42
// BC for Symfony < 3.3 where this trait does not exist
43
// NEXT_MAJOR: Remove the polyfill and inherit from \Symfony\Bundle\FrameworkBundle\Controller\Controller again
44
if (!trait_exists(ControllerTrait::class)) {
45
    require_once __DIR__.'/PolyfillControllerTrait.php';
46
}
47
48
/**
49
 * @author Thomas Rabaix <[email protected]>
50
 */
51
class CRUDController implements ContainerAwareInterface
52
{
53
    // NEXT_MAJOR: Don't use these traits anymore (inherit from Controller instead)
54
    use ControllerTrait, ContainerAwareTrait {
55
        ControllerTrait::render as originalRender;
56
    }
57
58
    /**
59
     * The related Admin class.
60
     *
61
     * @var AdminInterface
62
     */
63
    protected $admin;
64
65
    // BC for Symfony 3.3 where ControllerTrait exists but does not contain get() and has() methods.
66
    public function __call($method, $arguments)
67
    {
68
        if (in_array($method, ['get', 'has'])) {
69
            return call_user_func_array([$this->container, $method], $arguments);
70
        }
71
72
        if (method_exists($this, 'proxyToControllerClass')) {
73
            return $this->proxyToControllerClass($method, $arguments);
0 ignored issues
show
Documentation Bug introduced by
The method proxyToControllerClass does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
74
        }
75
76
        throw new \LogicException('Call to undefined method '.__CLASS__.'::'.$method);
77
    }
78
79
    /**
80
     * Sets the Container associated with this Controller.
81
     *
82
     * @param ContainerInterface $container A ContainerInterface instance
83
     */
84
    public function setContainer(ContainerInterface $container = null)
85
    {
86
        $this->container = $container;
87
88
        $this->configure();
89
    }
90
91
    /**
92
     * NEXT_MAJOR: Remove this method.
93
     *
94
     * @see renderWithExtraParams()
95
     *
96
     * @param string   $view       The view name
97
     * @param array    $parameters An array of parameters to pass to the view
98
     * @param Response $response   A response instance
99
     *
100
     * @return Response A Response instance
101
     *
102
     * @deprecated
103
     */
104
    public function render($view, array $parameters = [], Response $response = null)
105
    {
106
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
107
            'Method '.__CLASS__.'::render has been renamed to '.__CLASS__.'::renderWithExtraParams.',
108
            E_USER_DEPRECATED
109
        );
110
111
        return $this->renderWithExtraParams($view, $parameters, $response);
112
    }
113
114
    /**
115
     * Renders a view while passing mandatory parameters on to the template.
116
     *
117
     * @param string   $view       The view name
118
     * @param array    $parameters An array of parameters to pass to the view
119
     * @param Response $response   A response instance
120
     *
121
     * @return Response A Response instance
122
     */
123
    public function renderWithExtraParams($view, array $parameters = [], Response $response = null)
124
    {
125
        if (!$this->isXmlHttpRequest()) {
126
            $parameters['breadcrumbs_builder'] = $this->get('sonata.admin.breadcrumbs_builder');
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
127
        }
128
        $parameters['admin'] = isset($parameters['admin']) ?
129
            $parameters['admin'] :
130
            $this->admin;
131
132
        $parameters['base_template'] = isset($parameters['base_template']) ?
133
            $parameters['base_template'] :
134
            $this->getBaseTemplate();
135
136
        $parameters['admin_pool'] = $this->get('sonata.admin.pool');
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
137
138
        //NEXT_MAJOR: Remove method alias and use $this->render() directly.
139
        return $this->originalRender($view, $parameters, $response);
140
    }
141
142
    /**
143
     * List action.
144
     *
145
     * @return Response
146
     *
147
     * @throws AccessDeniedException If access is not granted
148
     */
149
    public function listAction()
150
    {
151
        $request = $this->getRequest();
152
153
        $this->admin->checkAccess('list');
154
155
        $preResponse = $this->preList($request);
156
        if (null !== $preResponse) {
157
            return $preResponse;
158
        }
159
160
        if ($listMode = $request->get('_list_mode')) {
161
            $this->admin->setListMode($listMode);
162
        }
163
164
        $datagrid = $this->admin->getDatagrid();
165
        $formView = $datagrid->getForm()->createView();
166
167
        // set the theme for the current Admin Form
168
        $this->setFormTheme($formView, $this->admin->getFilterTheme());
0 ignored issues
show
Documentation introduced by
$this->admin->getFilterTheme() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
169
170
        return $this->renderWithExtraParams($this->admin->getTemplate('list'), [
171
            'action' => 'list',
172
            'form' => $formView,
173
            'datagrid' => $datagrid,
174
            'csrf_token' => $this->getCsrfToken('sonata.batch'),
175
            'export_formats' => $this->has('sonata.admin.admin_exporter') ?
0 ignored issues
show
Documentation Bug introduced by
The method has does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
176
                $this->get('sonata.admin.admin_exporter')->getAvailableFormats($this->admin) :
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
177
                $this->admin->getExportFormats(),
178
        ], null);
179
    }
180
181
    /**
182
     * Execute a batch delete.
183
     *
184
     * @param ProxyQueryInterface $query
185
     *
186
     * @return RedirectResponse
187
     *
188
     * @throws AccessDeniedException If access is not granted
189
     */
190
    public function batchActionDelete(ProxyQueryInterface $query)
191
    {
192
        $this->admin->checkAccess('batchDelete');
193
194
        $modelManager = $this->admin->getModelManager();
195
196
        try {
197
            $modelManager->batchDelete($this->admin->getClass(), $query);
198
            $this->addFlash(
199
                'sonata_flash_success',
200
                $this->trans('flash_batch_delete_success', [], 'SonataAdminBundle')
201
            );
202
        } catch (ModelManagerException $e) {
203
            $this->handleModelManagerException($e);
204
            $this->addFlash(
205
                'sonata_flash_error',
206
                $this->trans('flash_batch_delete_error', [], 'SonataAdminBundle')
207
            );
208
        }
209
210
        return new RedirectResponse($this->admin->generateUrl(
211
            'list',
212
            ['filter' => $this->admin->getFilterParameters()]
213
        ));
214
    }
215
216
    /**
217
     * Delete action.
218
     *
219
     * @param int|string|null $id
220
     *
221
     * @return Response|RedirectResponse
222
     *
223
     * @throws NotFoundHttpException If the object does not exist
224
     * @throws AccessDeniedException If access is not granted
225
     */
226
    public function deleteAction($id)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
227
    {
228
        $request = $this->getRequest();
229
        $id = $request->get($this->admin->getIdParameter());
230
        $object = $this->admin->getObject($id);
231
232
        if (!$object) {
233
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
234
        }
235
236
        $this->admin->checkAccess('delete', $object);
237
238
        $preResponse = $this->preDelete($request, $object);
239
        if (null !== $preResponse) {
240
            return $preResponse;
241
        }
242
243
        if ('DELETE' == $this->getRestMethod()) {
244
            // check the csrf token
245
            $this->validateCsrfToken('sonata.delete');
246
247
            $objectName = $this->admin->toString($object);
248
249
            try {
250
                $this->admin->delete($object);
251
252
                if ($this->isXmlHttpRequest()) {
253
                    return $this->renderJson(['result' => 'ok'], 200, []);
254
                }
255
256
                $this->addFlash(
257
                    'sonata_flash_success',
258
                    $this->trans(
259
                        'flash_delete_success',
260
                        ['%name%' => $this->escapeHtml($objectName)],
261
                        'SonataAdminBundle'
262
                    )
263
                );
264
            } catch (ModelManagerException $e) {
265
                $this->handleModelManagerException($e);
266
267
                if ($this->isXmlHttpRequest()) {
268
                    return $this->renderJson(['result' => 'error'], 200, []);
269
                }
270
271
                $this->addFlash(
272
                    'sonata_flash_error',
273
                    $this->trans(
274
                        'flash_delete_error',
275
                        ['%name%' => $this->escapeHtml($objectName)],
276
                        'SonataAdminBundle'
277
                    )
278
                );
279
            }
280
281
            return $this->redirectTo($object);
282
        }
283
284
        return $this->renderWithExtraParams($this->admin->getTemplate('delete'), [
285
            'object' => $object,
286
            'action' => 'delete',
287
            'csrf_token' => $this->getCsrfToken('sonata.delete'),
288
        ], null);
289
    }
290
291
    /**
292
     * Edit action.
293
     *
294
     * @param int|string|null $id
295
     *
296
     * @return Response|RedirectResponse
297
     *
298
     * @throws NotFoundHttpException If the object does not exist
299
     * @throws AccessDeniedException If access is not granted
300
     */
301
    public function editAction($id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
302
    {
303
        $request = $this->getRequest();
304
        // the key used to lookup the template
305
        $templateKey = 'edit';
306
307
        $id = $request->get($this->admin->getIdParameter());
308
        $existingObject = $this->admin->getObject($id);
309
310
        if (!$existingObject) {
311
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
312
        }
313
314
        $this->admin->checkAccess('edit', $existingObject);
315
316
        $preResponse = $this->preEdit($request, $existingObject);
317
        if (null !== $preResponse) {
318
            return $preResponse;
319
        }
320
321
        $this->admin->setSubject($existingObject);
322
        $objectId = $this->admin->getNormalizedIdentifier($existingObject);
323
324
        /** @var $form Form */
325
        $form = $this->admin->getForm();
326
        $form->setData($existingObject);
327
        $form->handleRequest($request);
328
329
        if ($form->isSubmitted()) {
330
            //TODO: remove this check for 4.0
331
            if (method_exists($this->admin, 'preValidate')) {
332
                $this->admin->preValidate($existingObject);
0 ignored issues
show
Bug introduced by
The method preValidate() does not exist on Sonata\AdminBundle\Admin\AdminInterface. Did you maybe mean validate()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
333
            }
334
            $isFormValid = $form->isValid();
335
336
            // persist if the form was valid and if in preview mode the preview was approved
337
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
338
                $submittedObject = $form->getData();
339
                $this->admin->setSubject($submittedObject);
340
341
                try {
342
                    $existingObject = $this->admin->update($submittedObject);
343
344
                    if ($this->isXmlHttpRequest()) {
345
                        return $this->renderJson([
346
                            'result' => 'ok',
347
                            'objectId' => $objectId,
348
                            'objectName' => $this->escapeHtml($this->admin->toString($existingObject)),
349
                        ], 200, []);
350
                    }
351
352
                    $this->addFlash(
353
                        'sonata_flash_success',
354
                        $this->trans(
355
                            'flash_edit_success',
356
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
357
                            'SonataAdminBundle'
358
                        )
359
                    );
360
361
                    // redirect to edit mode
362
                    return $this->redirectTo($existingObject);
363
                } catch (ModelManagerException $e) {
364
                    $this->handleModelManagerException($e);
365
366
                    $isFormValid = false;
367
                } catch (LockException $e) {
368
                    $this->addFlash('sonata_flash_error', $this->trans('flash_lock_error', [
369
                        '%name%' => $this->escapeHtml($this->admin->toString($existingObject)),
370
                        '%link_start%' => '<a href="'.$this->admin->generateObjectUrl('edit', $existingObject).'">',
371
                        '%link_end%' => '</a>',
372
                    ], 'SonataAdminBundle'));
373
                }
374
            }
375
376
            // show an error message if the form failed validation
377
            if (!$isFormValid) {
378
                if (!$this->isXmlHttpRequest()) {
379
                    $this->addFlash(
380
                        'sonata_flash_error',
381
                        $this->trans(
382
                            'flash_edit_error',
383
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
384
                            'SonataAdminBundle'
385
                        )
386
                    );
387
                }
388
            } elseif ($this->isPreviewRequested()) {
389
                // enable the preview template if the form was valid and preview was requested
390
                $templateKey = 'preview';
391
                $this->admin->getShow();
392
            }
393
        }
394
395
        $formView = $form->createView();
396
        // set the theme for the current Admin Form
397
        $this->setFormTheme($formView, $this->admin->getFormTheme());
0 ignored issues
show
Documentation introduced by
$this->admin->getFormTheme() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
398
399
        return $this->renderWithExtraParams($this->admin->getTemplate($templateKey), [
400
            'action' => 'edit',
401
            'form' => $formView,
402
            'object' => $existingObject,
403
            'objectId' => $objectId,
404
        ], null);
405
    }
406
407
    /**
408
     * Batch action.
409
     *
410
     * @return Response|RedirectResponse
411
     *
412
     * @throws NotFoundHttpException If the HTTP method is not POST
413
     * @throws \RuntimeException     If the batch action is not defined
414
     */
415
    public function batchAction()
416
    {
417
        $request = $this->getRequest();
418
        $restMethod = $this->getRestMethod();
419
420
        if ('POST' !== $restMethod) {
421
            throw $this->createNotFoundException(sprintf('Invalid request type "%s", POST expected', $restMethod));
422
        }
423
424
        // check the csrf token
425
        $this->validateCsrfToken('sonata.batch');
426
427
        $confirmation = $request->get('confirmation', false);
428
429
        if ($data = json_decode($request->get('data'), true)) {
430
            $action = $data['action'];
431
            $idx = $data['idx'];
432
            $allElements = $data['all_elements'];
433
            $request->request->replace(array_merge($request->request->all(), $data));
434
        } else {
435
            $request->request->set('idx', $request->get('idx', []));
436
            $request->request->set('all_elements', $request->get('all_elements', false));
437
438
            $action = $request->get('action');
439
            $idx = $request->get('idx');
440
            $allElements = $request->get('all_elements');
441
            $data = $request->request->all();
442
443
            unset($data['_sonata_csrf_token']);
444
        }
445
446
        // NEXT_MAJOR: Remove reflection check.
447
        $reflector = new \ReflectionMethod($this->admin, 'getBatchActions');
448
        if ($reflector->getDeclaringClass()->getName() === get_class($this->admin)) {
0 ignored issues
show
introduced by
Consider using $reflector->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
449
            @trigger_error('Override Sonata\AdminBundle\Admin\AbstractAdmin::getBatchActions method'
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
450
                .' is deprecated since version 3.2.'
451
                .' Use Sonata\AdminBundle\Admin\AbstractAdmin::configureBatchActions instead.'
452
                .' The method will be final in 4.0.', E_USER_DEPRECATED
453
            );
454
        }
455
        $batchActions = $this->admin->getBatchActions();
456
        if (!array_key_exists($action, $batchActions)) {
457
            throw new \RuntimeException(sprintf('The `%s` batch action is not defined', $action));
458
        }
459
460
        $camelizedAction = Inflector::classify($action);
461
        $isRelevantAction = sprintf('batchAction%sIsRelevant', $camelizedAction);
462
463
        if (method_exists($this, $isRelevantAction)) {
464
            $nonRelevantMessage = call_user_func([$this, $isRelevantAction], $idx, $allElements, $request);
465
        } else {
466
            $nonRelevantMessage = 0 != count($idx) || $allElements; // at least one item is selected
467
        }
468
469
        if (!$nonRelevantMessage) { // default non relevant message (if false of null)
470
            $nonRelevantMessage = 'flash_batch_empty';
471
        }
472
473
        $datagrid = $this->admin->getDatagrid();
474
        $datagrid->buildPager();
475
476
        if (true !== $nonRelevantMessage) {
477
            $this->addFlash(
478
                'sonata_flash_info',
479
                $this->trans($nonRelevantMessage, [], 'SonataAdminBundle')
480
            );
481
482
            return new RedirectResponse(
483
                $this->admin->generateUrl(
484
                    'list',
485
                    ['filter' => $this->admin->getFilterParameters()]
486
                )
487
            );
488
        }
489
490
        $askConfirmation = isset($batchActions[$action]['ask_confirmation']) ?
491
            $batchActions[$action]['ask_confirmation'] :
492
            true;
493
494
        if ($askConfirmation && 'ok' != $confirmation) {
495
            $actionLabel = $batchActions[$action]['label'];
496
            $batchTranslationDomain = isset($batchActions[$action]['translation_domain']) ?
497
                $batchActions[$action]['translation_domain'] :
498
                $this->admin->getTranslationDomain();
499
500
            $formView = $datagrid->getForm()->createView();
501
            $this->setFormTheme($formView, $this->admin->getFilterTheme());
0 ignored issues
show
Documentation introduced by
$this->admin->getFilterTheme() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
502
503
            return $this->renderWithExtraParams($this->admin->getTemplate('batch_confirmation'), [
504
                'action' => 'list',
505
                'action_label' => $actionLabel,
506
                'batch_translation_domain' => $batchTranslationDomain,
507
                'datagrid' => $datagrid,
508
                'form' => $formView,
509
                'data' => $data,
510
                'csrf_token' => $this->getCsrfToken('sonata.batch'),
511
            ], null);
512
        }
513
514
        // execute the action, batchActionXxxxx
515
        $finalAction = sprintf('batchAction%s', $camelizedAction);
516
        if (!method_exists($this, $finalAction)) {
517
            throw new \RuntimeException(sprintf('A `%s::%s` method must be callable', get_class($this), $finalAction));
518
        }
519
520
        $query = $datagrid->getQuery();
521
522
        $query->setFirstResult(null);
523
        $query->setMaxResults(null);
524
525
        $this->admin->preBatchAction($action, $query, $idx, $allElements);
526
527
        if (count($idx) > 0) {
528
            $this->admin->getModelManager()->addIdentifiersToQuery($this->admin->getClass(), $query, $idx);
529
        } elseif (!$allElements) {
530
            $this->addFlash(
531
                'sonata_flash_info',
532
                $this->trans('flash_batch_no_elements_processed', [], 'SonataAdminBundle')
533
            );
534
535
            return new RedirectResponse(
536
                $this->admin->generateUrl('list', [
537
                    'filter' => $this->admin->getFilterParameters(),
538
                ])
539
            );
540
        }
541
542
        return call_user_func([$this, $finalAction], $query, $request);
543
    }
544
545
    /**
546
     * Create action.
547
     *
548
     * @return Response
549
     *
550
     * @throws AccessDeniedException If access is not granted
551
     */
552
    public function createAction()
553
    {
554
        $request = $this->getRequest();
555
        // the key used to lookup the template
556
        $templateKey = 'edit';
557
558
        $this->admin->checkAccess('create');
559
560
        $class = new \ReflectionClass($this->admin->hasActiveSubClass() ? $this->admin->getActiveSubClass() : $this->admin->getClass());
561
562
        if ($class->isAbstract()) {
563
            return $this->renderWithExtraParams(
564
                'SonataAdminBundle:CRUD:select_subclass.html.twig',
565
                [
566
                    'base_template' => $this->getBaseTemplate(),
567
                    'admin' => $this->admin,
568
                    'action' => 'create',
569
                ],
570
                null
571
            );
572
        }
573
574
        $newObject = $this->admin->getNewInstance();
575
576
        $preResponse = $this->preCreate($request, $newObject);
577
        if (null !== $preResponse) {
578
            return $preResponse;
579
        }
580
581
        $this->admin->setSubject($newObject);
582
583
        /** @var $form \Symfony\Component\Form\Form */
584
        $form = $this->admin->getForm();
585
        $form->setData($newObject);
586
        $form->handleRequest($request);
587
588
        if ($form->isSubmitted()) {
589
            //TODO: remove this check for 4.0
590
            if (method_exists($this->admin, 'preValidate')) {
591
                $this->admin->preValidate($newObject);
0 ignored issues
show
Bug introduced by
The method preValidate() does not exist on Sonata\AdminBundle\Admin\AdminInterface. Did you maybe mean validate()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
592
            }
593
            $isFormValid = $form->isValid();
594
595
            // persist if the form was valid and if in preview mode the preview was approved
596
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
597
                $submittedObject = $form->getData();
598
                $this->admin->setSubject($submittedObject);
599
                $this->admin->checkAccess('create', $submittedObject);
600
601
                try {
602
                    $newObject = $this->admin->create($submittedObject);
603
604
                    if ($this->isXmlHttpRequest()) {
605
                        return $this->renderJson([
606
                            'result' => 'ok',
607
                            'objectId' => $this->admin->getNormalizedIdentifier($newObject),
608
                        ], 200, []);
609
                    }
610
611
                    $this->addFlash(
612
                        'sonata_flash_success',
613
                        $this->trans(
614
                            'flash_create_success',
615
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
616
                            'SonataAdminBundle'
617
                        )
618
                    );
619
620
                    // redirect to edit mode
621
                    return $this->redirectTo($newObject);
622
                } catch (ModelManagerException $e) {
623
                    $this->handleModelManagerException($e);
624
625
                    $isFormValid = false;
626
                }
627
            }
628
629
            // show an error message if the form failed validation
630
            if (!$isFormValid) {
631
                if (!$this->isXmlHttpRequest()) {
632
                    $this->addFlash(
633
                        'sonata_flash_error',
634
                        $this->trans(
635
                            'flash_create_error',
636
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
637
                            'SonataAdminBundle'
638
                        )
639
                    );
640
                }
641
            } elseif ($this->isPreviewRequested()) {
642
                // pick the preview template if the form was valid and preview was requested
643
                $templateKey = 'preview';
644
                $this->admin->getShow();
645
            }
646
        }
647
648
        $formView = $form->createView();
649
        // set the theme for the current Admin Form
650
        $this->setFormTheme($formView, $this->admin->getFormTheme());
0 ignored issues
show
Documentation introduced by
$this->admin->getFormTheme() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
651
652
        return $this->renderWithExtraParams($this->admin->getTemplate($templateKey), [
653
            'action' => 'create',
654
            'form' => $formView,
655
            'object' => $newObject,
656
            'objectId' => null,
657
        ], null);
658
    }
659
660
    /**
661
     * Show action.
662
     *
663
     * @param int|string|null $id
664
     *
665
     * @return Response
666
     *
667
     * @throws NotFoundHttpException If the object does not exist
668
     * @throws AccessDeniedException If access is not granted
669
     */
670
    public function showAction($id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
671
    {
672
        $request = $this->getRequest();
673
        $id = $request->get($this->admin->getIdParameter());
674
675
        $object = $this->admin->getObject($id);
676
677
        if (!$object) {
678
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
679
        }
680
681
        $this->admin->checkAccess('show', $object);
682
683
        $preResponse = $this->preShow($request, $object);
684
        if (null !== $preResponse) {
685
            return $preResponse;
686
        }
687
688
        $this->admin->setSubject($object);
689
690
        return $this->renderWithExtraParams($this->admin->getTemplate('show'), [
691
            'action' => 'show',
692
            'object' => $object,
693
            'elements' => $this->admin->getShow(),
694
        ], null);
695
    }
696
697
    /**
698
     * Show history revisions for object.
699
     *
700
     * @param int|string|null $id
701
     *
702
     * @return Response
703
     *
704
     * @throws AccessDeniedException If access is not granted
705
     * @throws NotFoundHttpException If the object does not exist or the audit reader is not available
706
     */
707
    public function historyAction($id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
708
    {
709
        $request = $this->getRequest();
710
        $id = $request->get($this->admin->getIdParameter());
711
712
        $object = $this->admin->getObject($id);
713
714
        if (!$object) {
715
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
716
        }
717
718
        $this->admin->checkAccess('history', $object);
719
720
        $manager = $this->get('sonata.admin.audit.manager');
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
721
722
        if (!$manager->hasReader($this->admin->getClass())) {
723
            throw $this->createNotFoundException(
724
                sprintf(
725
                    'unable to find the audit reader for class : %s',
726
                    $this->admin->getClass()
727
                )
728
            );
729
        }
730
731
        $reader = $manager->getReader($this->admin->getClass());
732
733
        $revisions = $reader->findRevisions($this->admin->getClass(), $id);
734
735
        return $this->renderWithExtraParams($this->admin->getTemplate('history'), [
736
            'action' => 'history',
737
            'object' => $object,
738
            'revisions' => $revisions,
739
            'currentRevision' => $revisions ? current($revisions) : false,
740
        ], null);
741
    }
742
743
    /**
744
     * View history revision of object.
745
     *
746
     * @param int|string|null $id
747
     * @param string|null     $revision
748
     *
749
     * @return Response
750
     *
751
     * @throws AccessDeniedException If access is not granted
752
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
753
     */
754
    public function historyViewRevisionAction($id = null, $revision = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
755
    {
756
        $request = $this->getRequest();
757
        $id = $request->get($this->admin->getIdParameter());
758
759
        $object = $this->admin->getObject($id);
760
761
        if (!$object) {
762
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
763
        }
764
765
        $this->admin->checkAccess('historyViewRevision', $object);
766
767
        $manager = $this->get('sonata.admin.audit.manager');
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
768
769
        if (!$manager->hasReader($this->admin->getClass())) {
770
            throw $this->createNotFoundException(
771
                sprintf(
772
                    'unable to find the audit reader for class : %s',
773
                    $this->admin->getClass()
774
                )
775
            );
776
        }
777
778
        $reader = $manager->getReader($this->admin->getClass());
779
780
        // retrieve the revisioned object
781
        $object = $reader->find($this->admin->getClass(), $id, $revision);
782
783
        if (!$object) {
784
            throw $this->createNotFoundException(
785
                sprintf(
786
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
787
                    $id,
788
                    $revision,
789
                    $this->admin->getClass()
790
                )
791
            );
792
        }
793
794
        $this->admin->setSubject($object);
795
796
        return $this->renderWithExtraParams($this->admin->getTemplate('show'), [
797
            'action' => 'show',
798
            'object' => $object,
799
            'elements' => $this->admin->getShow(),
800
        ], null);
801
    }
802
803
    /**
804
     * Compare history revisions of object.
805
     *
806
     * @param int|string|null $id
807
     * @param int|string|null $base_revision
808
     * @param int|string|null $compare_revision
809
     *
810
     * @return Response
811
     *
812
     * @throws AccessDeniedException If access is not granted
813
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
814
     */
815
    public function historyCompareRevisionsAction($id = null, $base_revision = null, $compare_revision = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
816
    {
817
        $request = $this->getRequest();
818
819
        $this->admin->checkAccess('historyCompareRevisions');
820
821
        $id = $request->get($this->admin->getIdParameter());
822
823
        $object = $this->admin->getObject($id);
824
825
        if (!$object) {
826
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
827
        }
828
829
        $manager = $this->get('sonata.admin.audit.manager');
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
830
831
        if (!$manager->hasReader($this->admin->getClass())) {
832
            throw $this->createNotFoundException(
833
                sprintf(
834
                    'unable to find the audit reader for class : %s',
835
                    $this->admin->getClass()
836
                )
837
            );
838
        }
839
840
        $reader = $manager->getReader($this->admin->getClass());
841
842
        // retrieve the base revision
843
        $base_object = $reader->find($this->admin->getClass(), $id, $base_revision);
844
        if (!$base_object) {
845
            throw $this->createNotFoundException(
846
                sprintf(
847
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
848
                    $id,
849
                    $base_revision,
850
                    $this->admin->getClass()
851
                )
852
            );
853
        }
854
855
        // retrieve the compare revision
856
        $compare_object = $reader->find($this->admin->getClass(), $id, $compare_revision);
857
        if (!$compare_object) {
858
            throw $this->createNotFoundException(
859
                sprintf(
860
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
861
                    $id,
862
                    $compare_revision,
863
                    $this->admin->getClass()
864
                )
865
            );
866
        }
867
868
        $this->admin->setSubject($base_object);
869
870
        return $this->renderWithExtraParams($this->admin->getTemplate('show_compare'), [
871
            'action' => 'show',
872
            'object' => $base_object,
873
            'object_compare' => $compare_object,
874
            'elements' => $this->admin->getShow(),
875
        ], null);
876
    }
877
878
    /**
879
     * Export data to specified format.
880
     *
881
     * @param Request $request
882
     *
883
     * @return Response
884
     *
885
     * @throws AccessDeniedException If access is not granted
886
     * @throws \RuntimeException     If the export format is invalid
887
     */
888
    public function exportAction(Request $request)
889
    {
890
        $this->admin->checkAccess('export');
891
892
        $format = $request->get('format');
893
894
        // NEXT_MAJOR: remove the check
895
        if (!$this->has('sonata.admin.admin_exporter')) {
0 ignored issues
show
Documentation Bug introduced by
The method has does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
896
            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
897
                'Not registering the exporter bundle is deprecated since version 3.14.'
898
                .' You must register it to be able to use the export action in 4.0.',
899
                E_USER_DEPRECATED
900
            );
901
            $allowedExportFormats = (array) $this->admin->getExportFormats();
902
903
            $class = $this->admin->getClass();
904
            $filename = sprintf(
905
                'export_%s_%s.%s',
906
                strtolower(substr($class, strripos($class, '\\') + 1)),
907
                date('Y_m_d_H_i_s', strtotime('now')),
908
                $format
909
            );
910
            $exporter = $this->get('sonata.admin.exporter');
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
911
        } else {
912
            $adminExporter = $this->get('sonata.admin.admin_exporter');
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
913
            $allowedExportFormats = $adminExporter->getAvailableFormats($this->admin);
914
            $filename = $adminExporter->getExportFilename($this->admin, $format);
915
            $exporter = $this->get('sonata.exporter.exporter');
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
916
        }
917
918
        if (!in_array($format, $allowedExportFormats)) {
919
            throw new \RuntimeException(
920
                sprintf(
921
                    'Export in format `%s` is not allowed for class: `%s`. Allowed formats are: `%s`',
922
                    $format,
923
                    $this->admin->getClass(),
924
                    implode(', ', $allowedExportFormats)
925
                )
926
            );
927
        }
928
929
        return $exporter->getResponse(
930
            $format,
931
            $filename,
932
            $this->admin->getDataSourceIterator()
933
        );
934
    }
935
936
    /**
937
     * Returns the Response object associated to the acl action.
938
     *
939
     * @param int|string|null $id
940
     *
941
     * @return Response|RedirectResponse
942
     *
943
     * @throws AccessDeniedException If access is not granted
944
     * @throws NotFoundHttpException If the object does not exist or the ACL is not enabled
945
     */
946
    public function aclAction($id = null)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
947
    {
948
        $request = $this->getRequest();
949
950
        if (!$this->admin->isAclEnabled()) {
951
            throw $this->createNotFoundException('ACL are not enabled for this admin');
952
        }
953
954
        $id = $request->get($this->admin->getIdParameter());
955
956
        $object = $this->admin->getObject($id);
957
958
        if (!$object) {
959
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
960
        }
961
962
        $this->admin->checkAccess('acl', $object);
963
964
        $this->admin->setSubject($object);
965
        $aclUsers = $this->getAclUsers();
966
        $aclRoles = $this->getAclRoles();
967
968
        $adminObjectAclManipulator = $this->get('sonata.admin.object.manipulator.acl.admin');
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
969
        $adminObjectAclData = new AdminObjectAclData(
970
            $this->admin,
971
            $object,
972
            $aclUsers,
973
            $adminObjectAclManipulator->getMaskBuilderClass(),
974
            $aclRoles
975
        );
976
977
        $aclUsersForm = $adminObjectAclManipulator->createAclUsersForm($adminObjectAclData);
978
        $aclRolesForm = $adminObjectAclManipulator->createAclRolesForm($adminObjectAclData);
979
980
        if ('POST' === $request->getMethod()) {
981
            if ($request->request->has(AdminObjectAclManipulator::ACL_USERS_FORM_NAME)) {
982
                $form = $aclUsersForm;
983
                $updateMethod = 'updateAclUsers';
984
            } elseif ($request->request->has(AdminObjectAclManipulator::ACL_ROLES_FORM_NAME)) {
985
                $form = $aclRolesForm;
986
                $updateMethod = 'updateAclRoles';
987
            }
988
989
            if (isset($form)) {
990
                $form->handleRequest($request);
991
992
                if ($form->isValid()) {
993
                    $adminObjectAclManipulator->$updateMethod($adminObjectAclData);
0 ignored issues
show
Bug introduced by
The variable $updateMethod does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
994
                    $this->addFlash(
995
                        'sonata_flash_success',
996
                        $this->trans('flash_acl_edit_success', [], 'SonataAdminBundle')
997
                    );
998
999
                    return new RedirectResponse($this->admin->generateObjectUrl('acl', $object));
1000
                }
1001
            }
1002
        }
1003
1004
        return $this->renderWithExtraParams($this->admin->getTemplate('acl'), [
1005
            'action' => 'acl',
1006
            'permissions' => $adminObjectAclData->getUserPermissions(),
1007
            'object' => $object,
1008
            'users' => $aclUsers,
1009
            'roles' => $aclRoles,
1010
            'aclUsersForm' => $aclUsersForm->createView(),
1011
            'aclRolesForm' => $aclRolesForm->createView(),
1012
        ], null);
1013
    }
1014
1015
    /**
1016
     * @return Request
1017
     */
1018
    public function getRequest()
1019
    {
1020
        if ($this->container->has('request_stack')) {
1021
            return $this->container->get('request_stack')->getCurrentRequest();
1022
        }
1023
1024
        return $this->container->get('request');
1025
    }
1026
1027
    /**
1028
     * Gets a container configuration parameter by its name.
1029
     *
1030
     * @param string $name The parameter name
1031
     *
1032
     * @return mixed
1033
     */
1034
    protected function getParameter($name)
1035
    {
1036
        return $this->container->getParameter($name);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Container\ContainerInterface as the method getParameter() does only exist in the following implementations of said interface: Container14\ProjectServiceContainer, ProjectServiceContainer, Symfony\Component\DependencyInjection\Container, Symfony\Component\Depend...ection\ContainerBuilder, Symfony\Component\Depend...tainers\CustomContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony\Component\Depend...ProjectServiceContainer.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1037
    }
1038
1039
    /**
1040
     * Render JSON.
1041
     *
1042
     * @param mixed $data
1043
     * @param int   $status
1044
     * @param array $headers
1045
     *
1046
     * @return Response with json encoded data
1047
     */
1048
    protected function renderJson($data, $status = 200, $headers = [])
1049
    {
1050
        return new JsonResponse($data, $status, $headers);
1051
    }
1052
1053
    /**
1054
     * Returns true if the request is a XMLHttpRequest.
1055
     *
1056
     * @return bool True if the request is an XMLHttpRequest, false otherwise
1057
     */
1058
    protected function isXmlHttpRequest()
1059
    {
1060
        $request = $this->getRequest();
1061
1062
        return $request->isXmlHttpRequest() || $request->get('_xml_http_request');
1063
    }
1064
1065
    /**
1066
     * Returns the correct RESTful verb, given either by the request itself or
1067
     * via the "_method" parameter.
1068
     *
1069
     * @return string HTTP method, either
1070
     */
1071
    protected function getRestMethod()
1072
    {
1073
        $request = $this->getRequest();
1074
1075
        if (Request::getHttpMethodParameterOverride() || !$request->request->has('_method')) {
1076
            return $request->getMethod();
1077
        }
1078
1079
        return $request->request->get('_method');
1080
    }
1081
1082
    /**
1083
     * Contextualize the admin class depends on the current request.
1084
     *
1085
     * @throws \RuntimeException
1086
     */
1087
    protected function configure()
1088
    {
1089
        $request = $this->getRequest();
1090
1091
        $adminCode = $request->get('_sonata_admin');
1092
1093
        if (!$adminCode) {
1094
            throw new \RuntimeException(sprintf(
1095
                'There is no `_sonata_admin` defined for the controller `%s` and the current route `%s`',
1096
                get_class($this),
1097
                $request->get('_route')
1098
            ));
1099
        }
1100
1101
        $this->admin = $this->container->get('sonata.admin.pool')->getAdminByAdminCode($adminCode);
1102
1103
        if (!$this->admin) {
1104
            throw new \RuntimeException(sprintf(
1105
                'Unable to find the admin class related to the current controller (%s)',
1106
                get_class($this)
1107
            ));
1108
        }
1109
1110
        $rootAdmin = $this->admin;
1111
1112
        while ($rootAdmin->isChild()) {
1113
            $rootAdmin->setCurrentChild(true);
1114
            $rootAdmin = $rootAdmin->getParent();
1115
        }
1116
1117
        $rootAdmin->setRequest($request);
1118
1119
        if ($request->get('uniqid')) {
1120
            $this->admin->setUniqid($request->get('uniqid'));
1121
        }
1122
    }
1123
1124
    /**
1125
     * Proxy for the logger service of the container.
1126
     * If no such service is found, a NullLogger is returned.
1127
     *
1128
     * @return LoggerInterface
1129
     */
1130
    protected function getLogger()
1131
    {
1132
        if ($this->container->has('logger')) {
1133
            return $this->container->get('logger');
1134
        }
1135
1136
        return new NullLogger();
1137
    }
1138
1139
    /**
1140
     * Returns the base template name.
1141
     *
1142
     * @return string The template name
1143
     */
1144
    protected function getBaseTemplate()
1145
    {
1146
        if ($this->isXmlHttpRequest()) {
1147
            return $this->admin->getTemplate('ajax');
1148
        }
1149
1150
        return $this->admin->getTemplate('layout');
1151
    }
1152
1153
    /**
1154
     * @param \Exception $e
1155
     *
1156
     * @throws \Exception
1157
     */
1158
    protected function handleModelManagerException(\Exception $e)
1159
    {
1160
        if ($this->get('kernel')->isDebug()) {
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1161
            throw $e;
1162
        }
1163
1164
        $context = ['exception' => $e];
1165
        if ($e->getPrevious()) {
1166
            $context['previous_exception_message'] = $e->getPrevious()->getMessage();
1167
        }
1168
        $this->getLogger()->error($e->getMessage(), $context);
1169
    }
1170
1171
    /**
1172
     * Redirect the user depend on this choice.
1173
     *
1174
     * @param object $object
1175
     *
1176
     * @return RedirectResponse
1177
     */
1178
    protected function redirectTo($object)
1179
    {
1180
        $request = $this->getRequest();
1181
1182
        $url = false;
1183
1184
        if (null !== $request->get('btn_update_and_list')) {
1185
            $url = $this->admin->generateUrl('list');
1186
        }
1187
        if (null !== $request->get('btn_create_and_list')) {
1188
            $url = $this->admin->generateUrl('list');
1189
        }
1190
1191
        if (null !== $request->get('btn_create_and_create')) {
1192
            $params = [];
1193
            if ($this->admin->hasActiveSubClass()) {
1194
                $params['subclass'] = $request->get('subclass');
1195
            }
1196
            $url = $this->admin->generateUrl('create', $params);
1197
        }
1198
1199
        if ('DELETE' === $this->getRestMethod()) {
1200
            $url = $this->admin->generateUrl('list');
1201
        }
1202
1203
        if (!$url) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $url of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false 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...
1204
            foreach (['edit', 'show'] as $route) {
1205
                if ($this->admin->hasRoute($route) && $this->admin->hasAccess($route, $object)) {
1206
                    $url = $this->admin->generateObjectUrl($route, $object);
1207
1208
                    break;
1209
                }
1210
            }
1211
        }
1212
1213
        if (!$url) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $url of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false 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...
1214
            $url = $this->admin->generateUrl('list');
1215
        }
1216
1217
        return new RedirectResponse($url);
1218
    }
1219
1220
    /**
1221
     * Returns true if the preview is requested to be shown.
1222
     *
1223
     * @return bool
1224
     */
1225
    protected function isPreviewRequested()
1226
    {
1227
        $request = $this->getRequest();
1228
1229
        return null !== $request->get('btn_preview');
1230
    }
1231
1232
    /**
1233
     * Returns true if the preview has been approved.
1234
     *
1235
     * @return bool
1236
     */
1237
    protected function isPreviewApproved()
1238
    {
1239
        $request = $this->getRequest();
1240
1241
        return null !== $request->get('btn_preview_approve');
1242
    }
1243
1244
    /**
1245
     * Returns true if the request is in the preview workflow.
1246
     *
1247
     * That means either a preview is requested or the preview has already been shown
1248
     * and it got approved/declined.
1249
     *
1250
     * @return bool
1251
     */
1252
    protected function isInPreviewMode()
1253
    {
1254
        return $this->admin->supportsPreviewMode()
1255
        && ($this->isPreviewRequested()
1256
            || $this->isPreviewApproved()
1257
            || $this->isPreviewDeclined());
1258
    }
1259
1260
    /**
1261
     * Returns true if the preview has been declined.
1262
     *
1263
     * @return bool
1264
     */
1265
    protected function isPreviewDeclined()
1266
    {
1267
        $request = $this->getRequest();
1268
1269
        return null !== $request->get('btn_preview_decline');
1270
    }
1271
1272
    /**
1273
     * Gets ACL users.
1274
     *
1275
     * @return \Traversable
1276
     */
1277
    protected function getAclUsers()
1278
    {
1279
        $aclUsers = [];
1280
1281
        $userManagerServiceName = $this->container->getParameter('sonata.admin.security.acl_user_manager');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Container\ContainerInterface as the method getParameter() does only exist in the following implementations of said interface: Container14\ProjectServiceContainer, ProjectServiceContainer, Symfony\Component\DependencyInjection\Container, Symfony\Component\Depend...ection\ContainerBuilder, Symfony\Component\Depend...tainers\CustomContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony\Component\Depend...ProjectServiceContainer.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1282
        if (null !== $userManagerServiceName && $this->has($userManagerServiceName)) {
0 ignored issues
show
Documentation Bug introduced by
The method has does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1283
            $userManager = $this->get($userManagerServiceName);
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1284
1285
            if (method_exists($userManager, 'findUsers')) {
1286
                $aclUsers = $userManager->findUsers();
1287
            }
1288
        }
1289
1290
        return is_array($aclUsers) ? new \ArrayIterator($aclUsers) : $aclUsers;
1291
    }
1292
1293
    /**
1294
     * Gets ACL roles.
1295
     *
1296
     * @return \Traversable
1297
     */
1298
    protected function getAclRoles()
1299
    {
1300
        $aclRoles = [];
1301
        $roleHierarchy = $this->container->getParameter('security.role_hierarchy.roles');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Psr\Container\ContainerInterface as the method getParameter() does only exist in the following implementations of said interface: Container14\ProjectServiceContainer, ProjectServiceContainer, Symfony\Component\DependencyInjection\Container, Symfony\Component\Depend...ection\ContainerBuilder, Symfony\Component\Depend...tainers\CustomContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony\Component\Depend...ProjectServiceContainer.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1302
        $pool = $this->container->get('sonata.admin.pool');
1303
1304
        foreach ($pool->getAdminServiceIds() as $id) {
1305
            try {
1306
                $admin = $pool->getInstance($id);
1307
            } catch (\Exception $e) {
1308
                continue;
1309
            }
1310
1311
            $baseRole = $admin->getSecurityHandler()->getBaseRole($admin);
1312
            foreach ($admin->getSecurityInformation() as $role => $permissions) {
1313
                $role = sprintf($baseRole, $role);
1314
                $aclRoles[] = $role;
1315
            }
1316
        }
1317
1318
        foreach ($roleHierarchy as $name => $roles) {
1319
            $aclRoles[] = $name;
1320
            $aclRoles = array_merge($aclRoles, $roles);
1321
        }
1322
1323
        $aclRoles = array_unique($aclRoles);
1324
1325
        return is_array($aclRoles) ? new \ArrayIterator($aclRoles) : $aclRoles;
1326
    }
1327
1328
    /**
1329
     * Validate CSRF token for action without form.
1330
     *
1331
     * @param string $intention
1332
     *
1333
     * @throws HttpException
1334
     */
1335
    protected function validateCsrfToken($intention)
1336
    {
1337
        $request = $this->getRequest();
1338
        $token = $request->request->get('_sonata_csrf_token', false);
1339
1340
        if ($this->container->has('security.csrf.token_manager')) { // SF3.0
1341
            $valid = $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($intention, $token));
1342
        } elseif ($this->container->has('form.csrf_provider')) { // < SF3.0
1343
            $valid = $this->container->get('form.csrf_provider')->isCsrfTokenValid($intention, $token);
1344
        } else {
1345
            return;
1346
        }
1347
1348
        if (!$valid) {
1349
            throw new HttpException(400, 'The csrf token is not valid, CSRF attack?');
1350
        }
1351
    }
1352
1353
    /**
1354
     * Escape string for html output.
1355
     *
1356
     * @param string $s
1357
     *
1358
     * @return string
1359
     */
1360
    protected function escapeHtml($s)
1361
    {
1362
        return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
1363
    }
1364
1365
    /**
1366
     * Get CSRF token.
1367
     *
1368
     * @param string $intention
1369
     *
1370
     * @return string|false
1371
     */
1372
    protected function getCsrfToken($intention)
1373
    {
1374
        if ($this->container->has('security.csrf.token_manager')) {
1375
            return $this->container->get('security.csrf.token_manager')->getToken($intention)->getValue();
1376
        }
1377
1378
        // TODO: Remove it when bumping requirements to SF 2.4+
1379
        if ($this->container->has('form.csrf_provider')) {
1380
            return $this->container->get('form.csrf_provider')->generateCsrfToken($intention);
1381
        }
1382
1383
        return false;
1384
    }
1385
1386
    /**
1387
     * This method can be overloaded in your custom CRUD controller.
1388
     * It's called from createAction.
1389
     *
1390
     * @param Request $request
1391
     * @param mixed   $object
1392
     *
1393
     * @return Response|null
1394
     */
1395
    protected function preCreate(Request $request, $object)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1396
    {
1397
    }
1398
1399
    /**
1400
     * This method can be overloaded in your custom CRUD controller.
1401
     * It's called from editAction.
1402
     *
1403
     * @param Request $request
1404
     * @param mixed   $object
1405
     *
1406
     * @return Response|null
1407
     */
1408
    protected function preEdit(Request $request, $object)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1409
    {
1410
    }
1411
1412
    /**
1413
     * This method can be overloaded in your custom CRUD controller.
1414
     * It's called from deleteAction.
1415
     *
1416
     * @param Request $request
1417
     * @param mixed   $object
1418
     *
1419
     * @return Response|null
1420
     */
1421
    protected function preDelete(Request $request, $object)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1422
    {
1423
    }
1424
1425
    /**
1426
     * This method can be overloaded in your custom CRUD controller.
1427
     * It's called from showAction.
1428
     *
1429
     * @param Request $request
1430
     * @param mixed   $object
1431
     *
1432
     * @return Response|null
1433
     */
1434
    protected function preShow(Request $request, $object)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1435
    {
1436
    }
1437
1438
    /**
1439
     * This method can be overloaded in your custom CRUD controller.
1440
     * It's called from listAction.
1441
     *
1442
     * @param Request $request
1443
     *
1444
     * @return Response|null
1445
     */
1446
    protected function preList(Request $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1447
    {
1448
    }
1449
1450
    /**
1451
     * Translate a message id.
1452
     *
1453
     * @param string $id
1454
     * @param array  $parameters
1455
     * @param string $domain
1456
     * @param string $locale
1457
     *
1458
     * @return string translated string
1459
     */
1460
    final protected function trans($id, array $parameters = [], $domain = null, $locale = null)
1461
    {
1462
        $domain = $domain ?: $this->admin->getTranslationDomain();
1463
1464
        return $this->get('translator')->trans($id, $parameters, $domain, $locale);
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1465
    }
1466
1467
    /**
1468
     * Sets the admin form theme to form view. Used for compatibility between Symfony versions.
1469
     *
1470
     * @param FormView $formView
1471
     * @param string   $theme
1472
     */
1473
    private function setFormTheme(FormView $formView, $theme)
1474
    {
1475
        $twig = $this->get('twig');
0 ignored issues
show
Documentation Bug introduced by
The method get does not exist on object<Sonata\AdminBundl...troller\CRUDController>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1476
1477
        // BC for Symfony < 3.2 where this runtime does not exists
1478
        if (!method_exists(AppVariable::class, 'getToken')) {
1479
            $twig->getExtension(FormExtension::class)->renderer->setTheme($formView, $theme);
1480
1481
            return;
1482
        }
1483
1484
        // BC for Symfony < 3.4 where runtime should be TwigRenderer
1485
        if (!method_exists(DebugCommand::class, 'getLoaderPaths')) {
1486
            $twig->getRuntime(TwigRenderer::class)->setTheme($formView, $theme);
1487
1488
            return;
1489
        }
1490
1491
        $twig->getRuntime(FormRenderer::class)->setTheme($formView, $theme);
1492
    }
1493
}
1494