Completed
Push — 3.x ( 470eac...cc0fde )
by Christian
03:33
created

CRUDController::deleteAction()   B

Complexity

Conditions 7
Paths 13

Size

Total Lines 70

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 70
rs 7.7212
c 0
b 0
f 0
cc 7
nc 13
nop 1

How to fix   Long Method   

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

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...
1085
    }
1086
1087
    /**
1088
     * Render JSON.
1089
     *
1090
     * @param mixed $data
1091
     * @param int   $status
1092
     * @param array $headers
1093
     *
1094
     * @return JsonResponse with json encoded data
1095
     */
1096
    protected function renderJson($data, $status = 200, $headers = [])
1097
    {
1098
        return new JsonResponse($data, $status, $headers);
1099
    }
1100
1101
    /**
1102
     * Returns true if the request is a XMLHttpRequest.
1103
     *
1104
     * @return bool True if the request is an XMLHttpRequest, false otherwise
1105
     */
1106
    protected function isXmlHttpRequest()
1107
    {
1108
        $request = $this->getRequest();
1109
1110
        return $request->isXmlHttpRequest() || $request->get('_xml_http_request');
1111
    }
1112
1113
    /**
1114
     * Returns the correct RESTful verb, given either by the request itself or
1115
     * via the "_method" parameter.
1116
     *
1117
     * @return string HTTP method, either
1118
     */
1119
    protected function getRestMethod()
1120
    {
1121
        $request = $this->getRequest();
1122
1123
        if (Request::getHttpMethodParameterOverride() || !$request->request->has('_method')) {
1124
            return $request->getMethod();
1125
        }
1126
1127
        return $request->request->get('_method');
1128
    }
1129
1130
    /**
1131
     * Contextualize the admin class depends on the current request.
1132
     *
1133
     * @throws \RuntimeException
1134
     */
1135
    protected function configure()
1136
    {
1137
        $request = $this->getRequest();
1138
1139
        $adminCode = $request->get('_sonata_admin');
1140
1141
        if (!$adminCode) {
1142
            throw new \RuntimeException(sprintf(
1143
                'There is no `_sonata_admin` defined for the controller `%s` and the current route `%s`',
1144
                static::class,
1145
                $request->get('_route')
1146
            ));
1147
        }
1148
1149
        $this->admin = $this->container->get('sonata.admin.pool')->getAdminByAdminCode($adminCode);
1150
1151
        if (!$this->admin) {
1152
            throw new \RuntimeException(sprintf(
1153
                'Unable to find the admin class related to the current controller (%s)',
1154
                static::class
1155
            ));
1156
        }
1157
1158
        $this->templateRegistry = $this->container->get($this->admin->getCode().'.template_registry');
1159
        if (!$this->templateRegistry instanceof TemplateRegistryInterface) {
1160
            throw new \RuntimeException(sprintf(
1161
                'Unable to find the template registry related to the current admin (%s)',
1162
                $this->admin->getCode()
1163
            ));
1164
        }
1165
1166
        $rootAdmin = $this->admin;
1167
1168
        while ($rootAdmin->isChild()) {
1169
            $rootAdmin->setCurrentChild(true);
1170
            $rootAdmin = $rootAdmin->getParent();
1171
        }
1172
1173
        $rootAdmin->setRequest($request);
1174
1175
        if ($request->get('uniqid')) {
1176
            $this->admin->setUniqid($request->get('uniqid'));
1177
        }
1178
    }
1179
1180
    /**
1181
     * Proxy for the logger service of the container.
1182
     * If no such service is found, a NullLogger is returned.
1183
     *
1184
     * @return LoggerInterface
1185
     */
1186
    protected function getLogger()
1187
    {
1188
        if ($this->container->has('logger')) {
1189
            $logger = $this->container->get('logger');
1190
            \assert($logger instanceof LoggerInterface);
1191
1192
            return $logger;
1193
        }
1194
1195
        return new NullLogger();
1196
    }
1197
1198
    /**
1199
     * Returns the base template name.
1200
     *
1201
     * @return string The template name
1202
     */
1203
    protected function getBaseTemplate()
1204
    {
1205
        if ($this->isXmlHttpRequest()) {
1206
            // NEXT_MAJOR: Remove this line and use commented line below it instead
1207
            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...
1208
            // return $this->templateRegistry->getTemplate('ajax');
1209
        }
1210
1211
        // NEXT_MAJOR: Remove this line and use commented line below it instead
1212
        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...
1213
        // return $this->templateRegistry->getTemplate('layout');
1214
    }
1215
1216
    /**
1217
     * @throws \Exception
1218
     */
1219
    protected function handleModelManagerException(\Exception $e)
1220
    {
1221
        if ($this->get('kernel')->isDebug()) {
1222
            throw $e;
1223
        }
1224
1225
        $context = ['exception' => $e];
1226
        if ($e->getPrevious()) {
1227
            $context['previous_exception_message'] = $e->getPrevious()->getMessage();
1228
        }
1229
        $this->getLogger()->error($e->getMessage(), $context);
1230
    }
1231
1232
    /**
1233
     * Redirect the user depend on this choice.
1234
     *
1235
     * @param object $object
1236
     *
1237
     * @return RedirectResponse
1238
     */
1239
    protected function redirectTo($object)
1240
    {
1241
        $request = $this->getRequest();
1242
1243
        $url = false;
1244
1245
        if (null !== $request->get('btn_update_and_list')) {
1246
            return $this->redirectToList();
1247
        }
1248
        if (null !== $request->get('btn_create_and_list')) {
1249
            return $this->redirectToList();
1250
        }
1251
1252
        if (null !== $request->get('btn_create_and_create')) {
1253
            $params = [];
1254
            if ($this->admin->hasActiveSubClass()) {
1255
                $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...
1256
            }
1257
            $url = $this->admin->generateUrl('create', $params);
1258
        }
1259
1260
        if ('DELETE' === $this->getRestMethod()) {
1261
            return $this->redirectToList();
1262
        }
1263
1264
        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...
1265
            foreach (['edit', 'show'] as $route) {
1266
                if ($this->admin->hasRoute($route) && $this->admin->hasAccess($route, $object)) {
1267
                    $url = $this->admin->generateObjectUrl(
1268
                        $route,
1269
                        $object,
1270
                        $this->getSelectedTab($request)
1271
                    );
1272
1273
                    break;
1274
                }
1275
            }
1276
        }
1277
1278
        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...
1279
            return $this->redirectToList();
1280
        }
1281
1282
        return new RedirectResponse($url);
1283
    }
1284
1285
    /**
1286
     * Redirects the user to the list view.
1287
     *
1288
     * @return RedirectResponse
1289
     */
1290
    final protected function redirectToList()
1291
    {
1292
        $parameters = [];
1293
1294
        if ($filter = $this->admin->getFilterParameters()) {
1295
            $parameters['filter'] = $filter;
1296
        }
1297
1298
        return $this->redirect($this->admin->generateUrl('list', $parameters));
1299
    }
1300
1301
    /**
1302
     * Returns true if the preview is requested to be shown.
1303
     *
1304
     * @return bool
1305
     */
1306
    protected function isPreviewRequested()
1307
    {
1308
        $request = $this->getRequest();
1309
1310
        return null !== $request->get('btn_preview');
1311
    }
1312
1313
    /**
1314
     * Returns true if the preview has been approved.
1315
     *
1316
     * @return bool
1317
     */
1318
    protected function isPreviewApproved()
1319
    {
1320
        $request = $this->getRequest();
1321
1322
        return null !== $request->get('btn_preview_approve');
1323
    }
1324
1325
    /**
1326
     * Returns true if the request is in the preview workflow.
1327
     *
1328
     * That means either a preview is requested or the preview has already been shown
1329
     * and it got approved/declined.
1330
     *
1331
     * @return bool
1332
     */
1333
    protected function isInPreviewMode()
1334
    {
1335
        return $this->admin->supportsPreviewMode()
1336
        && ($this->isPreviewRequested()
1337
            || $this->isPreviewApproved()
1338
            || $this->isPreviewDeclined());
1339
    }
1340
1341
    /**
1342
     * Returns true if the preview has been declined.
1343
     *
1344
     * @return bool
1345
     */
1346
    protected function isPreviewDeclined()
1347
    {
1348
        $request = $this->getRequest();
1349
1350
        return null !== $request->get('btn_preview_decline');
1351
    }
1352
1353
    /**
1354
     * Gets ACL users.
1355
     *
1356
     * @return \Traversable
1357
     */
1358
    protected function getAclUsers()
1359
    {
1360
        $aclUsers = [];
1361
1362
        $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, Symfony_DI_PhpDumper_Test_Unsupported_Characters.

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...
1363
        if (null !== $userManagerServiceName && $this->has($userManagerServiceName)) {
1364
            $userManager = $this->get($userManagerServiceName);
1365
1366
            if (method_exists($userManager, 'findUsers')) {
1367
                $aclUsers = $userManager->findUsers();
1368
            }
1369
        }
1370
1371
        return \is_array($aclUsers) ? new \ArrayIterator($aclUsers) : $aclUsers;
1372
    }
1373
1374
    /**
1375
     * Gets ACL roles.
1376
     *
1377
     * @return \Traversable
1378
     */
1379
    protected function getAclRoles()
1380
    {
1381
        $aclRoles = [];
1382
        $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, Symfony_DI_PhpDumper_Test_Unsupported_Characters.

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...
1383
        $pool = $this->container->get('sonata.admin.pool');
1384
1385
        foreach ($pool->getAdminServiceIds() as $id) {
1386
            try {
1387
                $admin = $pool->getInstance($id);
1388
            } catch (\Exception $e) {
1389
                continue;
1390
            }
1391
1392
            $baseRole = $admin->getSecurityHandler()->getBaseRole($admin);
1393
            foreach ($admin->getSecurityInformation() as $role => $permissions) {
1394
                $role = sprintf($baseRole, $role);
1395
                $aclRoles[] = $role;
1396
            }
1397
        }
1398
1399
        foreach ($roleHierarchy as $name => $roles) {
1400
            $aclRoles[] = $name;
1401
            $aclRoles = array_merge($aclRoles, $roles);
1402
        }
1403
1404
        $aclRoles = array_unique($aclRoles);
1405
1406
        return \is_array($aclRoles) ? new \ArrayIterator($aclRoles) : $aclRoles;
1407
    }
1408
1409
    /**
1410
     * Validate CSRF token for action without form.
1411
     *
1412
     * @param string $intention
1413
     *
1414
     * @throws HttpException
1415
     */
1416
    protected function validateCsrfToken($intention)
1417
    {
1418
        $request = $this->getRequest();
1419
        $token = $request->get('_sonata_csrf_token');
1420
1421
        if ($this->container->has('security.csrf.token_manager')) {
1422
            $valid = $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($intention, $token));
1423
        } else {
1424
            return;
1425
        }
1426
1427
        if (!$valid) {
1428
            throw new HttpException(400, 'The csrf token is not valid, CSRF attack?');
1429
        }
1430
    }
1431
1432
    /**
1433
     * Escape string for html output.
1434
     *
1435
     * @param string $s
1436
     *
1437
     * @return string
1438
     */
1439
    protected function escapeHtml($s)
1440
    {
1441
        return htmlspecialchars((string) $s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
1442
    }
1443
1444
    /**
1445
     * Get CSRF token.
1446
     *
1447
     * @param string $intention
1448
     *
1449
     * @return string|false
1450
     */
1451
    protected function getCsrfToken($intention)
1452
    {
1453
        if ($this->container->has('security.csrf.token_manager')) {
1454
            return $this->container->get('security.csrf.token_manager')->getToken($intention)->getValue();
1455
        }
1456
1457
        return false;
1458
    }
1459
1460
    /**
1461
     * This method can be overloaded in your custom CRUD controller.
1462
     * It's called from createAction.
1463
     *
1464
     * @param object $object
1465
     *
1466
     * @return Response|null
1467
     */
1468
    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...
1469
    {
1470
        return null;
1471
    }
1472
1473
    /**
1474
     * This method can be overloaded in your custom CRUD controller.
1475
     * It's called from editAction.
1476
     *
1477
     * @param object $object
1478
     *
1479
     * @return Response|null
1480
     */
1481
    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...
1482
    {
1483
        return null;
1484
    }
1485
1486
    /**
1487
     * This method can be overloaded in your custom CRUD controller.
1488
     * It's called from deleteAction.
1489
     *
1490
     * @param object $object
1491
     *
1492
     * @return Response|null
1493
     */
1494
    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...
1495
    {
1496
        return null;
1497
    }
1498
1499
    /**
1500
     * This method can be overloaded in your custom CRUD controller.
1501
     * It's called from showAction.
1502
     *
1503
     * @param object $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
        return null;
1510
    }
1511
1512
    /**
1513
     * This method can be overloaded in your custom CRUD controller.
1514
     * It's called from listAction.
1515
     *
1516
     * @return Response|null
1517
     */
1518
    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...
1519
    {
1520
        return null;
1521
    }
1522
1523
    /**
1524
     * Translate a message id.
1525
     *
1526
     * @param string $id
1527
     * @param string $domain
1528
     * @param string $locale
1529
     *
1530
     * @return string translated string
1531
     */
1532
    final protected function trans($id, array $parameters = [], $domain = null, $locale = null)
1533
    {
1534
        $domain = $domain ?: $this->admin->getTranslationDomain();
1535
1536
        return $this->get('translator')->trans($id, $parameters, $domain, $locale);
1537
    }
1538
1539
    private function getSelectedTab(Request $request)
1540
    {
1541
        return array_filter(['_tab' => $request->request->get('_tab')]);
1542
    }
1543
1544
    private function checkParentChildAssociation(Request $request, $object): void
1545
    {
1546
        if (!($parentAdmin = $this->admin->getParent())) {
1547
            return;
1548
        }
1549
1550
        // NEXT_MAJOR: remove this check
1551
        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...
1552
            return;
1553
        }
1554
1555
        $parentId = $request->get($parentAdmin->getIdParameter());
1556
1557
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
1558
        $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...
1559
1560
        if ($parentAdmin->getObject($parentId) !== $propertyAccessor->getValue($object, $propertyPath)) {
1561
            // NEXT_MAJOR: make this exception
1562
            @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...
1563
                ." and won't be allowed in 4.0.",
1564
                E_USER_DEPRECATED
1565
            );
1566
        }
1567
    }
1568
1569
    /**
1570
     * Sets the admin form theme to form view. Used for compatibility between Symfony versions.
1571
     */
1572
    private function setFormTheme(FormView $formView, array $theme = null): void
1573
    {
1574
        $twig = $this->get('twig');
1575
1576
        // BC for Symfony < 3.2 where this runtime does not exists
1577
        if (!method_exists(AppVariable::class, 'getToken')) {
1578
            $twig->getExtension(FormExtension::class)->renderer->setTheme($formView, $theme);
1579
1580
            return;
1581
        }
1582
1583
        // BC for Symfony < 3.4 where runtime should be TwigRenderer
1584
        if (!method_exists(DebugCommand::class, 'getLoaderPaths')) {
1585
            $twig->getRuntime(TwigRenderer::class)->setTheme($formView, $theme);
1586
1587
            return;
1588
        }
1589
1590
        $twig->getRuntime(FormRenderer::class)->setTheme($formView, $theme);
1591
    }
1592
1593
    private function handleXmlHttpRequestErrorResponse(Request $request, FormInterface $form): ?JsonResponse
1594
    {
1595
        if ('application/json' !== $request->headers->get('Accept')) {
1596
            @trigger_error('In next major version response will return 406 NOT ACCEPTABLE without `Accept: application/json`', E_USER_DEPRECATED);
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...
1597
1598
            return null;
1599
        }
1600
1601
        $errors = [];
1602
        foreach ($form->getErrors(true) as $error) {
1603
            $errors[] = $error->getMessage();
1604
        }
1605
1606
        return $this->renderJson([
1607
            'result' => 'error',
1608
            'errors' => $errors,
1609
        ], 400);
1610
    }
1611
1612
    /**
1613
     * @param object $object
1614
     */
1615
    private function handleXmlHttpRequestSuccessResponse(Request $request, $object): JsonResponse
1616
    {
1617
        if ('application/json' !== $request->headers->get('Accept')) {
1618
            @trigger_error('In next major version response will return 406 NOT ACCEPTABLE without `Accept: application/json`', E_USER_DEPRECATED);
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...
1619
        }
1620
1621
        return $this->renderJson([
1622
            'result' => 'ok',
1623
            'objectId' => $this->admin->getNormalizedIdentifier($object),
1624
            'objectName' => $this->escapeHtml($this->admin->toString($object)),
1625
        ], 200);
1626
    }
1627
}
1628