Completed
Push — 3.x ( bef89f...9ae3e5 )
by Marko
04:09
created

CRUDController::redirectTo()   B

Complexity

Conditions 11
Paths 29

Size

Total Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 45
rs 7.3166
c 0
b 0
f 0
cc 11
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 57 and the first side effect is on line 51.

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
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Controller;
15
16
use Doctrine\Common\Inflector\Inflector;
17
use Psr\Log\LoggerInterface;
18
use Psr\Log\NullLogger;
19
use Sonata\AdminBundle\Admin\AdminInterface;
20
use Sonata\AdminBundle\Admin\FieldDescriptionCollection;
21
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
22
use Sonata\AdminBundle\Exception\LockException;
23
use Sonata\AdminBundle\Exception\ModelManagerException;
24
use Sonata\AdminBundle\Templating\TemplateRegistryInterface;
25
use Sonata\AdminBundle\Util\AdminObjectAclData;
26
use Sonata\AdminBundle\Util\AdminObjectAclManipulator;
27
use Symfony\Bridge\Twig\AppVariable;
28
use Symfony\Bridge\Twig\Command\DebugCommand;
29
use Symfony\Bridge\Twig\Extension\FormExtension;
30
use Symfony\Bridge\Twig\Form\TwigRenderer;
31
use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
32
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
33
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
34
use Symfony\Component\DependencyInjection\ContainerInterface;
35
use Symfony\Component\Form\FormRenderer;
36
use Symfony\Component\Form\FormView;
37
use Symfony\Component\HttpFoundation\JsonResponse;
38
use Symfony\Component\HttpFoundation\RedirectResponse;
39
use Symfony\Component\HttpFoundation\Request;
40
use Symfony\Component\HttpFoundation\Response;
41
use Symfony\Component\HttpKernel\Exception\HttpException;
42
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
43
use Symfony\Component\PropertyAccess\PropertyAccess;
44
use Symfony\Component\PropertyAccess\PropertyPath;
45
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
46
use Symfony\Component\Security\Csrf\CsrfToken;
47
48
// BC for Symfony < 3.3 where this trait does not exist
49
// NEXT_MAJOR: Remove the polyfill and inherit from \Symfony\Bundle\FrameworkBundle\Controller\Controller again
50
if (!trait_exists(ControllerTrait::class)) {
51
    require_once __DIR__.'/PolyfillControllerTrait.php';
52
}
53
54
/**
55
 * @author Thomas Rabaix <[email protected]>
56
 */
57
class CRUDController implements ContainerAwareInterface
58
{
59
    // NEXT_MAJOR: Don't use these traits anymore (inherit from Controller instead)
60
    use ControllerTrait, ContainerAwareTrait {
61
        ControllerTrait::render as originalRender;
62
    }
63
64
    /**
65
     * The related Admin class.
66
     *
67
     * @var AdminInterface
68
     */
69
    protected $admin;
70
71
    /**
72
     * The template registry of the related Admin class.
73
     *
74
     * @var TemplateRegistryInterface
75
     */
76
    private $templateRegistry;
77
78
    // BC for Symfony 3.3 where ControllerTrait exists but does not contain get() and has() methods.
79
    public function __call($method, $arguments)
80
    {
81
        if (\in_array($method, ['get', 'has'], true)) {
82
            return \call_user_func_array([$this->container, $method], $arguments);
83
        }
84
85
        if (method_exists($this, 'proxyToControllerClass')) {
86
            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...
87
        }
88
89
        throw new \LogicException('Call to undefined method '.__CLASS__.'::'.$method);
90
    }
91
92
    public function setContainer(ContainerInterface $container = null)
93
    {
94
        $this->container = $container;
95
96
        $this->configure();
97
    }
98
99
    /**
100
     * NEXT_MAJOR: Remove this method.
101
     *
102
     * @see renderWithExtraParams()
103
     *
104
     * @param string $view       The view name
105
     * @param array  $parameters An array of parameters to pass to the view
106
     *
107
     * @return Response A Response instance
108
     *
109
     * @deprecated since version 3.27, to be removed in 4.0. Use Sonata\AdminBundle\Controller\CRUDController::renderWithExtraParams() instead.
110
     */
111
    public function render($view, array $parameters = [], Response $response = null)
112
    {
113
        @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...
114
            'Method '.__CLASS__.'::render has been renamed to '.__CLASS__.'::renderWithExtraParams.',
115
            E_USER_DEPRECATED
116
        );
117
118
        return $this->renderWithExtraParams($view, $parameters, $response);
119
    }
120
121
    /**
122
     * Renders a view while passing mandatory parameters on to the template.
123
     *
124
     * @param string $view The view name
125
     *
126
     * @return Response A Response instance
127
     */
128
    public function renderWithExtraParams($view, array $parameters = [], Response $response = null)
129
    {
130
        if (!$this->isXmlHttpRequest()) {
131
            $parameters['breadcrumbs_builder'] = $this->get('sonata.admin.breadcrumbs_builder');
132
        }
133
        $parameters['admin'] = $parameters['admin'] ??
134
            $this->admin;
135
136
        $parameters['base_template'] = $parameters['base_template'] ??
137
            $this->getBaseTemplate();
138
139
        $parameters['admin_pool'] = $this->get('sonata.admin.pool');
140
141
        //NEXT_MAJOR: Remove method alias and use $this->render() directly.
142
        return $this->originalRender($view, $parameters, $response);
143
    }
144
145
    /**
146
     * List action.
147
     *
148
     * @throws AccessDeniedException If access is not granted
149
     *
150
     * @return Response
151
     */
152
    public function listAction()
153
    {
154
        $request = $this->getRequest();
155
156
        $this->admin->checkAccess('list');
157
158
        $preResponse = $this->preList($request);
159
        if (null !== $preResponse) {
160
            return $preResponse;
161
        }
162
163
        if ($listMode = $request->get('_list_mode')) {
164
            $this->admin->setListMode($listMode);
165
        }
166
167
        $datagrid = $this->admin->getDatagrid();
168
        $formView = $datagrid->getForm()->createView();
169
170
        // set the theme for the current Admin Form
171
        $this->setFormTheme($formView, $this->admin->getFilterTheme());
172
173
        // NEXT_MAJOR: Remove this line and use commented line below it instead
174
        $template = $this->admin->getTemplate('list');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
175
        // $template = $this->templateRegistry->getTemplate('list');
176
177
        return $this->renderWithExtraParams($template, [
178
            'action' => 'list',
179
            'form' => $formView,
180
            'datagrid' => $datagrid,
181
            'csrf_token' => $this->getCsrfToken('sonata.batch'),
182
            'export_formats' => $this->has('sonata.admin.admin_exporter') ?
183
                $this->get('sonata.admin.admin_exporter')->getAvailableFormats($this->admin) :
184
                $this->admin->getExportFormats(),
185
        ], null);
186
    }
187
188
    /**
189
     * Execute a batch delete.
190
     *
191
     * @throws AccessDeniedException If access is not granted
192
     *
193
     * @return RedirectResponse
194
     */
195
    public function batchActionDelete(ProxyQueryInterface $query)
196
    {
197
        $this->admin->checkAccess('batchDelete');
198
199
        $modelManager = $this->admin->getModelManager();
200
201
        try {
202
            $modelManager->batchDelete($this->admin->getClass(), $query);
203
            $this->addFlash(
204
                'sonata_flash_success',
205
                $this->trans('flash_batch_delete_success', [], 'SonataAdminBundle')
206
            );
207
        } catch (ModelManagerException $e) {
208
            $this->handleModelManagerException($e);
209
            $this->addFlash(
210
                'sonata_flash_error',
211
                $this->trans('flash_batch_delete_error', [], 'SonataAdminBundle')
212
            );
213
        }
214
215
        return $this->redirectToList();
216
    }
217
218
    /**
219
     * Delete action.
220
     *
221
     * @param int|string|null $id
222
     *
223
     * @throws NotFoundHttpException If the object does not exist
224
     * @throws AccessDeniedException If access is not granted
225
     *
226
     * @return Response|RedirectResponse
227
     */
228
    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...
229
    {
230
        $request = $this->getRequest();
231
        $id = $request->get($this->admin->getIdParameter());
232
        $object = $this->admin->getObject($id);
233
234
        if (!$object) {
235
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
236
        }
237
238
        $this->checkParentChildAssociation($request, $object);
239
240
        $this->admin->checkAccess('delete', $object);
241
242
        $preResponse = $this->preDelete($request, $object);
243
        if (null !== $preResponse) {
244
            return $preResponse;
245
        }
246
247
        if ('DELETE' === $this->getRestMethod()) {
248
            // check the csrf token
249
            $this->validateCsrfToken('sonata.delete');
250
251
            $objectName = $this->admin->toString($object);
252
253
            try {
254
                $this->admin->delete($object);
255
256
                if ($this->isXmlHttpRequest()) {
257
                    return $this->renderJson(['result' => 'ok'], 200, []);
258
                }
259
260
                $this->addFlash(
261
                    'sonata_flash_success',
262
                    $this->trans(
263
                        'flash_delete_success',
264
                        ['%name%' => $this->escapeHtml($objectName)],
265
                        'SonataAdminBundle'
266
                    )
267
                );
268
            } catch (ModelManagerException $e) {
269
                $this->handleModelManagerException($e);
270
271
                if ($this->isXmlHttpRequest()) {
272
                    return $this->renderJson(['result' => 'error'], 200, []);
273
                }
274
275
                $this->addFlash(
276
                    'sonata_flash_error',
277
                    $this->trans(
278
                        'flash_delete_error',
279
                        ['%name%' => $this->escapeHtml($objectName)],
280
                        'SonataAdminBundle'
281
                    )
282
                );
283
            }
284
285
            return $this->redirectTo($object);
286
        }
287
288
        // NEXT_MAJOR: Remove this line and use commented line below it instead
289
        $template = $this->admin->getTemplate('delete');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
290
        // $template = $this->templateRegistry->getTemplate('delete');
291
292
        return $this->renderWithExtraParams($template, [
293
            'object' => $object,
294
            'action' => 'delete',
295
            'csrf_token' => $this->getCsrfToken('sonata.delete'),
296
        ], null);
297
    }
298
299
    /**
300
     * Edit action.
301
     *
302
     * @param int|string|null $id
303
     *
304
     * @throws NotFoundHttpException If the object does not exist
305
     * @throws \RuntimeException     If no editable field is defined
306
     * @throws AccessDeniedException If access is not granted
307
     *
308
     * @return Response|RedirectResponse
309
     */
310
    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...
311
    {
312
        $request = $this->getRequest();
313
        // the key used to lookup the template
314
        $templateKey = 'edit';
315
316
        $id = $request->get($this->admin->getIdParameter());
317
        $existingObject = $this->admin->getObject($id);
318
319
        if (!$existingObject) {
320
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
321
        }
322
323
        $this->checkParentChildAssociation($request, $existingObject);
324
325
        $this->admin->checkAccess('edit', $existingObject);
326
327
        $preResponse = $this->preEdit($request, $existingObject);
328
        if (null !== $preResponse) {
329
            return $preResponse;
330
        }
331
332
        $this->admin->setSubject($existingObject);
333
        $objectId = $this->admin->getNormalizedIdentifier($existingObject);
334
335
        $form = $this->admin->getForm();
336
337
        if (!\is_array($fields = $form->all()) || 0 === \count($fields)) {
338
            throw new \RuntimeException(
339
                'No editable field defined. Did you forget to implement the "configureFormFields" method?'
340
            );
341
        }
342
343
        $form->setData($existingObject);
344
        $form->handleRequest($request);
345
346
        if ($form->isSubmitted()) {
347
            $isFormValid = $form->isValid();
348
349
            // persist if the form was valid and if in preview mode the preview was approved
350
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
351
                $submittedObject = $form->getData();
352
                $this->admin->setSubject($submittedObject);
353
354
                try {
355
                    $existingObject = $this->admin->update($submittedObject);
356
357
                    if ($this->isXmlHttpRequest()) {
358
                        return $this->renderJson([
359
                            'result' => 'ok',
360
                            'objectId' => $objectId,
361
                            'objectName' => $this->escapeHtml($this->admin->toString($existingObject)),
362
                        ], 200, []);
363
                    }
364
365
                    $this->addFlash(
366
                        'sonata_flash_success',
367
                        $this->trans(
368
                            'flash_edit_success',
369
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
370
                            'SonataAdminBundle'
371
                        )
372
                    );
373
374
                    // redirect to edit mode
375
                    return $this->redirectTo($existingObject);
376
                } catch (ModelManagerException $e) {
377
                    $this->handleModelManagerException($e);
378
379
                    $isFormValid = false;
380
                } catch (LockException $e) {
381
                    $this->addFlash('sonata_flash_error', $this->trans('flash_lock_error', [
382
                        '%name%' => $this->escapeHtml($this->admin->toString($existingObject)),
383
                        '%link_start%' => '<a href="'.$this->admin->generateObjectUrl('edit', $existingObject).'">',
384
                        '%link_end%' => '</a>',
385
                    ], 'SonataAdminBundle'));
386
                }
387
            }
388
389
            // show an error message if the form failed validation
390
            if (!$isFormValid) {
391
                if (!$this->isXmlHttpRequest()) {
392
                    $this->addFlash(
393
                        'sonata_flash_error',
394
                        $this->trans(
395
                            'flash_edit_error',
396
                            ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
397
                            'SonataAdminBundle'
398
                        )
399
                    );
400
                }
401
            } elseif ($this->isPreviewRequested()) {
402
                // enable the preview template if the form was valid and preview was requested
403
                $templateKey = 'preview';
404
                $this->admin->getShow();
405
            }
406
        }
407
408
        $formView = $form->createView();
409
        // set the theme for the current Admin Form
410
        $this->setFormTheme($formView, $this->admin->getFormTheme());
411
412
        // NEXT_MAJOR: Remove this line and use commented line below it instead
413
        $template = $this->admin->getTemplate($templateKey);
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
414
        // $template = $this->templateRegistry->getTemplate($templateKey);
415
416
        return $this->renderWithExtraParams($template, [
417
            'action' => 'edit',
418
            'form' => $formView,
419
            'object' => $existingObject,
420
            'objectId' => $objectId,
421
        ], null);
422
    }
423
424
    /**
425
     * Batch action.
426
     *
427
     * @throws NotFoundHttpException If the HTTP method is not POST
428
     * @throws \RuntimeException     If the batch action is not defined
429
     *
430
     * @return Response|RedirectResponse
431
     */
432
    public function batchAction()
433
    {
434
        $request = $this->getRequest();
435
        $restMethod = $this->getRestMethod();
436
437
        if ('POST' !== $restMethod) {
438
            throw $this->createNotFoundException(sprintf('Invalid request type "%s", POST expected', $restMethod));
439
        }
440
441
        // check the csrf token
442
        $this->validateCsrfToken('sonata.batch');
443
444
        $confirmation = $request->get('confirmation', false);
445
446
        if ($data = json_decode((string) $request->get('data'), true)) {
447
            $action = $data['action'];
448
            $idx = $data['idx'];
449
            $allElements = $data['all_elements'];
450
            $request->request->replace(array_merge($request->request->all(), $data));
451
        } else {
452
            $request->request->set('idx', $request->get('idx', []));
453
            $request->request->set('all_elements', $request->get('all_elements', false));
454
455
            $action = $request->get('action');
456
            $idx = $request->get('idx');
457
            $allElements = $request->get('all_elements');
458
            $data = $request->request->all();
459
460
            unset($data['_sonata_csrf_token']);
461
        }
462
463
        // NEXT_MAJOR: Remove reflection check.
464
        $reflector = new \ReflectionMethod($this->admin, 'getBatchActions');
465
        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...
466
            @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...
467
                .' is deprecated since version 3.2.'
468
                .' Use Sonata\AdminBundle\Admin\AbstractAdmin::configureBatchActions instead.'
469
                .' The method will be final in 4.0.', E_USER_DEPRECATED
470
            );
471
        }
472
        $batchActions = $this->admin->getBatchActions();
473
        if (!\array_key_exists($action, $batchActions)) {
474
            throw new \RuntimeException(sprintf('The `%s` batch action is not defined', $action));
475
        }
476
477
        $camelizedAction = Inflector::classify($action);
478
        $isRelevantAction = sprintf('batchAction%sIsRelevant', $camelizedAction);
479
480
        if (method_exists($this, $isRelevantAction)) {
481
            $nonRelevantMessage = \call_user_func([$this, $isRelevantAction], $idx, $allElements, $request);
482
        } else {
483
            $nonRelevantMessage = 0 !== \count($idx) || $allElements; // at least one item is selected
484
        }
485
486
        if (!$nonRelevantMessage) { // default non relevant message (if false of null)
487
            $nonRelevantMessage = 'flash_batch_empty';
488
        }
489
490
        $datagrid = $this->admin->getDatagrid();
491
        $datagrid->buildPager();
492
493
        if (true !== $nonRelevantMessage) {
494
            $this->addFlash(
495
                'sonata_flash_info',
496
                $this->trans($nonRelevantMessage, [], 'SonataAdminBundle')
497
            );
498
499
            return $this->redirectToList();
500
        }
501
502
        $askConfirmation = $batchActions[$action]['ask_confirmation'] ??
503
            true;
504
505
        if ($askConfirmation && 'ok' !== $confirmation) {
506
            $actionLabel = $batchActions[$action]['label'];
507
            $batchTranslationDomain = $batchActions[$action]['translation_domain'] ??
508
                $this->admin->getTranslationDomain();
509
510
            $formView = $datagrid->getForm()->createView();
511
            $this->setFormTheme($formView, $this->admin->getFilterTheme());
512
513
            // NEXT_MAJOR: Remove these lines and use commented lines below them instead
514
            $template = !empty($batchActions[$action]['template']) ?
515
                $batchActions[$action]['template'] :
516
                $this->admin->getTemplate('batch_confirmation');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
517
            // $template = !empty($batchActions[$action]['template']) ?
518
            //     $batchActions[$action]['template'] :
519
            //     $this->templateRegistry->getTemplate('batch_confirmation');
520
521
            return $this->renderWithExtraParams($template, [
522
                'action' => 'list',
523
                'action_label' => $actionLabel,
524
                'batch_translation_domain' => $batchTranslationDomain,
525
                'datagrid' => $datagrid,
526
                'form' => $formView,
527
                'data' => $data,
528
                'csrf_token' => $this->getCsrfToken('sonata.batch'),
529
            ], null);
530
        }
531
532
        // execute the action, batchActionXxxxx
533
        $finalAction = sprintf('batchAction%s', $camelizedAction);
534
        if (!method_exists($this, $finalAction)) {
535
            throw new \RuntimeException(sprintf('A `%s::%s` method must be callable', \get_class($this), $finalAction));
536
        }
537
538
        $query = $datagrid->getQuery();
539
540
        $query->setFirstResult(null);
541
        $query->setMaxResults(null);
542
543
        $this->admin->preBatchAction($action, $query, $idx, $allElements);
544
545
        if (\count($idx) > 0) {
546
            $this->admin->getModelManager()->addIdentifiersToQuery($this->admin->getClass(), $query, $idx);
547
        } elseif (!$allElements) {
548
            $this->addFlash(
549
                'sonata_flash_info',
550
                $this->trans('flash_batch_no_elements_processed', [], 'SonataAdminBundle')
551
            );
552
553
            return $this->redirectToList();
554
        }
555
556
        return \call_user_func([$this, $finalAction], $query, $request);
557
    }
558
559
    /**
560
     * Create action.
561
     *
562
     * @throws AccessDeniedException If access is not granted
563
     * @throws \RuntimeException     If no editable field is defined
564
     *
565
     * @return Response
566
     */
567
    public function createAction()
568
    {
569
        $request = $this->getRequest();
570
        // the key used to lookup the template
571
        $templateKey = 'edit';
572
573
        $this->admin->checkAccess('create');
574
575
        $class = new \ReflectionClass($this->admin->hasActiveSubClass() ? $this->admin->getActiveSubClass() : $this->admin->getClass());
576
577
        if ($class->isAbstract()) {
578
            return $this->renderWithExtraParams(
579
                '@SonataAdmin/CRUD/select_subclass.html.twig',
580
                [
581
                    'base_template' => $this->getBaseTemplate(),
582
                    'admin' => $this->admin,
583
                    'action' => 'create',
584
                ],
585
                null
586
            );
587
        }
588
589
        $newObject = $this->admin->getNewInstance();
590
591
        $preResponse = $this->preCreate($request, $newObject);
592
        if (null !== $preResponse) {
593
            return $preResponse;
594
        }
595
596
        $this->admin->setSubject($newObject);
597
598
        $form = $this->admin->getForm();
599
600
        if (!\is_array($fields = $form->all()) || 0 === \count($fields)) {
601
            throw new \RuntimeException(
602
                'No editable field defined. Did you forget to implement the "configureFormFields" method?'
603
            );
604
        }
605
606
        $form->setData($newObject);
607
        $form->handleRequest($request);
608
609
        if ($form->isSubmitted()) {
610
            $isFormValid = $form->isValid();
611
612
            // persist if the form was valid and if in preview mode the preview was approved
613
            if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
614
                $submittedObject = $form->getData();
615
                $this->admin->setSubject($submittedObject);
616
                $this->admin->checkAccess('create', $submittedObject);
617
618
                try {
619
                    $newObject = $this->admin->create($submittedObject);
620
621
                    if ($this->isXmlHttpRequest()) {
622
                        return $this->renderJson([
623
                            'result' => 'ok',
624
                            'objectId' => $this->admin->getNormalizedIdentifier($newObject),
625
                            'objectName' => $this->escapeHtml($this->admin->toString($newObject)),
626
                        ], 200, []);
627
                    }
628
629
                    $this->addFlash(
630
                        'sonata_flash_success',
631
                        $this->trans(
632
                            'flash_create_success',
633
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
634
                            'SonataAdminBundle'
635
                        )
636
                    );
637
638
                    // redirect to edit mode
639
                    return $this->redirectTo($newObject);
640
                } catch (ModelManagerException $e) {
641
                    $this->handleModelManagerException($e);
642
643
                    $isFormValid = false;
644
                }
645
            }
646
647
            // show an error message if the form failed validation
648
            if (!$isFormValid) {
649
                if (!$this->isXmlHttpRequest()) {
650
                    $this->addFlash(
651
                        'sonata_flash_error',
652
                        $this->trans(
653
                            'flash_create_error',
654
                            ['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
655
                            'SonataAdminBundle'
656
                        )
657
                    );
658
                }
659
            } elseif ($this->isPreviewRequested()) {
660
                // pick the preview template if the form was valid and preview was requested
661
                $templateKey = 'preview';
662
                $this->admin->getShow();
663
            }
664
        }
665
666
        $formView = $form->createView();
667
        // set the theme for the current Admin Form
668
        $this->setFormTheme($formView, $this->admin->getFormTheme());
669
670
        // NEXT_MAJOR: Remove this line and use commented line below it instead
671
        $template = $this->admin->getTemplate($templateKey);
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
672
        // $template = $this->templateRegistry->getTemplate($templateKey);
673
674
        return $this->renderWithExtraParams($template, [
675
            'action' => 'create',
676
            'form' => $formView,
677
            'object' => $newObject,
678
            'objectId' => null,
679
        ], null);
680
    }
681
682
    /**
683
     * Show action.
684
     *
685
     * @param int|string|null $id
686
     *
687
     * @throws NotFoundHttpException If the object does not exist
688
     * @throws AccessDeniedException If access is not granted
689
     *
690
     * @return Response
691
     */
692
    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...
693
    {
694
        $request = $this->getRequest();
695
        $id = $request->get($this->admin->getIdParameter());
696
697
        $object = $this->admin->getObject($id);
698
699
        if (!$object) {
700
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
701
        }
702
703
        $this->checkParentChildAssociation($request, $object);
704
705
        $this->admin->checkAccess('show', $object);
706
707
        $preResponse = $this->preShow($request, $object);
708
        if (null !== $preResponse) {
709
            return $preResponse;
710
        }
711
712
        $this->admin->setSubject($object);
713
714
        $fields = $this->admin->getShow();
715
        \assert($fields instanceof FieldDescriptionCollection);
716
717
        // NEXT_MAJOR: replace deprecation with exception
718
        if (!\is_array($fields->getElements()) || 0 === $fields->count()) {
719
            @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...
720
                'Calling this method without implementing "configureShowFields"'
721
                .' is not supported since 3.40.0'
722
                .' and will no longer be possible in 4.0',
723
                E_USER_DEPRECATED
724
            );
725
        }
726
727
        // NEXT_MAJOR: Remove this line and use commented line below it instead
728
        $template = $this->admin->getTemplate('show');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
729
        //$template = $this->templateRegistry->getTemplate('show');
730
731
        return $this->renderWithExtraParams($template, [
732
            'action' => 'show',
733
            'object' => $object,
734
            'elements' => $fields,
735
        ], null);
736
    }
737
738
    /**
739
     * Show history revisions for object.
740
     *
741
     * @param int|string|null $id
742
     *
743
     * @throws AccessDeniedException If access is not granted
744
     * @throws NotFoundHttpException If the object does not exist or the audit reader is not available
745
     *
746
     * @return Response
747
     */
748
    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...
749
    {
750
        $request = $this->getRequest();
751
        $id = $request->get($this->admin->getIdParameter());
752
753
        $object = $this->admin->getObject($id);
754
755
        if (!$object) {
756
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
757
        }
758
759
        $this->admin->checkAccess('history', $object);
760
761
        $manager = $this->get('sonata.admin.audit.manager');
762
763
        if (!$manager->hasReader($this->admin->getClass())) {
764
            throw $this->createNotFoundException(
765
                sprintf(
766
                    'unable to find the audit reader for class : %s',
767
                    $this->admin->getClass()
768
                )
769
            );
770
        }
771
772
        $reader = $manager->getReader($this->admin->getClass());
773
774
        $revisions = $reader->findRevisions($this->admin->getClass(), $id);
775
776
        // NEXT_MAJOR: Remove this line and use commented line below it instead
777
        $template = $this->admin->getTemplate('history');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
778
        // $template = $this->templateRegistry->getTemplate('history');
779
780
        return $this->renderWithExtraParams($template, [
781
            'action' => 'history',
782
            'object' => $object,
783
            'revisions' => $revisions,
784
            'currentRevision' => $revisions ? current($revisions) : false,
785
        ], null);
786
    }
787
788
    /**
789
     * View history revision of object.
790
     *
791
     * @param int|string|null $id
792
     * @param string|null     $revision
793
     *
794
     * @throws AccessDeniedException If access is not granted
795
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
796
     *
797
     * @return Response
798
     */
799
    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...
800
    {
801
        $request = $this->getRequest();
802
        $id = $request->get($this->admin->getIdParameter());
803
804
        $object = $this->admin->getObject($id);
805
806
        if (!$object) {
807
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
808
        }
809
810
        $this->admin->checkAccess('historyViewRevision', $object);
811
812
        $manager = $this->get('sonata.admin.audit.manager');
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 revisioned object
826
        $object = $reader->find($this->admin->getClass(), $id, $revision);
827
828
        if (!$object) {
829
            throw $this->createNotFoundException(
830
                sprintf(
831
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
832
                    $id,
833
                    $revision,
834
                    $this->admin->getClass()
835
                )
836
            );
837
        }
838
839
        $this->admin->setSubject($object);
840
841
        // NEXT_MAJOR: Remove this line and use commented line below it instead
842
        $template = $this->admin->getTemplate('show');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
843
        // $template = $this->templateRegistry->getTemplate('show');
844
845
        return $this->renderWithExtraParams($template, [
846
            'action' => 'show',
847
            'object' => $object,
848
            'elements' => $this->admin->getShow(),
849
        ], null);
850
    }
851
852
    /**
853
     * Compare history revisions of object.
854
     *
855
     * @param int|string|null $id
856
     * @param int|string|null $base_revision
857
     * @param int|string|null $compare_revision
858
     *
859
     * @throws AccessDeniedException If access is not granted
860
     * @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
861
     *
862
     * @return Response
863
     */
864
    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...
865
    {
866
        $request = $this->getRequest();
867
868
        $this->admin->checkAccess('historyCompareRevisions');
869
870
        $id = $request->get($this->admin->getIdParameter());
871
872
        $object = $this->admin->getObject($id);
873
874
        if (!$object) {
875
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
876
        }
877
878
        $manager = $this->get('sonata.admin.audit.manager');
879
880
        if (!$manager->hasReader($this->admin->getClass())) {
881
            throw $this->createNotFoundException(
882
                sprintf(
883
                    'unable to find the audit reader for class : %s',
884
                    $this->admin->getClass()
885
                )
886
            );
887
        }
888
889
        $reader = $manager->getReader($this->admin->getClass());
890
891
        // retrieve the base revision
892
        $base_object = $reader->find($this->admin->getClass(), $id, $base_revision);
893
        if (!$base_object) {
894
            throw $this->createNotFoundException(
895
                sprintf(
896
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
897
                    $id,
898
                    $base_revision,
899
                    $this->admin->getClass()
900
                )
901
            );
902
        }
903
904
        // retrieve the compare revision
905
        $compare_object = $reader->find($this->admin->getClass(), $id, $compare_revision);
906
        if (!$compare_object) {
907
            throw $this->createNotFoundException(
908
                sprintf(
909
                    'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
910
                    $id,
911
                    $compare_revision,
912
                    $this->admin->getClass()
913
                )
914
            );
915
        }
916
917
        $this->admin->setSubject($base_object);
918
919
        // NEXT_MAJOR: Remove this line and use commented line below it instead
920
        $template = $this->admin->getTemplate('show_compare');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
921
        // $template = $this->templateRegistry->getTemplate('show_compare');
922
923
        return $this->renderWithExtraParams($template, [
924
            'action' => 'show',
925
            'object' => $base_object,
926
            'object_compare' => $compare_object,
927
            'elements' => $this->admin->getShow(),
928
        ], null);
929
    }
930
931
    /**
932
     * Export data to specified format.
933
     *
934
     * @throws AccessDeniedException If access is not granted
935
     * @throws \RuntimeException     If the export format is invalid
936
     *
937
     * @return Response
938
     */
939
    public function exportAction(Request $request)
940
    {
941
        $this->admin->checkAccess('export');
942
943
        $format = $request->get('format');
944
945
        // NEXT_MAJOR: remove the check
946
        if (!$this->has('sonata.admin.admin_exporter')) {
947
            @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...
948
                'Not registering the exporter bundle is deprecated since version 3.14.'
949
                .' You must register it to be able to use the export action in 4.0.',
950
                E_USER_DEPRECATED
951
            );
952
            $allowedExportFormats = (array) $this->admin->getExportFormats();
953
954
            $class = (string) $this->admin->getClass();
955
            $filename = sprintf(
956
                'export_%s_%s.%s',
957
                strtolower((string) substr($class, strripos($class, '\\') + 1)),
958
                date('Y_m_d_H_i_s', strtotime('now')),
959
                $format
960
            );
961
            $exporter = $this->get('sonata.admin.exporter');
962
        } else {
963
            $adminExporter = $this->get('sonata.admin.admin_exporter');
964
            $allowedExportFormats = $adminExporter->getAvailableFormats($this->admin);
965
            $filename = $adminExporter->getExportFilename($this->admin, $format);
966
            $exporter = $this->get('sonata.exporter.exporter');
967
        }
968
969
        if (!\in_array($format, $allowedExportFormats, true)) {
970
            throw new \RuntimeException(
971
                sprintf(
972
                    'Export in format `%s` is not allowed for class: `%s`. Allowed formats are: `%s`',
973
                    $format,
974
                    $this->admin->getClass(),
975
                    implode(', ', $allowedExportFormats)
976
                )
977
            );
978
        }
979
980
        return $exporter->getResponse(
981
            $format,
982
            $filename,
983
            $this->admin->getDataSourceIterator()
984
        );
985
    }
986
987
    /**
988
     * Returns the Response object associated to the acl action.
989
     *
990
     * @param int|string|null $id
991
     *
992
     * @throws AccessDeniedException If access is not granted
993
     * @throws NotFoundHttpException If the object does not exist or the ACL is not enabled
994
     *
995
     * @return Response|RedirectResponse
996
     */
997
    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...
998
    {
999
        $request = $this->getRequest();
1000
1001
        if (!$this->admin->isAclEnabled()) {
1002
            throw $this->createNotFoundException('ACL are not enabled for this admin');
1003
        }
1004
1005
        $id = $request->get($this->admin->getIdParameter());
1006
1007
        $object = $this->admin->getObject($id);
1008
1009
        if (!$object) {
1010
            throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
1011
        }
1012
1013
        $this->admin->checkAccess('acl', $object);
1014
1015
        $this->admin->setSubject($object);
1016
        $aclUsers = $this->getAclUsers();
1017
        $aclRoles = $this->getAclRoles();
1018
1019
        $adminObjectAclManipulator = $this->get('sonata.admin.object.manipulator.acl.admin');
1020
        $adminObjectAclData = new AdminObjectAclData(
1021
            $this->admin,
1022
            $object,
1023
            $aclUsers,
1024
            $adminObjectAclManipulator->getMaskBuilderClass(),
1025
            $aclRoles
1026
        );
1027
1028
        $aclUsersForm = $adminObjectAclManipulator->createAclUsersForm($adminObjectAclData);
1029
        $aclRolesForm = $adminObjectAclManipulator->createAclRolesForm($adminObjectAclData);
1030
1031
        if ('POST' === $request->getMethod()) {
1032
            if ($request->request->has(AdminObjectAclManipulator::ACL_USERS_FORM_NAME)) {
1033
                $form = $aclUsersForm;
1034
                $updateMethod = 'updateAclUsers';
1035
            } elseif ($request->request->has(AdminObjectAclManipulator::ACL_ROLES_FORM_NAME)) {
1036
                $form = $aclRolesForm;
1037
                $updateMethod = 'updateAclRoles';
1038
            }
1039
1040
            if (isset($form)) {
1041
                $form->handleRequest($request);
1042
1043
                if ($form->isValid()) {
1044
                    $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...
1045
                    $this->addFlash(
1046
                        'sonata_flash_success',
1047
                        $this->trans('flash_acl_edit_success', [], 'SonataAdminBundle')
1048
                    );
1049
1050
                    return new RedirectResponse($this->admin->generateObjectUrl('acl', $object));
1051
                }
1052
            }
1053
        }
1054
1055
        // NEXT_MAJOR: Remove this line and use commented line below it instead
1056
        $template = $this->admin->getTemplate('acl');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1057
        // $template = $this->templateRegistry->getTemplate('acl');
1058
1059
        return $this->renderWithExtraParams($template, [
1060
            'action' => 'acl',
1061
            'permissions' => $adminObjectAclData->getUserPermissions(),
1062
            'object' => $object,
1063
            'users' => $aclUsers,
1064
            'roles' => $aclRoles,
1065
            'aclUsersForm' => $aclUsersForm->createView(),
1066
            'aclRolesForm' => $aclRolesForm->createView(),
1067
        ], null);
1068
    }
1069
1070
    /**
1071
     * @return Request
1072
     */
1073
    public function getRequest()
1074
    {
1075
        return $this->container->get('request_stack')->getCurrentRequest();
1076
    }
1077
1078
    /**
1079
     * Gets a container configuration parameter by its name.
1080
     *
1081
     * @param string $name The parameter name
1082
     *
1083
     * @return mixed
1084
     */
1085
    protected function getParameter($name)
1086
    {
1087
        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\Depend...urationContainerBuilder, Symfony\Component\DependencyInjection\Container, Symfony\Component\Depend...ection\ContainerBuilder, Symfony\Component\Depend...\NoConstructorContainer, Symfony\Component\Depend...tainers\CustomContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony_DI_PhpDumper_Test_Almost_Circular_Private, Symfony_DI_PhpDumper_Test_Almost_Circular_Public, Symfony_DI_PhpDumper_Test_Base64Parameters, Symfony_DI_PhpDumper_Test_Deep_Graph, Symfony_DI_PhpDumper_Test_EnvParameters, Symfony_DI_PhpDumper_Test_Inline_Self_Ref, Symfony_DI_PhpDumper_Test_Legacy_Privates, Symfony_DI_PhpDumper_Test_Rot13Parameters, Symfony_DI_PhpDumper_Test_Uninitialized_Reference.

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...
1088
    }
1089
1090
    /**
1091
     * Render JSON.
1092
     *
1093
     * @param mixed $data
1094
     * @param int   $status
1095
     * @param array $headers
1096
     *
1097
     * @return Response with json encoded data
1098
     */
1099
    protected function renderJson($data, $status = 200, $headers = [])
1100
    {
1101
        return new JsonResponse($data, $status, $headers);
1102
    }
1103
1104
    /**
1105
     * Returns true if the request is a XMLHttpRequest.
1106
     *
1107
     * @return bool True if the request is an XMLHttpRequest, false otherwise
1108
     */
1109
    protected function isXmlHttpRequest()
1110
    {
1111
        $request = $this->getRequest();
1112
1113
        return $request->isXmlHttpRequest() || $request->get('_xml_http_request');
1114
    }
1115
1116
    /**
1117
     * Returns the correct RESTful verb, given either by the request itself or
1118
     * via the "_method" parameter.
1119
     *
1120
     * @return string HTTP method, either
1121
     */
1122
    protected function getRestMethod()
1123
    {
1124
        $request = $this->getRequest();
1125
1126
        if (Request::getHttpMethodParameterOverride() || !$request->request->has('_method')) {
1127
            return $request->getMethod();
1128
        }
1129
1130
        return $request->request->get('_method');
1131
    }
1132
1133
    /**
1134
     * Contextualize the admin class depends on the current request.
1135
     *
1136
     * @throws \RuntimeException
1137
     */
1138
    protected function configure()
1139
    {
1140
        $request = $this->getRequest();
1141
1142
        $adminCode = $request->get('_sonata_admin');
1143
1144
        if (!$adminCode) {
1145
            throw new \RuntimeException(sprintf(
1146
                'There is no `_sonata_admin` defined for the controller `%s` and the current route `%s`',
1147
                \get_class($this),
1148
                $request->get('_route')
1149
            ));
1150
        }
1151
1152
        $this->admin = $this->container->get('sonata.admin.pool')->getAdminByAdminCode($adminCode);
1153
1154
        if (!$this->admin) {
1155
            throw new \RuntimeException(sprintf(
1156
                'Unable to find the admin class related to the current controller (%s)',
1157
                \get_class($this)
1158
            ));
1159
        }
1160
1161
        $this->templateRegistry = $this->container->get($this->admin->getCode().'.template_registry');
1162
        if (!$this->templateRegistry instanceof TemplateRegistryInterface) {
1163
            throw new \RuntimeException(sprintf(
1164
                'Unable to find the template registry related to the current admin (%s)',
1165
                $this->admin->getCode()
1166
            ));
1167
        }
1168
1169
        $rootAdmin = $this->admin;
1170
1171
        while ($rootAdmin->isChild()) {
1172
            $rootAdmin->setCurrentChild(true);
1173
            $rootAdmin = $rootAdmin->getParent();
1174
        }
1175
1176
        $rootAdmin->setRequest($request);
1177
1178
        if ($request->get('uniqid')) {
1179
            $this->admin->setUniqid($request->get('uniqid'));
1180
        }
1181
    }
1182
1183
    /**
1184
     * Proxy for the logger service of the container.
1185
     * If no such service is found, a NullLogger is returned.
1186
     *
1187
     * @return LoggerInterface
1188
     */
1189
    protected function getLogger()
1190
    {
1191
        if ($this->container->has('logger')) {
1192
            $logger = $this->container->get('logger');
1193
            \assert($logger instanceof LoggerInterface);
1194
1195
            return $logger;
1196
        }
1197
1198
        return new NullLogger();
1199
    }
1200
1201
    /**
1202
     * Returns the base template name.
1203
     *
1204
     * @return string The template name
1205
     */
1206
    protected function getBaseTemplate()
1207
    {
1208
        if ($this->isXmlHttpRequest()) {
1209
            // NEXT_MAJOR: Remove this line and use commented line below it instead
1210
            return $this->admin->getTemplate('ajax');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1211
            // return $this->templateRegistry->getTemplate('ajax');
1212
        }
1213
1214
        // NEXT_MAJOR: Remove this line and use commented line below it instead
1215
        return $this->admin->getTemplate('layout');
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...nterface::getTemplate() has been deprecated with message: since 3.35. To be removed in 4.0. Use TemplateRegistry services instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1216
        // return $this->templateRegistry->getTemplate('layout');
1217
    }
1218
1219
    /**
1220
     * @throws \Exception
1221
     */
1222
    protected function handleModelManagerException(\Exception $e)
1223
    {
1224
        if ($this->get('kernel')->isDebug()) {
1225
            throw $e;
1226
        }
1227
1228
        $context = ['exception' => $e];
1229
        if ($e->getPrevious()) {
1230
            $context['previous_exception_message'] = $e->getPrevious()->getMessage();
1231
        }
1232
        $this->getLogger()->error($e->getMessage(), $context);
1233
    }
1234
1235
    /**
1236
     * Redirect the user depend on this choice.
1237
     *
1238
     * @param object $object
1239
     *
1240
     * @return RedirectResponse
1241
     */
1242
    protected function redirectTo($object)
1243
    {
1244
        $request = $this->getRequest();
1245
1246
        $url = false;
1247
1248
        if (null !== $request->get('btn_update_and_list')) {
1249
            return $this->redirectToList();
1250
        }
1251
        if (null !== $request->get('btn_create_and_list')) {
1252
            return $this->redirectToList();
1253
        }
1254
1255
        if (null !== $request->get('btn_create_and_create')) {
1256
            $params = [];
1257
            if ($this->admin->hasActiveSubClass()) {
1258
                $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...
1259
            }
1260
            $url = $this->admin->generateUrl('create', $params);
1261
        }
1262
1263
        if ('DELETE' === $this->getRestMethod()) {
1264
            return $this->redirectToList();
1265
        }
1266
1267
        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...
1268
            foreach (['edit', 'show'] as $route) {
1269
                if ($this->admin->hasRoute($route) && $this->admin->hasAccess($route, $object)) {
1270
                    $url = $this->admin->generateObjectUrl(
1271
                        $route,
1272
                        $object,
1273
                        $this->getSelectedTab($request)
1274
                    );
1275
1276
                    break;
1277
                }
1278
            }
1279
        }
1280
1281
        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...
1282
            return $this->redirectToList();
1283
        }
1284
1285
        return new RedirectResponse($url);
1286
    }
1287
1288
    /**
1289
     * Redirects the user to the list view.
1290
     *
1291
     * @return RedirectResponse
1292
     */
1293
    final protected function redirectToList()
1294
    {
1295
        $parameters = [];
1296
1297
        if ($filter = $this->admin->getFilterParameters()) {
1298
            $parameters['filter'] = $filter;
1299
        }
1300
1301
        return $this->redirect($this->admin->generateUrl('list', $parameters));
1302
    }
1303
1304
    /**
1305
     * Returns true if the preview is requested to be shown.
1306
     *
1307
     * @return bool
1308
     */
1309
    protected function isPreviewRequested()
1310
    {
1311
        $request = $this->getRequest();
1312
1313
        return null !== $request->get('btn_preview');
1314
    }
1315
1316
    /**
1317
     * Returns true if the preview has been approved.
1318
     *
1319
     * @return bool
1320
     */
1321
    protected function isPreviewApproved()
1322
    {
1323
        $request = $this->getRequest();
1324
1325
        return null !== $request->get('btn_preview_approve');
1326
    }
1327
1328
    /**
1329
     * Returns true if the request is in the preview workflow.
1330
     *
1331
     * That means either a preview is requested or the preview has already been shown
1332
     * and it got approved/declined.
1333
     *
1334
     * @return bool
1335
     */
1336
    protected function isInPreviewMode()
1337
    {
1338
        return $this->admin->supportsPreviewMode()
1339
        && ($this->isPreviewRequested()
1340
            || $this->isPreviewApproved()
1341
            || $this->isPreviewDeclined());
1342
    }
1343
1344
    /**
1345
     * Returns true if the preview has been declined.
1346
     *
1347
     * @return bool
1348
     */
1349
    protected function isPreviewDeclined()
1350
    {
1351
        $request = $this->getRequest();
1352
1353
        return null !== $request->get('btn_preview_decline');
1354
    }
1355
1356
    /**
1357
     * Gets ACL users.
1358
     *
1359
     * @return \Traversable
1360
     */
1361
    protected function getAclUsers()
1362
    {
1363
        $aclUsers = [];
1364
1365
        $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\Depend...urationContainerBuilder, Symfony\Component\DependencyInjection\Container, Symfony\Component\Depend...ection\ContainerBuilder, Symfony\Component\Depend...\NoConstructorContainer, Symfony\Component\Depend...tainers\CustomContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony_DI_PhpDumper_Test_Almost_Circular_Private, Symfony_DI_PhpDumper_Test_Almost_Circular_Public, Symfony_DI_PhpDumper_Test_Base64Parameters, Symfony_DI_PhpDumper_Test_Deep_Graph, Symfony_DI_PhpDumper_Test_EnvParameters, Symfony_DI_PhpDumper_Test_Inline_Self_Ref, Symfony_DI_PhpDumper_Test_Legacy_Privates, Symfony_DI_PhpDumper_Test_Rot13Parameters, Symfony_DI_PhpDumper_Test_Uninitialized_Reference.

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...
1366
        if (null !== $userManagerServiceName && $this->has($userManagerServiceName)) {
1367
            $userManager = $this->get($userManagerServiceName);
1368
1369
            if (method_exists($userManager, 'findUsers')) {
1370
                $aclUsers = $userManager->findUsers();
1371
            }
1372
        }
1373
1374
        return \is_array($aclUsers) ? new \ArrayIterator($aclUsers) : $aclUsers;
1375
    }
1376
1377
    /**
1378
     * Gets ACL roles.
1379
     *
1380
     * @return \Traversable
1381
     */
1382
    protected function getAclRoles()
1383
    {
1384
        $aclRoles = [];
1385
        $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\Depend...urationContainerBuilder, Symfony\Component\DependencyInjection\Container, Symfony\Component\Depend...ection\ContainerBuilder, Symfony\Component\Depend...\NoConstructorContainer, Symfony\Component\Depend...tainers\CustomContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony\Component\Depend...ProjectServiceContainer, Symfony_DI_PhpDumper_Test_Almost_Circular_Private, Symfony_DI_PhpDumper_Test_Almost_Circular_Public, Symfony_DI_PhpDumper_Test_Base64Parameters, Symfony_DI_PhpDumper_Test_Deep_Graph, Symfony_DI_PhpDumper_Test_EnvParameters, Symfony_DI_PhpDumper_Test_Inline_Self_Ref, Symfony_DI_PhpDumper_Test_Legacy_Privates, Symfony_DI_PhpDumper_Test_Rot13Parameters, Symfony_DI_PhpDumper_Test_Uninitialized_Reference.

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...
1386
        $pool = $this->container->get('sonata.admin.pool');
1387
1388
        foreach ($pool->getAdminServiceIds() as $id) {
1389
            try {
1390
                $admin = $pool->getInstance($id);
1391
            } catch (\Exception $e) {
1392
                continue;
1393
            }
1394
1395
            $baseRole = $admin->getSecurityHandler()->getBaseRole($admin);
1396
            foreach ($admin->getSecurityInformation() as $role => $permissions) {
1397
                $role = sprintf($baseRole, $role);
1398
                $aclRoles[] = $role;
1399
            }
1400
        }
1401
1402
        foreach ($roleHierarchy as $name => $roles) {
1403
            $aclRoles[] = $name;
1404
            $aclRoles = array_merge($aclRoles, $roles);
1405
        }
1406
1407
        $aclRoles = array_unique($aclRoles);
1408
1409
        return \is_array($aclRoles) ? new \ArrayIterator($aclRoles) : $aclRoles;
1410
    }
1411
1412
    /**
1413
     * Validate CSRF token for action without form.
1414
     *
1415
     * @param string $intention
1416
     *
1417
     * @throws HttpException
1418
     */
1419
    protected function validateCsrfToken($intention)
1420
    {
1421
        $request = $this->getRequest();
1422
        $token = $request->request->get('_sonata_csrf_token', false);
1423
1424
        if ($this->container->has('security.csrf.token_manager')) {
1425
            $valid = $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($intention, $token));
1426
        } else {
1427
            return;
1428
        }
1429
1430
        if (!$valid) {
1431
            throw new HttpException(400, 'The csrf token is not valid, CSRF attack?');
1432
        }
1433
    }
1434
1435
    /**
1436
     * Escape string for html output.
1437
     *
1438
     * @param string $s
1439
     *
1440
     * @return string
1441
     */
1442
    protected function escapeHtml($s)
1443
    {
1444
        return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
1445
    }
1446
1447
    /**
1448
     * Get CSRF token.
1449
     *
1450
     * @param string $intention
1451
     *
1452
     * @return string|false
1453
     */
1454
    protected function getCsrfToken($intention)
1455
    {
1456
        if ($this->container->has('security.csrf.token_manager')) {
1457
            return $this->container->get('security.csrf.token_manager')->getToken($intention)->getValue();
1458
        }
1459
1460
        return false;
1461
    }
1462
1463
    /**
1464
     * This method can be overloaded in your custom CRUD controller.
1465
     * It's called from createAction.
1466
     *
1467
     * @param mixed $object
1468
     *
1469
     * @return Response|null
1470
     */
1471
    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...
1472
    {
1473
    }
1474
1475
    /**
1476
     * This method can be overloaded in your custom CRUD controller.
1477
     * It's called from editAction.
1478
     *
1479
     * @param mixed $object
1480
     *
1481
     * @return Response|null
1482
     */
1483
    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...
1484
    {
1485
    }
1486
1487
    /**
1488
     * This method can be overloaded in your custom CRUD controller.
1489
     * It's called from deleteAction.
1490
     *
1491
     * @param mixed $object
1492
     *
1493
     * @return Response|null
1494
     */
1495
    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...
1496
    {
1497
    }
1498
1499
    /**
1500
     * This method can be overloaded in your custom CRUD controller.
1501
     * It's called from showAction.
1502
     *
1503
     * @param mixed $object
1504
     *
1505
     * @return Response|null
1506
     */
1507
    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...
1508
    {
1509
    }
1510
1511
    /**
1512
     * This method can be overloaded in your custom CRUD controller.
1513
     * It's called from listAction.
1514
     *
1515
     * @return Response|null
1516
     */
1517
    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...
1518
    {
1519
    }
1520
1521
    /**
1522
     * Translate a message id.
1523
     *
1524
     * @param string $id
1525
     * @param string $domain
1526
     * @param string $locale
1527
     *
1528
     * @return string translated string
1529
     */
1530
    final protected function trans($id, array $parameters = [], $domain = null, $locale = null)
1531
    {
1532
        $domain = $domain ?: $this->admin->getTranslationDomain();
1533
1534
        return $this->get('translator')->trans($id, $parameters, $domain, $locale);
1535
    }
1536
1537
    private function getSelectedTab(Request $request)
1538
    {
1539
        return array_filter(['_tab' => $request->request->get('_tab')]);
1540
    }
1541
1542
    private function checkParentChildAssociation(Request $request, $object): void
1543
    {
1544
        if (!($parentAdmin = $this->admin->getParent())) {
1545
            return;
1546
        }
1547
1548
        // NEXT_MAJOR: remove this check
1549
        if (!$this->admin->getParentAssociationMapping()) {
0 ignored issues
show
Bug introduced by
The method getParentAssociationMapping() does not exist on Sonata\AdminBundle\Admin\AdminInterface. Did you maybe mean getParent()?

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...
1550
            return;
1551
        }
1552
1553
        $parentId = $request->get($parentAdmin->getIdParameter());
1554
1555
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
1556
        $propertyPath = new PropertyPath($this->admin->getParentAssociationMapping());
0 ignored issues
show
Bug introduced by
The method getParentAssociationMapping() does not exist on Sonata\AdminBundle\Admin\AdminInterface. Did you maybe mean getParent()?

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...
1557
1558
        if ($parentAdmin->getObject($parentId) !== $propertyAccessor->getValue($object, $propertyPath)) {
1559
            // NEXT_MAJOR: make this exception
1560
            @trigger_error("Accessing a child that isn't connected to a given parent is deprecated since 3.34"
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...
1561
                ." and won't be allowed in 4.0.",
1562
                E_USER_DEPRECATED
1563
            );
1564
        }
1565
    }
1566
1567
    /**
1568
     * Sets the admin form theme to form view. Used for compatibility between Symfony versions.
1569
     */
1570
    private function setFormTheme(FormView $formView, array $theme = null): void
1571
    {
1572
        $twig = $this->get('twig');
1573
1574
        // BC for Symfony < 3.2 where this runtime does not exists
1575
        if (!method_exists(AppVariable::class, 'getToken')) {
1576
            $twig->getExtension(FormExtension::class)->renderer->setTheme($formView, $theme);
1577
1578
            return;
1579
        }
1580
1581
        // BC for Symfony < 3.4 where runtime should be TwigRenderer
1582
        if (!method_exists(DebugCommand::class, 'getLoaderPaths')) {
1583
            $twig->getRuntime(TwigRenderer::class)->setTheme($formView, $theme);
1584
1585
            return;
1586
        }
1587
1588
        $twig->getRuntime(FormRenderer::class)->setTheme($formView, $theme);
1589
    }
1590
}
1591