Completed
Push — 3.x ( d52542...0596fd )
by Christian
04:02
created

CRUDController::redirectTo()   C

Complexity

Conditions 11
Paths 29

Size

Total Lines 41
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 41
rs 5.2653
c 0
b 0
f 0
cc 11
eloc 22
nc 29
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 since version 3.27, to be removed in 4.0. Use Sonata\AdminBundle\Controller\CRUDController instead.
103
     */
104
    public function render($view, array $parameters = [], Response $response = null)
105
    {
106
        return $this->renderWithExtraParams($view, $parameters, $response);
107
    }
108
109
    /**
110
     * Renders a view while passing mandatory parameters on to the template.
111
     *
112
     * @param string   $view       The view name
113
     * @param array    $parameters An array of parameters to pass to the view
114
     * @param Response $response   A response instance
115
     *
116
     * @return Response A Response instance
117
     */
118
    public function renderWithExtraParams($view, array $parameters = [], Response $response = null)
119
    {
120
        if (!$this->isXmlHttpRequest()) {
121
            $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...
122
        }
123
        $parameters['admin'] = isset($parameters['admin']) ?
124
            $parameters['admin'] :
125
            $this->admin;
126
127
        $parameters['base_template'] = isset($parameters['base_template']) ?
128
            $parameters['base_template'] :
129
            $this->getBaseTemplate();
130
131
        $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...
132
133
        //NEXT_MAJOR: Remove method alias and use $this->render() directly.
134
        return $this->originalRender($view, $parameters, $response);
135
    }
136
137
    /**
138
     * List action.
139
     *
140
     * @return Response
141
     *
142
     * @throws AccessDeniedException If access is not granted
143
     */
144
    public function listAction()
145
    {
146
        $request = $this->getRequest();
147
148
        $this->admin->checkAccess('list');
149
150
        $preResponse = $this->preList($request);
151
        if (null !== $preResponse) {
152
            return $preResponse;
153
        }
154
155
        if ($listMode = $request->get('_list_mode')) {
156
            $this->admin->setListMode($listMode);
157
        }
158
159
        $datagrid = $this->admin->getDatagrid();
160
        $formView = $datagrid->getForm()->createView();
161
162
        // set the theme for the current Admin Form
163
        $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...
164
165
        return $this->renderWithExtraParams($this->admin->getTemplate('list'), [
166
            'action' => 'list',
167
            'form' => $formView,
168
            'datagrid' => $datagrid,
169
            'csrf_token' => $this->getCsrfToken('sonata.batch'),
170
            '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...
171
                $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...
172
                $this->admin->getExportFormats(),
173
        ], null);
174
    }
175
176
    /**
177
     * Execute a batch delete.
178
     *
179
     * @param ProxyQueryInterface $query
180
     *
181
     * @return RedirectResponse
182
     *
183
     * @throws AccessDeniedException If access is not granted
184
     */
185
    public function batchActionDelete(ProxyQueryInterface $query)
186
    {
187
        $this->admin->checkAccess('batchDelete');
188
189
        $modelManager = $this->admin->getModelManager();
190
191
        try {
192
            $modelManager->batchDelete($this->admin->getClass(), $query);
193
            $this->addFlash(
194
                'sonata_flash_success',
195
                $this->trans('flash_batch_delete_success', [], 'SonataAdminBundle')
196
            );
197
        } catch (ModelManagerException $e) {
198
            $this->handleModelManagerException($e);
199
            $this->addFlash(
200
                'sonata_flash_error',
201
                $this->trans('flash_batch_delete_error', [], 'SonataAdminBundle')
202
            );
203
        }
204
205
        return $this->redirectToList();
206
    }
207
208
    /**
209
     * Delete action.
210
     *
211
     * @param int|string|null $id
212
     *
213
     * @return Response|RedirectResponse
214
     *
215
     * @throws NotFoundHttpException If the object does not exist
216
     * @throws AccessDeniedException If access is not granted
217
     */
218
    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...
219
    {
220
        $request = $this->getRequest();
221
        $id = $request->get($this->admin->getIdParameter());
222
        $object = $this->admin->getObject($id);
223
224
        if (!$object) {
225
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
226
        }
227
228
        $this->admin->checkAccess('delete', $object);
229
230
        $preResponse = $this->preDelete($request, $object);
231
        if (null !== $preResponse) {
232
            return $preResponse;
233
        }
234
235
        if ('DELETE' == $this->getRestMethod()) {
236
            // check the csrf token
237
            $this->validateCsrfToken('sonata.delete');
238
239
            $objectName = $this->admin->toString($object);
240
241
            try {
242
                $this->admin->delete($object);
243
244
                if ($this->isXmlHttpRequest()) {
245
                    return $this->renderJson(['result' => 'ok'], 200, []);
246
                }
247
248
                $this->addFlash(
249
                    'sonata_flash_success',
250
                    $this->trans(
251
                        'flash_delete_success',
252
                        ['%name%' => $this->escapeHtml($objectName)],
253
                        'SonataAdminBundle'
254
                    )
255
                );
256
            } catch (ModelManagerException $e) {
257
                $this->handleModelManagerException($e);
258
259
                if ($this->isXmlHttpRequest()) {
260
                    return $this->renderJson(['result' => 'error'], 200, []);
261
                }
262
263
                $this->addFlash(
264
                    'sonata_flash_error',
265
                    $this->trans(
266
                        'flash_delete_error',
267
                        ['%name%' => $this->escapeHtml($objectName)],
268
                        'SonataAdminBundle'
269
                    )
270
                );
271
            }
272
273
            return $this->redirectTo($object);
274
        }
275
276
        return $this->renderWithExtraParams($this->admin->getTemplate('delete'), [
277
            'object' => $object,
278
            'action' => 'delete',
279
            'csrf_token' => $this->getCsrfToken('sonata.delete'),
280
        ], null);
281
    }
282
283
    /**
284
     * Edit action.
285
     *
286
     * @param int|string|null $id
287
     *
288
     * @return Response|RedirectResponse
289
     *
290
     * @throws NotFoundHttpException If the object does not exist
291
     * @throws AccessDeniedException If access is not granted
292
     */
293
    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...
294
    {
295
        $request = $this->getRequest();
296
        // the key used to lookup the template
297
        $templateKey = 'edit';
298
299
        $id = $request->get($this->admin->getIdParameter());
300
        $existingObject = $this->admin->getObject($id);
301
302
        if (!$existingObject) {
303
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
304
        }
305
306
        $this->admin->checkAccess('edit', $existingObject);
307
308
        $preResponse = $this->preEdit($request, $existingObject);
309
        if (null !== $preResponse) {
310
            return $preResponse;
311
        }
312
313
        $this->admin->setSubject($existingObject);
314
        $objectId = $this->admin->getNormalizedIdentifier($existingObject);
315
316
        /** @var $form Form */
317
        $form = $this->admin->getForm();
318
        $form->setData($existingObject);
319
        $form->handleRequest($request);
320
321
        if ($form->isSubmitted()) {
322
            //TODO: remove this check for 4.0
323
            if (method_exists($this->admin, 'preValidate')) {
324
                $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...
325
            }
326
            $isFormValid = $form->isValid();
327
328
            // persist if the form was valid and if in preview mode the preview was approved
329
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
330
                $submittedObject = $form->getData();
331
                $this->admin->setSubject($submittedObject);
332
333
                try {
334
                    $existingObject = $this->admin->update($submittedObject);
335
336
                    if ($this->isXmlHttpRequest()) {
337
                        return $this->renderJson([
338
                            'result' => 'ok',
339
                            'objectId' => $objectId,
340
                            'objectName' => $this->escapeHtml($this->admin->toString($existingObject)),
341
                        ], 200, []);
342
                    }
343
344
                    $this->addFlash(
345
                        'sonata_flash_success',
346
                        $this->trans(
347
                            'flash_edit_success',
348
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
349
                            'SonataAdminBundle'
350
                        )
351
                    );
352
353
                    // redirect to edit mode
354
                    return $this->redirectTo($existingObject);
355
                } catch (ModelManagerException $e) {
356
                    $this->handleModelManagerException($e);
357
358
                    $isFormValid = false;
359
                } catch (LockException $e) {
360
                    $this->addFlash('sonata_flash_error', $this->trans('flash_lock_error', [
361
                        '%name%' => $this->escapeHtml($this->admin->toString($existingObject)),
362
                        '%link_start%' => '<a href="'.$this->admin->generateObjectUrl('edit', $existingObject).'">',
363
                        '%link_end%' => '</a>',
364
                    ], 'SonataAdminBundle'));
365
                }
366
            }
367
368
            // show an error message if the form failed validation
369
            if (!$isFormValid) {
370
                if (!$this->isXmlHttpRequest()) {
371
                    $this->addFlash(
372
                        'sonata_flash_error',
373
                        $this->trans(
374
                            'flash_edit_error',
375
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
376
                            'SonataAdminBundle'
377
                        )
378
                    );
379
                }
380
            } elseif ($this->isPreviewRequested()) {
381
                // enable the preview template if the form was valid and preview was requested
382
                $templateKey = 'preview';
383
                $this->admin->getShow();
384
            }
385
        }
386
387
        $formView = $form->createView();
388
        // set the theme for the current Admin Form
389
        $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...
390
391
        return $this->renderWithExtraParams($this->admin->getTemplate($templateKey), [
392
            'action' => 'edit',
393
            'form' => $formView,
394
            'object' => $existingObject,
395
            'objectId' => $objectId,
396
        ], null);
397
    }
398
399
    /**
400
     * Batch action.
401
     *
402
     * @return Response|RedirectResponse
403
     *
404
     * @throws NotFoundHttpException If the HTTP method is not POST
405
     * @throws \RuntimeException     If the batch action is not defined
406
     */
407
    public function batchAction()
408
    {
409
        $request = $this->getRequest();
410
        $restMethod = $this->getRestMethod();
411
412
        if ('POST' !== $restMethod) {
413
            throw $this->createNotFoundException(sprintf('Invalid request type "%s", POST expected', $restMethod));
414
        }
415
416
        // check the csrf token
417
        $this->validateCsrfToken('sonata.batch');
418
419
        $confirmation = $request->get('confirmation', false);
420
421
        if ($data = json_decode($request->get('data'), true)) {
422
            $action = $data['action'];
423
            $idx = $data['idx'];
424
            $allElements = $data['all_elements'];
425
            $request->request->replace(array_merge($request->request->all(), $data));
426
        } else {
427
            $request->request->set('idx', $request->get('idx', []));
428
            $request->request->set('all_elements', $request->get('all_elements', false));
429
430
            $action = $request->get('action');
431
            $idx = $request->get('idx');
432
            $allElements = $request->get('all_elements');
433
            $data = $request->request->all();
434
435
            unset($data['_sonata_csrf_token']);
436
        }
437
438
        // NEXT_MAJOR: Remove reflection check.
439
        $reflector = new \ReflectionMethod($this->admin, 'getBatchActions');
440
        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...
441
            @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...
442
                .' is deprecated since version 3.2.'
443
                .' Use Sonata\AdminBundle\Admin\AbstractAdmin::configureBatchActions instead.'
444
                .' The method will be final in 4.0.', E_USER_DEPRECATED
445
            );
446
        }
447
        $batchActions = $this->admin->getBatchActions();
448
        if (!array_key_exists($action, $batchActions)) {
449
            throw new \RuntimeException(sprintf('The `%s` batch action is not defined', $action));
450
        }
451
452
        $camelizedAction = Inflector::classify($action);
453
        $isRelevantAction = sprintf('batchAction%sIsRelevant', $camelizedAction);
454
455
        if (method_exists($this, $isRelevantAction)) {
456
            $nonRelevantMessage = call_user_func([$this, $isRelevantAction], $idx, $allElements, $request);
457
        } else {
458
            $nonRelevantMessage = 0 != count($idx) || $allElements; // at least one item is selected
459
        }
460
461
        if (!$nonRelevantMessage) { // default non relevant message (if false of null)
462
            $nonRelevantMessage = 'flash_batch_empty';
463
        }
464
465
        $datagrid = $this->admin->getDatagrid();
466
        $datagrid->buildPager();
467
468
        if (true !== $nonRelevantMessage) {
469
            $this->addFlash(
470
                'sonata_flash_info',
471
                $this->trans($nonRelevantMessage, [], 'SonataAdminBundle')
472
            );
473
474
            return $this->redirectToList();
475
        }
476
477
        $askConfirmation = isset($batchActions[$action]['ask_confirmation']) ?
478
            $batchActions[$action]['ask_confirmation'] :
479
            true;
480
481
        if ($askConfirmation && 'ok' != $confirmation) {
482
            $actionLabel = $batchActions[$action]['label'];
483
            $batchTranslationDomain = isset($batchActions[$action]['translation_domain']) ?
484
                $batchActions[$action]['translation_domain'] :
485
                $this->admin->getTranslationDomain();
486
487
            $formView = $datagrid->getForm()->createView();
488
            $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...
489
490
            return $this->renderWithExtraParams($this->admin->getTemplate('batch_confirmation'), [
491
                'action' => 'list',
492
                'action_label' => $actionLabel,
493
                'batch_translation_domain' => $batchTranslationDomain,
494
                'datagrid' => $datagrid,
495
                'form' => $formView,
496
                'data' => $data,
497
                'csrf_token' => $this->getCsrfToken('sonata.batch'),
498
            ], null);
499
        }
500
501
        // execute the action, batchActionXxxxx
502
        $finalAction = sprintf('batchAction%s', $camelizedAction);
503
        if (!method_exists($this, $finalAction)) {
504
            throw new \RuntimeException(sprintf('A `%s::%s` method must be callable', get_class($this), $finalAction));
505
        }
506
507
        $query = $datagrid->getQuery();
508
509
        $query->setFirstResult(null);
510
        $query->setMaxResults(null);
511
512
        $this->admin->preBatchAction($action, $query, $idx, $allElements);
513
514
        if (count($idx) > 0) {
515
            $this->admin->getModelManager()->addIdentifiersToQuery($this->admin->getClass(), $query, $idx);
516
        } elseif (!$allElements) {
517
            $this->addFlash(
518
                'sonata_flash_info',
519
                $this->trans('flash_batch_no_elements_processed', [], 'SonataAdminBundle')
520
            );
521
522
            return $this->redirectToList();
523
        }
524
525
        return call_user_func([$this, $finalAction], $query, $request);
526
    }
527
528
    /**
529
     * Create action.
530
     *
531
     * @return Response
532
     *
533
     * @throws AccessDeniedException If access is not granted
534
     */
535
    public function createAction()
536
    {
537
        $request = $this->getRequest();
538
        // the key used to lookup the template
539
        $templateKey = 'edit';
540
541
        $this->admin->checkAccess('create');
542
543
        $class = new \ReflectionClass($this->admin->hasActiveSubClass() ? $this->admin->getActiveSubClass() : $this->admin->getClass());
544
545
        if ($class->isAbstract()) {
546
            return $this->renderWithExtraParams(
547
                'SonataAdminBundle:CRUD:select_subclass.html.twig',
548
                [
549
                    'base_template' => $this->getBaseTemplate(),
550
                    'admin' => $this->admin,
551
                    'action' => 'create',
552
                ],
553
                null
554
            );
555
        }
556
557
        $newObject = $this->admin->getNewInstance();
558
559
        $preResponse = $this->preCreate($request, $newObject);
560
        if (null !== $preResponse) {
561
            return $preResponse;
562
        }
563
564
        $this->admin->setSubject($newObject);
565
566
        /** @var $form \Symfony\Component\Form\Form */
567
        $form = $this->admin->getForm();
568
        $form->setData($newObject);
569
        $form->handleRequest($request);
570
571
        if ($form->isSubmitted()) {
572
            //TODO: remove this check for 4.0
573
            if (method_exists($this->admin, 'preValidate')) {
574
                $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...
575
            }
576
            $isFormValid = $form->isValid();
577
578
            // persist if the form was valid and if in preview mode the preview was approved
579
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
580
                $submittedObject = $form->getData();
581
                $this->admin->setSubject($submittedObject);
582
                $this->admin->checkAccess('create', $submittedObject);
583
584
                try {
585
                    $newObject = $this->admin->create($submittedObject);
586
587
                    if ($this->isXmlHttpRequest()) {
588
                        return $this->renderJson([
589
                            'result' => 'ok',
590
                            'objectId' => $this->admin->getNormalizedIdentifier($newObject),
591
                        ], 200, []);
592
                    }
593
594
                    $this->addFlash(
595
                        'sonata_flash_success',
596
                        $this->trans(
597
                            'flash_create_success',
598
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
599
                            'SonataAdminBundle'
600
                        )
601
                    );
602
603
                    // redirect to edit mode
604
                    return $this->redirectTo($newObject);
605
                } catch (ModelManagerException $e) {
606
                    $this->handleModelManagerException($e);
607
608
                    $isFormValid = false;
609
                }
610
            }
611
612
            // show an error message if the form failed validation
613
            if (!$isFormValid) {
614
                if (!$this->isXmlHttpRequest()) {
615
                    $this->addFlash(
616
                        'sonata_flash_error',
617
                        $this->trans(
618
                            'flash_create_error',
619
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
620
                            'SonataAdminBundle'
621
                        )
622
                    );
623
                }
624
            } elseif ($this->isPreviewRequested()) {
625
                // pick the preview template if the form was valid and preview was requested
626
                $templateKey = 'preview';
627
                $this->admin->getShow();
628
            }
629
        }
630
631
        $formView = $form->createView();
632
        // set the theme for the current Admin Form
633
        $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...
634
635
        return $this->renderWithExtraParams($this->admin->getTemplate($templateKey), [
636
            'action' => 'create',
637
            'form' => $formView,
638
            'object' => $newObject,
639
            'objectId' => null,
640
        ], null);
641
    }
642
643
    /**
644
     * Show action.
645
     *
646
     * @param int|string|null $id
647
     *
648
     * @return Response
649
     *
650
     * @throws NotFoundHttpException If the object does not exist
651
     * @throws AccessDeniedException If access is not granted
652
     */
653
    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...
654
    {
655
        $request = $this->getRequest();
656
        $id = $request->get($this->admin->getIdParameter());
657
658
        $object = $this->admin->getObject($id);
659
660
        if (!$object) {
661
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
662
        }
663
664
        $this->admin->checkAccess('show', $object);
665
666
        $preResponse = $this->preShow($request, $object);
667
        if (null !== $preResponse) {
668
            return $preResponse;
669
        }
670
671
        $this->admin->setSubject($object);
672
673
        return $this->renderWithExtraParams($this->admin->getTemplate('show'), [
674
            'action' => 'show',
675
            'object' => $object,
676
            'elements' => $this->admin->getShow(),
677
        ], null);
678
    }
679
680
    /**
681
     * Show history revisions for object.
682
     *
683
     * @param int|string|null $id
684
     *
685
     * @return Response
686
     *
687
     * @throws AccessDeniedException If access is not granted
688
     * @throws NotFoundHttpException If the object does not exist or the audit reader is not available
689
     */
690
    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...
691
    {
692
        $request = $this->getRequest();
693
        $id = $request->get($this->admin->getIdParameter());
694
695
        $object = $this->admin->getObject($id);
696
697
        if (!$object) {
698
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
699
        }
700
701
        $this->admin->checkAccess('history', $object);
702
703
        $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...
704
705
        if (!$manager->hasReader($this->admin->getClass())) {
706
            throw $this->createNotFoundException(
707
                sprintf(
708
                    'unable to find the audit reader for class : %s',
709
                    $this->admin->getClass()
710
                )
711
            );
712
        }
713
714
        $reader = $manager->getReader($this->admin->getClass());
715
716
        $revisions = $reader->findRevisions($this->admin->getClass(), $id);
717
718
        return $this->renderWithExtraParams($this->admin->getTemplate('history'), [
719
            'action' => 'history',
720
            'object' => $object,
721
            'revisions' => $revisions,
722
            'currentRevision' => $revisions ? current($revisions) : false,
723
        ], null);
724
    }
725
726
    /**
727
     * View history revision of object.
728
     *
729
     * @param int|string|null $id
730
     * @param string|null     $revision
731
     *
732
     * @return Response
733
     *
734
     * @throws AccessDeniedException If access is not granted
735
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
736
     */
737
    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...
738
    {
739
        $request = $this->getRequest();
740
        $id = $request->get($this->admin->getIdParameter());
741
742
        $object = $this->admin->getObject($id);
743
744
        if (!$object) {
745
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
746
        }
747
748
        $this->admin->checkAccess('historyViewRevision', $object);
749
750
        $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...
751
752
        if (!$manager->hasReader($this->admin->getClass())) {
753
            throw $this->createNotFoundException(
754
                sprintf(
755
                    'unable to find the audit reader for class : %s',
756
                    $this->admin->getClass()
757
                )
758
            );
759
        }
760
761
        $reader = $manager->getReader($this->admin->getClass());
762
763
        // retrieve the revisioned object
764
        $object = $reader->find($this->admin->getClass(), $id, $revision);
765
766
        if (!$object) {
767
            throw $this->createNotFoundException(
768
                sprintf(
769
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
770
                    $id,
771
                    $revision,
772
                    $this->admin->getClass()
773
                )
774
            );
775
        }
776
777
        $this->admin->setSubject($object);
778
779
        return $this->renderWithExtraParams($this->admin->getTemplate('show'), [
780
            'action' => 'show',
781
            'object' => $object,
782
            'elements' => $this->admin->getShow(),
783
        ], null);
784
    }
785
786
    /**
787
     * Compare history revisions of object.
788
     *
789
     * @param int|string|null $id
790
     * @param int|string|null $base_revision
791
     * @param int|string|null $compare_revision
792
     *
793
     * @return Response
794
     *
795
     * @throws AccessDeniedException If access is not granted
796
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
797
     */
798
    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...
799
    {
800
        $request = $this->getRequest();
801
802
        $this->admin->checkAccess('historyCompareRevisions');
803
804
        $id = $request->get($this->admin->getIdParameter());
805
806
        $object = $this->admin->getObject($id);
807
808
        if (!$object) {
809
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
810
        }
811
812
        $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...
813
814
        if (!$manager->hasReader($this->admin->getClass())) {
815
            throw $this->createNotFoundException(
816
                sprintf(
817
                    'unable to find the audit reader for class : %s',
818
                    $this->admin->getClass()
819
                )
820
            );
821
        }
822
823
        $reader = $manager->getReader($this->admin->getClass());
824
825
        // retrieve the base revision
826
        $base_object = $reader->find($this->admin->getClass(), $id, $base_revision);
827
        if (!$base_object) {
828
            throw $this->createNotFoundException(
829
                sprintf(
830
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
831
                    $id,
832
                    $base_revision,
833
                    $this->admin->getClass()
834
                )
835
            );
836
        }
837
838
        // retrieve the compare revision
839
        $compare_object = $reader->find($this->admin->getClass(), $id, $compare_revision);
840
        if (!$compare_object) {
841
            throw $this->createNotFoundException(
842
                sprintf(
843
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
844
                    $id,
845
                    $compare_revision,
846
                    $this->admin->getClass()
847
                )
848
            );
849
        }
850
851
        $this->admin->setSubject($base_object);
852
853
        return $this->renderWithExtraParams($this->admin->getTemplate('show_compare'), [
854
            'action' => 'show',
855
            'object' => $base_object,
856
            'object_compare' => $compare_object,
857
            'elements' => $this->admin->getShow(),
858
        ], null);
859
    }
860
861
    /**
862
     * Export data to specified format.
863
     *
864
     * @param Request $request
865
     *
866
     * @return Response
867
     *
868
     * @throws AccessDeniedException If access is not granted
869
     * @throws \RuntimeException     If the export format is invalid
870
     */
871
    public function exportAction(Request $request)
872
    {
873
        $this->admin->checkAccess('export');
874
875
        $format = $request->get('format');
876
877
        // NEXT_MAJOR: remove the check
878
        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...
879
            @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...
880
                'Not registering the exporter bundle is deprecated since version 3.14.'
881
                .' You must register it to be able to use the export action in 4.0.',
882
                E_USER_DEPRECATED
883
            );
884
            $allowedExportFormats = (array) $this->admin->getExportFormats();
885
886
            $class = $this->admin->getClass();
887
            $filename = sprintf(
888
                'export_%s_%s.%s',
889
                strtolower(substr($class, strripos($class, '\\') + 1)),
890
                date('Y_m_d_H_i_s', strtotime('now')),
891
                $format
892
            );
893
            $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...
894
        } else {
895
            $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...
896
            $allowedExportFormats = $adminExporter->getAvailableFormats($this->admin);
897
            $filename = $adminExporter->getExportFilename($this->admin, $format);
898
            $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...
899
        }
900
901
        if (!in_array($format, $allowedExportFormats)) {
902
            throw new \RuntimeException(
903
                sprintf(
904
                    'Export in format `%s` is not allowed for class: `%s`. Allowed formats are: `%s`',
905
                    $format,
906
                    $this->admin->getClass(),
907
                    implode(', ', $allowedExportFormats)
908
                )
909
            );
910
        }
911
912
        return $exporter->getResponse(
913
            $format,
914
            $filename,
915
            $this->admin->getDataSourceIterator()
916
        );
917
    }
918
919
    /**
920
     * Returns the Response object associated to the acl action.
921
     *
922
     * @param int|string|null $id
923
     *
924
     * @return Response|RedirectResponse
925
     *
926
     * @throws AccessDeniedException If access is not granted
927
     * @throws NotFoundHttpException If the object does not exist or the ACL is not enabled
928
     */
929
    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...
930
    {
931
        $request = $this->getRequest();
932
933
        if (!$this->admin->isAclEnabled()) {
934
            throw $this->createNotFoundException('ACL are not enabled for this admin');
935
        }
936
937
        $id = $request->get($this->admin->getIdParameter());
938
939
        $object = $this->admin->getObject($id);
940
941
        if (!$object) {
942
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
943
        }
944
945
        $this->admin->checkAccess('acl', $object);
946
947
        $this->admin->setSubject($object);
948
        $aclUsers = $this->getAclUsers();
949
        $aclRoles = $this->getAclRoles();
950
951
        $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...
952
        $adminObjectAclData = new AdminObjectAclData(
953
            $this->admin,
954
            $object,
955
            $aclUsers,
956
            $adminObjectAclManipulator->getMaskBuilderClass(),
957
            $aclRoles
958
        );
959
960
        $aclUsersForm = $adminObjectAclManipulator->createAclUsersForm($adminObjectAclData);
961
        $aclRolesForm = $adminObjectAclManipulator->createAclRolesForm($adminObjectAclData);
962
963
        if ('POST' === $request->getMethod()) {
964
            if ($request->request->has(AdminObjectAclManipulator::ACL_USERS_FORM_NAME)) {
965
                $form = $aclUsersForm;
966
                $updateMethod = 'updateAclUsers';
967
            } elseif ($request->request->has(AdminObjectAclManipulator::ACL_ROLES_FORM_NAME)) {
968
                $form = $aclRolesForm;
969
                $updateMethod = 'updateAclRoles';
970
            }
971
972
            if (isset($form)) {
973
                $form->handleRequest($request);
974
975
                if ($form->isValid()) {
976
                    $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...
977
                    $this->addFlash(
978
                        'sonata_flash_success',
979
                        $this->trans('flash_acl_edit_success', [], 'SonataAdminBundle')
980
                    );
981
982
                    return new RedirectResponse($this->admin->generateObjectUrl('acl', $object));
983
                }
984
            }
985
        }
986
987
        return $this->renderWithExtraParams($this->admin->getTemplate('acl'), [
988
            'action' => 'acl',
989
            'permissions' => $adminObjectAclData->getUserPermissions(),
990
            'object' => $object,
991
            'users' => $aclUsers,
992
            'roles' => $aclRoles,
993
            'aclUsersForm' => $aclUsersForm->createView(),
994
            'aclRolesForm' => $aclRolesForm->createView(),
995
        ], null);
996
    }
997
998
    /**
999
     * @return Request
1000
     */
1001
    public function getRequest()
1002
    {
1003
        if ($this->container->has('request_stack')) {
1004
            return $this->container->get('request_stack')->getCurrentRequest();
1005
        }
1006
1007
        return $this->container->get('request');
1008
    }
1009
1010
    /**
1011
     * Gets a container configuration parameter by its name.
1012
     *
1013
     * @param string $name The parameter name
1014
     *
1015
     * @return mixed
1016
     */
1017
    protected function getParameter($name)
1018
    {
1019
        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...
1020
    }
1021
1022
    /**
1023
     * Render JSON.
1024
     *
1025
     * @param mixed $data
1026
     * @param int   $status
1027
     * @param array $headers
1028
     *
1029
     * @return Response with json encoded data
1030
     */
1031
    protected function renderJson($data, $status = 200, $headers = [])
1032
    {
1033
        return new JsonResponse($data, $status, $headers);
1034
    }
1035
1036
    /**
1037
     * Returns true if the request is a XMLHttpRequest.
1038
     *
1039
     * @return bool True if the request is an XMLHttpRequest, false otherwise
1040
     */
1041
    protected function isXmlHttpRequest()
1042
    {
1043
        $request = $this->getRequest();
1044
1045
        return $request->isXmlHttpRequest() || $request->get('_xml_http_request');
1046
    }
1047
1048
    /**
1049
     * Returns the correct RESTful verb, given either by the request itself or
1050
     * via the "_method" parameter.
1051
     *
1052
     * @return string HTTP method, either
1053
     */
1054
    protected function getRestMethod()
1055
    {
1056
        $request = $this->getRequest();
1057
1058
        if (Request::getHttpMethodParameterOverride() || !$request->request->has('_method')) {
1059
            return $request->getMethod();
1060
        }
1061
1062
        return $request->request->get('_method');
1063
    }
1064
1065
    /**
1066
     * Contextualize the admin class depends on the current request.
1067
     *
1068
     * @throws \RuntimeException
1069
     */
1070
    protected function configure()
1071
    {
1072
        $request = $this->getRequest();
1073
1074
        $adminCode = $request->get('_sonata_admin');
1075
1076
        if (!$adminCode) {
1077
            throw new \RuntimeException(sprintf(
1078
                'There is no `_sonata_admin` defined for the controller `%s` and the current route `%s`',
1079
                get_class($this),
1080
                $request->get('_route')
1081
            ));
1082
        }
1083
1084
        $this->admin = $this->container->get('sonata.admin.pool')->getAdminByAdminCode($adminCode);
1085
1086
        if (!$this->admin) {
1087
            throw new \RuntimeException(sprintf(
1088
                'Unable to find the admin class related to the current controller (%s)',
1089
                get_class($this)
1090
            ));
1091
        }
1092
1093
        $rootAdmin = $this->admin;
1094
1095
        while ($rootAdmin->isChild()) {
1096
            $rootAdmin->setCurrentChild(true);
1097
            $rootAdmin = $rootAdmin->getParent();
1098
        }
1099
1100
        $rootAdmin->setRequest($request);
1101
1102
        if ($request->get('uniqid')) {
1103
            $this->admin->setUniqid($request->get('uniqid'));
1104
        }
1105
    }
1106
1107
    /**
1108
     * Proxy for the logger service of the container.
1109
     * If no such service is found, a NullLogger is returned.
1110
     *
1111
     * @return LoggerInterface
1112
     */
1113
    protected function getLogger()
1114
    {
1115
        if ($this->container->has('logger')) {
1116
            return $this->container->get('logger');
1117
        }
1118
1119
        return new NullLogger();
1120
    }
1121
1122
    /**
1123
     * Returns the base template name.
1124
     *
1125
     * @return string The template name
1126
     */
1127
    protected function getBaseTemplate()
1128
    {
1129
        if ($this->isXmlHttpRequest()) {
1130
            return $this->admin->getTemplate('ajax');
1131
        }
1132
1133
        return $this->admin->getTemplate('layout');
1134
    }
1135
1136
    /**
1137
     * @param \Exception $e
1138
     *
1139
     * @throws \Exception
1140
     */
1141
    protected function handleModelManagerException(\Exception $e)
1142
    {
1143
        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...
1144
            throw $e;
1145
        }
1146
1147
        $context = ['exception' => $e];
1148
        if ($e->getPrevious()) {
1149
            $context['previous_exception_message'] = $e->getPrevious()->getMessage();
1150
        }
1151
        $this->getLogger()->error($e->getMessage(), $context);
1152
    }
1153
1154
    /**
1155
     * Redirect the user depend on this choice.
1156
     *
1157
     * @param object $object
1158
     *
1159
     * @return RedirectResponse
1160
     */
1161
    protected function redirectTo($object)
1162
    {
1163
        $request = $this->getRequest();
1164
1165
        $url = false;
1166
1167
        if (null !== $request->get('btn_update_and_list')) {
1168
            return $this->redirectToList();
1169
        }
1170
        if (null !== $request->get('btn_create_and_list')) {
1171
            return $this->redirectToList();
1172
        }
1173
1174
        if (null !== $request->get('btn_create_and_create')) {
1175
            $params = [];
1176
            if ($this->admin->hasActiveSubClass()) {
1177
                $params['subclass'] = $request->get('subclass');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $params['subclass'] is correct as $request->get('subclass') (which targets Symfony\Component\HttpFoundation\Request::get()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1178
            }
1179
            $url = $this->admin->generateUrl('create', $params);
1180
        }
1181
1182
        if ('DELETE' === $this->getRestMethod()) {
1183
            return $this->redirectToList();
1184
        }
1185
1186
        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...
1187
            foreach (['edit', 'show'] as $route) {
1188
                if ($this->admin->hasRoute($route) && $this->admin->hasAccess($route, $object)) {
1189
                    $url = $this->admin->generateObjectUrl($route, $object);
1190
1191
                    break;
1192
                }
1193
            }
1194
        }
1195
1196
        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...
1197
            return $this->redirectToList();
1198
        }
1199
1200
        return new RedirectResponse($url);
1201
    }
1202
1203
    /**
1204
     * Redirects the user to the list view.
1205
     *
1206
     * @return RedirectResponse
1207
     */
1208
    final protected function redirectToList()
1209
    {
1210
        $parameters = [];
1211
1212
        if ($filter = $this->admin->getFilterParameters()) {
1213
            $parameters['filter'] = $filter;
1214
        }
1215
1216
        return $this->redirect($this->admin->generateUrl('list', $parameters));
1217
    }
1218
1219
    /**
1220
     * Returns true if the preview is requested to be shown.
1221
     *
1222
     * @return bool
1223
     */
1224
    protected function isPreviewRequested()
1225
    {
1226
        $request = $this->getRequest();
1227
1228
        return null !== $request->get('btn_preview');
1229
    }
1230
1231
    /**
1232
     * Returns true if the preview has been approved.
1233
     *
1234
     * @return bool
1235
     */
1236
    protected function isPreviewApproved()
1237
    {
1238
        $request = $this->getRequest();
1239
1240
        return null !== $request->get('btn_preview_approve');
1241
    }
1242
1243
    /**
1244
     * Returns true if the request is in the preview workflow.
1245
     *
1246
     * That means either a preview is requested or the preview has already been shown
1247
     * and it got approved/declined.
1248
     *
1249
     * @return bool
1250
     */
1251
    protected function isInPreviewMode()
1252
    {
1253
        return $this->admin->supportsPreviewMode()
1254
        && ($this->isPreviewRequested()
1255
            || $this->isPreviewApproved()
1256
            || $this->isPreviewDeclined());
1257
    }
1258
1259
    /**
1260
     * Returns true if the preview has been declined.
1261
     *
1262
     * @return bool
1263
     */
1264
    protected function isPreviewDeclined()
1265
    {
1266
        $request = $this->getRequest();
1267
1268
        return null !== $request->get('btn_preview_decline');
1269
    }
1270
1271
    /**
1272
     * Gets ACL users.
1273
     *
1274
     * @return \Traversable
1275
     */
1276
    protected function getAclUsers()
1277
    {
1278
        $aclUsers = [];
1279
1280
        $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...
1281
        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...
1282
            $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...
1283
1284
            if (method_exists($userManager, 'findUsers')) {
1285
                $aclUsers = $userManager->findUsers();
1286
            }
1287
        }
1288
1289
        return is_array($aclUsers) ? new \ArrayIterator($aclUsers) : $aclUsers;
1290
    }
1291
1292
    /**
1293
     * Gets ACL roles.
1294
     *
1295
     * @return \Traversable
1296
     */
1297
    protected function getAclRoles()
1298
    {
1299
        $aclRoles = [];
1300
        $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...
1301
        $pool = $this->container->get('sonata.admin.pool');
1302
1303
        foreach ($pool->getAdminServiceIds() as $id) {
1304
            try {
1305
                $admin = $pool->getInstance($id);
1306
            } catch (\Exception $e) {
1307
                continue;
1308
            }
1309
1310
            $baseRole = $admin->getSecurityHandler()->getBaseRole($admin);
1311
            foreach ($admin->getSecurityInformation() as $role => $permissions) {
1312
                $role = sprintf($baseRole, $role);
1313
                $aclRoles[] = $role;
1314
            }
1315
        }
1316
1317
        foreach ($roleHierarchy as $name => $roles) {
1318
            $aclRoles[] = $name;
1319
            $aclRoles = array_merge($aclRoles, $roles);
1320
        }
1321
1322
        $aclRoles = array_unique($aclRoles);
1323
1324
        return is_array($aclRoles) ? new \ArrayIterator($aclRoles) : $aclRoles;
1325
    }
1326
1327
    /**
1328
     * Validate CSRF token for action without form.
1329
     *
1330
     * @param string $intention
1331
     *
1332
     * @throws HttpException
1333
     */
1334
    protected function validateCsrfToken($intention)
1335
    {
1336
        $request = $this->getRequest();
1337
        $token = $request->request->get('_sonata_csrf_token', false);
1338
1339
        if ($this->container->has('security.csrf.token_manager')) { // SF3.0
1340
            $valid = $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($intention, $token));
1341
        } elseif ($this->container->has('form.csrf_provider')) { // < SF3.0
1342
            $valid = $this->container->get('form.csrf_provider')->isCsrfTokenValid($intention, $token);
1343
        } else {
1344
            return;
1345
        }
1346
1347
        if (!$valid) {
1348
            throw new HttpException(400, 'The csrf token is not valid, CSRF attack?');
1349
        }
1350
    }
1351
1352
    /**
1353
     * Escape string for html output.
1354
     *
1355
     * @param string $s
1356
     *
1357
     * @return string
1358
     */
1359
    protected function escapeHtml($s)
1360
    {
1361
        return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
1362
    }
1363
1364
    /**
1365
     * Get CSRF token.
1366
     *
1367
     * @param string $intention
1368
     *
1369
     * @return string|false
1370
     */
1371
    protected function getCsrfToken($intention)
1372
    {
1373
        if ($this->container->has('security.csrf.token_manager')) {
1374
            return $this->container->get('security.csrf.token_manager')->getToken($intention)->getValue();
1375
        }
1376
1377
        // TODO: Remove it when bumping requirements to SF 2.4+
1378
        if ($this->container->has('form.csrf_provider')) {
1379
            return $this->container->get('form.csrf_provider')->generateCsrfToken($intention);
1380
        }
1381
1382
        return false;
1383
    }
1384
1385
    /**
1386
     * This method can be overloaded in your custom CRUD controller.
1387
     * It's called from createAction.
1388
     *
1389
     * @param Request $request
1390
     * @param mixed   $object
1391
     *
1392
     * @return Response|null
1393
     */
1394
    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...
1395
    {
1396
    }
1397
1398
    /**
1399
     * This method can be overloaded in your custom CRUD controller.
1400
     * It's called from editAction.
1401
     *
1402
     * @param Request $request
1403
     * @param mixed   $object
1404
     *
1405
     * @return Response|null
1406
     */
1407
    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...
1408
    {
1409
    }
1410
1411
    /**
1412
     * This method can be overloaded in your custom CRUD controller.
1413
     * It's called from deleteAction.
1414
     *
1415
     * @param Request $request
1416
     * @param mixed   $object
1417
     *
1418
     * @return Response|null
1419
     */
1420
    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...
1421
    {
1422
    }
1423
1424
    /**
1425
     * This method can be overloaded in your custom CRUD controller.
1426
     * It's called from showAction.
1427
     *
1428
     * @param Request $request
1429
     * @param mixed   $object
1430
     *
1431
     * @return Response|null
1432
     */
1433
    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...
1434
    {
1435
    }
1436
1437
    /**
1438
     * This method can be overloaded in your custom CRUD controller.
1439
     * It's called from listAction.
1440
     *
1441
     * @param Request $request
1442
     *
1443
     * @return Response|null
1444
     */
1445
    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...
1446
    {
1447
    }
1448
1449
    /**
1450
     * Translate a message id.
1451
     *
1452
     * @param string $id
1453
     * @param array  $parameters
1454
     * @param string $domain
1455
     * @param string $locale
1456
     *
1457
     * @return string translated string
1458
     */
1459
    final protected function trans($id, array $parameters = [], $domain = null, $locale = null)
1460
    {
1461
        $domain = $domain ?: $this->admin->getTranslationDomain();
1462
1463
        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...
1464
    }
1465
1466
    /**
1467
     * Sets the admin form theme to form view. Used for compatibility between Symfony versions.
1468
     *
1469
     * @param FormView $formView
1470
     * @param string   $theme
1471
     */
1472
    private function setFormTheme(FormView $formView, $theme)
1473
    {
1474
        $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...
1475
1476
        // BC for Symfony < 3.2 where this runtime does not exists
1477
        if (!method_exists(AppVariable::class, 'getToken')) {
1478
            $twig->getExtension(FormExtension::class)->renderer->setTheme($formView, $theme);
1479
1480
            return;
1481
        }
1482
1483
        // BC for Symfony < 3.4 where runtime should be TwigRenderer
1484
        if (!method_exists(DebugCommand::class, 'getLoaderPaths')) {
1485
            $twig->getRuntime(TwigRenderer::class)->setTheme($formView, $theme);
1486
1487
            return;
1488
        }
1489
1490
        $twig->getRuntime(FormRenderer::class)->setTheme($formView, $theme);
1491
    }
1492
}
1493