Completed
Push — 3.x ( 8fbc94...df0d37 )
by Vincent
23:37
created

CRUDController::showAction()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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...
1350
        if (null !== $userManagerServiceName && $this->has($userManagerServiceName)) {
1351
            $userManager = $this->get($userManagerServiceName);
1352
1353
            if (method_exists($userManager, 'findUsers')) {
1354
                $aclUsers = $userManager->findUsers();
1355
            }
1356
        }
1357
1358
        return \is_array($aclUsers) ? new \ArrayIterator($aclUsers) : $aclUsers;
1359
    }
1360
1361
    /**
1362
     * Gets ACL roles.
1363
     *
1364
     * @return \Traversable
1365
     */
1366
    protected function getAclRoles()
1367
    {
1368
        $aclRoles = [];
1369
        $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: Symfony\Bundle\FrameworkBundle\Test\TestContainer, Symfony\Component\Depend...urationContainerBuilder, Symfony\Component\DependencyInjection\Container, Symfony\Component\Depend...ection\ContainerBuilder.

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