Completed
Pull Request — 3.x (#6171)
by Vincent
02:57
created

AbstractAdmin::setListBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
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\Admin;
15
16
use Doctrine\Common\Util\ClassUtils;
17
use Knp\Menu\FactoryInterface;
18
use Knp\Menu\ItemInterface;
19
use Sonata\AdminBundle\Builder\DatagridBuilderInterface;
20
use Sonata\AdminBundle\Builder\FormContractorInterface;
21
use Sonata\AdminBundle\Builder\ListBuilderInterface;
22
use Sonata\AdminBundle\Builder\RouteBuilderInterface;
23
use Sonata\AdminBundle\Builder\ShowBuilderInterface;
24
use Sonata\AdminBundle\Datagrid\DatagridInterface;
25
use Sonata\AdminBundle\Datagrid\DatagridMapper;
26
use Sonata\AdminBundle\Datagrid\ListMapper;
27
use Sonata\AdminBundle\Datagrid\Pager;
28
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
29
use Sonata\AdminBundle\Filter\Persister\FilterPersisterInterface;
30
use Sonata\AdminBundle\Form\FormMapper;
31
use Sonata\AdminBundle\Form\Type\ModelHiddenType;
32
use Sonata\AdminBundle\Model\ModelManagerInterface;
33
use Sonata\AdminBundle\Object\Metadata;
34
use Sonata\AdminBundle\Route\RouteCollection;
35
use Sonata\AdminBundle\Route\RouteGeneratorInterface;
36
use Sonata\AdminBundle\Security\Handler\AclSecurityHandlerInterface;
37
use Sonata\AdminBundle\Security\Handler\SecurityHandlerInterface;
38
use Sonata\AdminBundle\Show\ShowMapper;
39
use Sonata\AdminBundle\Templating\MutableTemplateRegistryInterface;
40
use Sonata\AdminBundle\Translator\LabelTranslatorStrategyInterface;
41
use Sonata\Form\Validator\Constraints\InlineConstraint;
42
use Sonata\Form\Validator\ErrorElement;
43
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
44
use Symfony\Component\Form\Form;
45
use Symfony\Component\Form\FormBuilderInterface;
46
use Symfony\Component\Form\FormEvent;
47
use Symfony\Component\Form\FormEvents;
48
use Symfony\Component\HttpFoundation\Request;
49
use Symfony\Component\Routing\Generator\UrlGeneratorInterface as RoutingUrlGeneratorInterface;
50
use Symfony\Component\Security\Acl\Model\DomainObjectInterface;
51
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
52
use Symfony\Component\Translation\TranslatorInterface;
53
use Symfony\Component\Validator\Validator\ValidatorInterface;
54
55
/**
56
 * @author Thomas Rabaix <[email protected]>
57
 */
58
abstract class AbstractAdmin implements AdminInterface, DomainObjectInterface, AdminTreeInterface
59
{
60
    public const CONTEXT_MENU = 'menu';
61
    public const CONTEXT_DASHBOARD = 'dashboard';
62
63
    public const CLASS_REGEX =
64
        '@
65
        (?:([A-Za-z0-9]*)\\\)?        # vendor name / app name
66
        (Bundle\\\)?                  # optional bundle directory
67
        ([A-Za-z0-9]+?)(?:Bundle)?\\\ # bundle name, with optional suffix
68
        (
69
            Entity|Document|Model|PHPCR|CouchDocument|Phpcr|
70
            Doctrine\\\Orm|Doctrine\\\Phpcr|Doctrine\\\MongoDB|Doctrine\\\CouchDB
71
        )\\\(.*)@x';
72
73
    public const MOSAIC_ICON_CLASS = 'fa fa-th-large fa-fw';
74
75
    /**
76
     * The list FieldDescription constructed from the configureListField method.
77
     *
78
     * @var FieldDescriptionInterface[]
79
     */
80
    protected $listFieldDescriptions = [];
81
82
    /**
83
     * The show FieldDescription constructed from the configureShowFields method.
84
     *
85
     * @var FieldDescriptionInterface[]
86
     */
87
    protected $showFieldDescriptions = [];
88
89
    /**
90
     * The list FieldDescription constructed from the configureFormField method.
91
     *
92
     * @var FieldDescriptionInterface[]
93
     */
94
    protected $formFieldDescriptions = [];
95
96
    /**
97
     * The filter FieldDescription constructed from the configureFilterField method.
98
     *
99
     * @var FieldDescriptionInterface[]
100
     */
101
    protected $filterFieldDescriptions = [];
102
103
    /**
104
     * NEXT_MAJOR: Remove this property.
105
     *
106
     * The number of result to display in the list.
107
     *
108
     * @deprecated since sonata-project/admin-bundle 3.67.
109
     *
110
     * @var int
111
     */
112
    protected $maxPerPage = 32;
113
114
    /**
115
     * The maximum number of page numbers to display in the list.
116
     *
117
     * @var int
118
     */
119
    protected $maxPageLinks = 25;
120
121
    /**
122
     * The base route name used to generate the routing information.
123
     *
124
     * @var string
125
     */
126
    protected $baseRouteName;
127
128
    /**
129
     * The base route pattern used to generate the routing information.
130
     *
131
     * @var string
132
     */
133
    protected $baseRoutePattern;
134
135
    /**
136
     * The base name controller used to generate the routing information.
137
     *
138
     * @var string
139
     */
140
    protected $baseControllerName;
141
142
    /**
143
     * The label class name  (used in the title/breadcrumb ...).
144
     *
145
     * @var string
146
     */
147
    protected $classnameLabel;
148
149
    /**
150
     * The translation domain to be used to translate messages.
151
     *
152
     * @var string
153
     */
154
    protected $translationDomain = 'messages';
155
156
    /**
157
     * Options to set to the form (ie, validation_groups).
158
     *
159
     * @var array
160
     */
161
    protected $formOptions = [];
162
163
    /**
164
     * NEXT_MAJOR: Remove this property.
165
     *
166
     * Default values to the datagrid.
167
     *
168
     * @deprecated since sonata-project/admin-bundle 3.67, use configureDefaultSortValues() instead.
169
     *
170
     * @var array
171
     */
172
    protected $datagridValues = [
173
        '_page' => 1,
174
        '_per_page' => 32,
175
    ];
176
177
    /**
178
     * NEXT_MAJOR: Remove this property.
179
     *
180
     * Predefined per page options.
181
     *
182
     * @deprecated since sonata-project/admin-bundle 3.67.
183
     *
184
     * @var array
185
     */
186
    protected $perPageOptions = [16, 32, 64, 128, 256];
187
188
    /**
189
     * Pager type.
190
     *
191
     * @var string
192
     */
193
    protected $pagerType = Pager::TYPE_DEFAULT;
194
195
    /**
196
     * The code related to the admin.
197
     *
198
     * @var string
199
     */
200
    protected $code;
201
202
    /**
203
     * The label.
204
     *
205
     * @var string
206
     */
207
    protected $label;
208
209
    /**
210
     * Whether or not to persist the filters in the session.
211
     *
212
     * NEXT_MAJOR: remove this property
213
     *
214
     * @var bool
215
     *
216
     * @deprecated since sonata-project/admin-bundle 3.34, to be removed in 4.0.
217
     */
218
    protected $persistFilters = false;
219
220
    /**
221
     * Array of routes related to this admin.
222
     *
223
     * @var RouteCollection
224
     */
225
    protected $routes;
226
227
    /**
228
     * The subject only set in edit/update/create mode.
229
     *
230
     * @var object|null
231
     */
232
    protected $subject;
233
234
    /**
235
     * Define a Collection of child admin, ie /admin/order/{id}/order-element/{childId}.
236
     *
237
     * @var array
238
     */
239
    protected $children = [];
240
241
    /**
242
     * Reference the parent admin.
243
     *
244
     * @var AdminInterface|null
245
     */
246
    protected $parent;
247
248
    /**
249
     * The base code route refer to the prefix used to generate the route name.
250
     *
251
     * NEXT_MAJOR: remove this attribute.
252
     *
253
     * @deprecated This attribute is deprecated since sonata-project/admin-bundle 3.24 and will be removed in 4.0
254
     *
255
     * @var string
256
     */
257
    protected $baseCodeRoute = '';
258
259
    /**
260
     * NEXT_MAJOR: should be default array and private.
261
     *
262
     * @var string|array
263
     */
264
    protected $parentAssociationMapping;
265
266
    /**
267
     * Reference the parent FieldDescription related to this admin
268
     * only set for FieldDescription which is associated to an Sub Admin instance.
269
     *
270
     * @var FieldDescriptionInterface
271
     */
272
    protected $parentFieldDescription;
273
274
    /**
275
     * If true then the current admin is part of the nested admin set (from the url).
276
     *
277
     * @var bool
278
     */
279
    protected $currentChild = false;
280
281
    /**
282
     * The uniqid is used to avoid clashing with 2 admin related to the code
283
     * ie: a Block linked to a Block.
284
     *
285
     * @var string
286
     */
287
    protected $uniqid;
288
289
    /**
290
     * The Entity or Document manager.
291
     *
292
     * @var ModelManagerInterface
293
     */
294
    protected $modelManager;
295
296
    /**
297
     * The current request object.
298
     *
299
     * @var Request|null
300
     */
301
    protected $request;
302
303
    /**
304
     * The translator component.
305
     *
306
     * NEXT_MAJOR: remove this property
307
     *
308
     * @var \Symfony\Component\Translation\TranslatorInterface
309
     *
310
     * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
311
     */
312
    protected $translator;
313
314
    /**
315
     * The related form contractor.
316
     *
317
     * @var FormContractorInterface
318
     */
319
    protected $formContractor;
320
321
    /**
322
     * The related list builder.
323
     *
324
     * @var ListBuilderInterface
325
     */
326
    protected $listBuilder;
327
328
    /**
329
     * The related view builder.
330
     *
331
     * @var ShowBuilderInterface
332
     */
333
    protected $showBuilder;
334
335
    /**
336
     * The related datagrid builder.
337
     *
338
     * @var DatagridBuilderInterface
339
     */
340
    protected $datagridBuilder;
341
342
    /**
343
     * @var RouteBuilderInterface
344
     */
345
    protected $routeBuilder;
346
347
    /**
348
     * The datagrid instance.
349
     *
350
     * @var DatagridInterface|null
351
     */
352
    protected $datagrid;
353
354
    /**
355
     * The router instance.
356
     *
357
     * @var RouteGeneratorInterface|null
358
     */
359
    protected $routeGenerator;
360
361
    /**
362
     * The generated breadcrumbs.
363
     *
364
     * NEXT_MAJOR : remove this property
365
     *
366
     * @var array
367
     */
368
    protected $breadcrumbs = [];
369
370
    /**
371
     * @var SecurityHandlerInterface
372
     */
373
    protected $securityHandler;
374
375
    /**
376
     * @var ValidatorInterface
377
     */
378
    protected $validator;
379
380
    /**
381
     * The configuration pool.
382
     *
383
     * @var Pool
384
     */
385
    protected $configurationPool;
386
387
    /**
388
     * @var ItemInterface
389
     */
390
    protected $menu;
391
392
    /**
393
     * @var FactoryInterface
394
     */
395
    protected $menuFactory;
396
397
    /**
398
     * @var array<string, bool>
399
     */
400
    protected $loaded = [
401
        'view_fields' => false, // NEXT_MAJOR: Remove this unused value.
402
        'view_groups' => false, // NEXT_MAJOR: Remove this unused value.
403
        'routes' => false,
404
        'tab_menu' => false,
405
        'show' => false,
406
        'list' => false,
407
        'form' => false,
408
        'datagrid' => false,
409
    ];
410
411
    /**
412
     * @var string[]
413
     */
414
    protected $formTheme = [];
415
416
    /**
417
     * @var string[]
418
     */
419
    protected $filterTheme = [];
420
421
    /**
422
     * @var array<string, string>
423
     *
424
     * @deprecated since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead
425
     */
426
    protected $templates = [];
427
428
    /**
429
     * @var AdminExtensionInterface[]
430
     */
431
    protected $extensions = [];
432
433
    /**
434
     * @var LabelTranslatorStrategyInterface
435
     */
436
    protected $labelTranslatorStrategy;
437
438
    /**
439
     * Setting to true will enable preview mode for
440
     * the entity and show a preview button in the
441
     * edit/create forms.
442
     *
443
     * @var bool
444
     */
445
    protected $supportsPreviewMode = false;
446
447
    /**
448
     * Roles and permissions per role.
449
     *
450
     * @var array 'role' => ['permission', 'permission']
451
     */
452
    protected $securityInformation = [];
453
454
    protected $cacheIsGranted = [];
455
456
    /**
457
     * Action list for the search result.
458
     *
459
     * @var string[]
460
     */
461
    protected $searchResultActions = ['edit', 'show'];
462
463
    protected $listModes = [
464
        'list' => [
465
            'class' => 'fa fa-list fa-fw',
466
        ],
467
        'mosaic' => [
468
            'class' => self::MOSAIC_ICON_CLASS,
469
        ],
470
    ];
471
472
    /**
473
     * The Access mapping.
474
     *
475
     * @var array [action1 => requiredRole1, action2 => [requiredRole2, requiredRole3]]
476
     */
477
    protected $accessMapping = [];
478
479
    /**
480
     * @var MutableTemplateRegistryInterface
481
     */
482
    private $templateRegistry;
483
484
    /**
485
     * The class name managed by the admin class.
486
     *
487
     * @var string
488
     */
489
    private $class;
490
491
    /**
492
     * The subclasses supported by the admin class.
493
     *
494
     * @var array<string, string>
495
     */
496
    private $subClasses = [];
497
498
    /**
499
     * The list collection.
500
     *
501
     * @var FieldDescriptionCollection|null
502
     */
503
    private $list;
504
505
    /**
506
     * @var FieldDescriptionCollection|null
507
     */
508
    private $show;
509
510
    /**
511
     * @var Form|null
512
     */
513
    private $form;
514
515
    /**
516
     * The cached base route name.
517
     *
518
     * @var string
519
     */
520
    private $cachedBaseRouteName;
521
522
    /**
523
     * The cached base route pattern.
524
     *
525
     * @var string
526
     */
527
    private $cachedBaseRoutePattern;
528
529
    /**
530
     * The form group disposition.
531
     *
532
     * NEXT_MAJOR: must have `[]` as default value and remove the possibility to
533
     * hold boolean values.
534
     *
535
     * @var array|bool
536
     */
537
    private $formGroups = false;
538
539
    /**
540
     * The form tabs disposition.
541
     *
542
     * NEXT_MAJOR: must have `[]` as default value and remove the possibility to
543
     * hold boolean values.
544
     *
545
     * @var array|bool
546
     */
547
    private $formTabs = false;
548
549
    /**
550
     * The view group disposition.
551
     *
552
     * NEXT_MAJOR: must have `[]` as default value and remove the possibility to
553
     * hold boolean values.
554
     *
555
     * @var array|bool
556
     */
557
    private $showGroups = false;
558
559
    /**
560
     * The view tab disposition.
561
     *
562
     * NEXT_MAJOR: must have `[]` as default value and remove the possibility to
563
     * hold boolean values.
564
     *
565
     * @var array|bool
566
     */
567
    private $showTabs = false;
568
569
    /**
570
     * The manager type to use for the admin.
571
     *
572
     * @var string
573
     */
574
    private $managerType;
575
576
    /**
577
     * The breadcrumbsBuilder component.
578
     *
579
     * @var BreadcrumbsBuilderInterface
580
     */
581
    private $breadcrumbsBuilder;
582
583
    /**
584
     * Component responsible for persisting filters.
585
     *
586
     * @var FilterPersisterInterface|null
587
     */
588
    private $filterPersister;
589
590
    /**
591
     * @param string      $code
592
     * @param string      $class
593
     * @param string|null $baseControllerName
594
     */
595
    public function __construct($code, $class, $baseControllerName = null)
596
    {
597
        if (!\is_string($code)) {
598
            @trigger_error(sprintf(
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...
599
                'Passing other type than string as argument 1 for method %s() is deprecated since sonata-project/admin-bundle 3.65. It will accept only string in version 4.0.',
600
                __METHOD__
601
            ), E_USER_DEPRECATED);
602
        }
603
        $this->code = $code;
604
        if (!\is_string($class)) {
605
            @trigger_error(sprintf(
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...
606
                'Passing other type than string as argument 2 for method %s() is deprecated since sonata-project/admin-bundle 3.65. It will accept only string in version 4.0.',
607
                __METHOD__
608
            ), E_USER_DEPRECATED);
609
        }
610
        $this->class = $class;
611
        if (null !== $baseControllerName && !\is_string($baseControllerName)) {
612
            @trigger_error(sprintf(
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...
613
                'Passing other type than string or null as argument 3 for method %s() is deprecated since sonata-project/admin-bundle 3.65. It will accept only string and null in version 4.0.',
614
                __METHOD__
615
            ), E_USER_DEPRECATED);
616
        }
617
        $this->baseControllerName = $baseControllerName;
618
619
        // NEXT_MAJOR: Remove this line.
620
        $this->predefinePerPageOptions();
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...edefinePerPageOptions() has been deprecated with message: since sonata-project/admin-bundle 3.67, to be removed in 4.0. Predefine per page options.

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...
621
622
        // NEXT_MAJOR: Remove this line.
623
        $this->datagridValues['_per_page'] = $this->maxPerPage;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$datagridValues has been deprecated with message: since sonata-project/admin-bundle 3.67, use configureDefaultSortValues() instead.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$maxPerPage has been deprecated with message: since sonata-project/admin-bundle 3.67.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
624
    }
625
626
    /**
627
     * {@inheritdoc}
628
     */
629
    public function getExportFormats()
630
    {
631
        return [
632
            'json', 'xml', 'csv', 'xls',
633
        ];
634
    }
635
636
    /**
637
     * @return array
638
     */
639
    public function getExportFields()
640
    {
641
        $fields = $this->getModelManager()->getExportFields($this->getClass());
642
643
        foreach ($this->getExtensions() as $extension) {
644
            if (method_exists($extension, 'configureExportFields')) {
645
                $fields = $extension->configureExportFields($this, $fields);
646
            }
647
        }
648
649
        return $fields;
650
    }
651
652
    public function getDataSourceIterator()
653
    {
654
        $datagrid = $this->getDatagrid();
655
        $datagrid->buildPager();
656
657
        $fields = [];
658
659
        foreach ($this->getExportFields() as $key => $field) {
660
            $label = $this->getTranslationLabel($field, 'export', 'label');
661
            $transLabel = $this->trans($label);
662
663
            // NEXT_MAJOR: Remove this hack, because all field labels will be translated with the major release
664
            // No translation key exists
665
            if ($transLabel === $label) {
666
                $fields[$key] = $field;
667
            } else {
668
                $fields[$transLabel] = $field;
669
            }
670
        }
671
672
        return $this->getModelManager()->getDataSourceIterator($datagrid, $fields);
0 ignored issues
show
Bug introduced by
It seems like $datagrid defined by $this->getDatagrid() on line 654 can be null; however, Sonata\AdminBundle\Model...getDataSourceIterator() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
673
    }
674
675
    public function validate(ErrorElement $errorElement, $object)
676
    {
677
    }
678
679
    /**
680
     * define custom variable.
681
     */
682
    public function initialize()
683
    {
684
        if (!$this->classnameLabel) {
685
            /* NEXT_MAJOR: remove cast to string, null is not supposed to be
686
            supported but was documented as such */
687
            $this->classnameLabel = substr(
688
                (string) $this->getClass(),
689
                strrpos((string) $this->getClass(), '\\') + 1
690
            );
691
        }
692
693
        // NEXT_MAJOR: Remove this line.
694
        $this->baseCodeRoute = $this->getCode();
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...ctAdmin::$baseCodeRoute has been deprecated with message: This attribute is deprecated since sonata-project/admin-bundle 3.24 and will be removed in 4.0

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
695
696
        $this->configure();
697
    }
698
699
    public function configure()
700
    {
701
    }
702
703
    public function update($object)
704
    {
705
        $this->preUpdate($object);
706
        foreach ($this->extensions as $extension) {
707
            $extension->preUpdate($this, $object);
708
        }
709
710
        $result = $this->getModelManager()->update($object);
711
        // BC compatibility
712
        if (null !== $result) {
713
            $object = $result;
714
        }
715
716
        $this->postUpdate($object);
717
        foreach ($this->extensions as $extension) {
718
            $extension->postUpdate($this, $object);
719
        }
720
721
        return $object;
722
    }
723
724
    public function create($object)
725
    {
726
        $this->prePersist($object);
727
        foreach ($this->extensions as $extension) {
728
            $extension->prePersist($this, $object);
729
        }
730
731
        $result = $this->getModelManager()->create($object);
732
        // BC compatibility
733
        if (null !== $result) {
734
            $object = $result;
735
        }
736
737
        $this->postPersist($object);
738
        foreach ($this->extensions as $extension) {
739
            $extension->postPersist($this, $object);
740
        }
741
742
        $this->createObjectSecurity($object);
743
744
        return $object;
745
    }
746
747
    public function delete($object)
748
    {
749
        $this->preRemove($object);
750
        foreach ($this->extensions as $extension) {
751
            $extension->preRemove($this, $object);
752
        }
753
754
        $this->getSecurityHandler()->deleteObjectSecurity($this, $object);
755
        $this->getModelManager()->delete($object);
756
757
        $this->postRemove($object);
758
        foreach ($this->extensions as $extension) {
759
            $extension->postRemove($this, $object);
760
        }
761
    }
762
763
    /**
764
     * @param object $object
765
     */
766
    public function preValidate($object)
0 ignored issues
show
Unused Code introduced by
The parameter $object 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...
767
    {
768
    }
769
770
    public function preUpdate($object)
771
    {
772
    }
773
774
    public function postUpdate($object)
775
    {
776
    }
777
778
    public function prePersist($object)
779
    {
780
    }
781
782
    public function postPersist($object)
783
    {
784
    }
785
786
    public function preRemove($object)
787
    {
788
    }
789
790
    public function postRemove($object)
791
    {
792
    }
793
794
    public function preBatchAction($actionName, ProxyQueryInterface $query, array &$idx, $allElements)
795
    {
796
    }
797
798
    public function getFilterParameters()
799
    {
800
        $parameters = [];
801
802
        // build the values array
803
        if ($this->hasRequest()) {
804
            $filters = $this->request->query->get('filter', []);
805
            if (isset($filters['_page'])) {
806
                $filters['_page'] = (int) $filters['_page'];
807
            }
808
            if (isset($filters['_per_page'])) {
809
                $filters['_per_page'] = (int) $filters['_per_page'];
810
            }
811
812
            // if filter persistence is configured
813
            // NEXT_MAJOR: remove `$this->persistFilters !== false` from the condition
814
            if (false !== $this->persistFilters && null !== $this->filterPersister) {
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$persistFilters has been deprecated with message: since sonata-project/admin-bundle 3.34, to be removed in 4.0.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
815
                // if reset filters is asked, remove from storage
816
                if ('reset' === $this->request->query->get('filters')) {
817
                    $this->filterPersister->reset($this->getCode());
818
                }
819
820
                // if no filters, fetch from storage
821
                // otherwise save to storage
822
                if (empty($filters)) {
823
                    $filters = $this->filterPersister->get($this->getCode());
824
                } else {
825
                    $this->filterPersister->set($this->getCode(), $filters);
826
                }
827
            }
828
829
            $parameters = array_merge(
830
                $this->getModelManager()->getDefaultSortValues($this->getClass()),
831
                $this->datagridValues, // NEXT_MAJOR: Remove this line.
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$datagridValues has been deprecated with message: since sonata-project/admin-bundle 3.67, use configureDefaultSortValues() instead.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
832
                $this->getDefaultSortValues(),
833
                $this->getDefaultFilterValues(),
834
                $filters
835
            );
836
837
            if (!$this->determinedPerPageValue($parameters['_per_page'])) {
838
                $parameters['_per_page'] = $this->getMaxPerPage();
839
            }
840
841
            // always force the parent value
842
            if ($this->isChild() && $this->getParentAssociationMapping()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getParentAssociationMapping() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null 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...
843
                $name = str_replace('.', '__', $this->getParentAssociationMapping());
844
                $parameters[$name] = ['value' => $this->request->get($this->getParent()->getIdParameter())];
845
            }
846
        }
847
848
        return $parameters;
849
    }
850
851
    /**
852
     * NEXT_MAJOR: Change the visibility to protected (similar to buildShow, buildForm, ...).
853
     */
854
    public function buildDatagrid()
855
    {
856
        if ($this->loaded['datagrid']) {
857
            return;
858
        }
859
860
        $this->loaded['datagrid'] = true;
861
862
        $filterParameters = $this->getFilterParameters();
863
864
        // transform _sort_by from a string to a FieldDescriptionInterface for the datagrid.
865
        if (isset($filterParameters['_sort_by']) && \is_string($filterParameters['_sort_by'])) {
866
            if ($this->hasListFieldDescription($filterParameters['_sort_by'])) {
867
                $filterParameters['_sort_by'] = $this->getListFieldDescription($filterParameters['_sort_by']);
868
            } else {
869
                $filterParameters['_sort_by'] = $this->getModelManager()->getNewFieldDescriptionInstance(
870
                    $this->getClass(),
871
                    $filterParameters['_sort_by'],
872
                    []
873
                );
874
875
                $this->getListBuilder()->buildField(null, $filterParameters['_sort_by'], $this);
876
            }
877
        }
878
879
        // initialize the datagrid
880
        $this->datagrid = $this->getDatagridBuilder()->getBaseDatagrid($this, $filterParameters);
881
882
        $this->datagrid->getPager()->setMaxPageLinks($this->maxPageLinks);
883
884
        $mapper = new DatagridMapper($this->getDatagridBuilder(), $this->datagrid, $this);
885
886
        // build the datagrid filter
887
        $this->configureDatagridFilters($mapper);
888
889
        // ok, try to limit to add parent filter
890
        if ($this->isChild() && $this->getParentAssociationMapping() && !$mapper->has($this->getParentAssociationMapping())) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getParentAssociationMapping() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null 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...
891
            $mapper->add($this->getParentAssociationMapping(), null, [
892
                'show_filter' => false,
893
                'label' => false,
894
                'field_type' => ModelHiddenType::class,
895
                'field_options' => [
896
                    'model_manager' => $this->getModelManager(),
897
                ],
898
                'operator_type' => HiddenType::class,
899
            ], null, null, [
900
                'admin_code' => $this->getParent()->getCode(),
901
            ]);
902
        }
903
904
        foreach ($this->getExtensions() as $extension) {
905
            $extension->configureDatagridFilters($mapper);
906
        }
907
    }
908
909
    /**
910
     * Returns the name of the parent related field, so the field can be use to set the default
911
     * value (ie the parent object) or to filter the object.
912
     *
913
     * @throws \InvalidArgumentException
914
     *
915
     * @return string|null
916
     */
917
    public function getParentAssociationMapping()
918
    {
919
        // NEXT_MAJOR: remove array check
920
        if (\is_array($this->parentAssociationMapping) && $this->isChild()) {
921
            $parent = $this->getParent()->getCode();
922
923
            if (\array_key_exists($parent, $this->parentAssociationMapping)) {
924
                return $this->parentAssociationMapping[$parent];
925
            }
926
927
            throw new \InvalidArgumentException(sprintf(
928
                "There's no association between %s and %s.",
929
                $this->getCode(),
930
                $this->getParent()->getCode()
931
            ));
932
        }
933
934
        // NEXT_MAJOR: remove this line
935
        return $this->parentAssociationMapping;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->parentAssociationMapping; of type string|array adds the type array to the return on line 935 which is incompatible with the return type documented by Sonata\AdminBundle\Admin...arentAssociationMapping of type string|null.
Loading history...
936
    }
937
938
    /**
939
     * @param string $code
940
     * @param string $value
941
     */
942
    final public function addParentAssociationMapping($code, $value)
943
    {
944
        $this->parentAssociationMapping[$code] = $value;
945
    }
946
947
    /**
948
     * Returns the baseRoutePattern used to generate the routing information.
949
     *
950
     * @throws \RuntimeException
951
     *
952
     * @return string the baseRoutePattern used to generate the routing information
953
     */
954
    public function getBaseRoutePattern()
955
    {
956
        if (null !== $this->cachedBaseRoutePattern) {
957
            return $this->cachedBaseRoutePattern;
958
        }
959
960
        if ($this->isChild()) { // the admin class is a child, prefix it with the parent route pattern
961
            $baseRoutePattern = $this->baseRoutePattern;
962
            if (!$this->baseRoutePattern) {
963
                preg_match(self::CLASS_REGEX, $this->class, $matches);
964
965
                if (!$matches) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matches of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
966
                    throw new \RuntimeException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`', static::class));
967
                }
968
                $baseRoutePattern = $this->urlize($matches[5], '-');
969
            }
970
971
            $this->cachedBaseRoutePattern = sprintf(
972
                '%s/%s/%s',
973
                $this->getParent()->getBaseRoutePattern(),
974
                $this->getParent()->getRouterIdParameter(),
975
                $baseRoutePattern
976
            );
977
        } elseif ($this->baseRoutePattern) {
978
            $this->cachedBaseRoutePattern = $this->baseRoutePattern;
979
        } else {
980
            preg_match(self::CLASS_REGEX, $this->class, $matches);
981
982
            if (!$matches) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matches of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
983
                throw new \RuntimeException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`', static::class));
984
            }
985
986
            $this->cachedBaseRoutePattern = sprintf(
987
                '/%s%s/%s',
988
                empty($matches[1]) ? '' : $this->urlize($matches[1], '-').'/',
989
                $this->urlize($matches[3], '-'),
990
                $this->urlize($matches[5], '-')
991
            );
992
        }
993
994
        return $this->cachedBaseRoutePattern;
995
    }
996
997
    /**
998
     * Returns the baseRouteName used to generate the routing information.
999
     *
1000
     * @throws \RuntimeException
1001
     *
1002
     * @return string the baseRouteName used to generate the routing information
1003
     */
1004
    public function getBaseRouteName()
1005
    {
1006
        if (null !== $this->cachedBaseRouteName) {
1007
            return $this->cachedBaseRouteName;
1008
        }
1009
1010
        if ($this->isChild()) { // the admin class is a child, prefix it with the parent route name
1011
            $baseRouteName = $this->baseRouteName;
1012
            if (!$this->baseRouteName) {
1013
                preg_match(self::CLASS_REGEX, $this->class, $matches);
1014
1015
                if (!$matches) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matches of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1016
                    throw new \RuntimeException(sprintf('Cannot automatically determine base route name, please define a default `baseRouteName` value for the admin class `%s`', static::class));
1017
                }
1018
                $baseRouteName = $this->urlize($matches[5]);
1019
            }
1020
1021
            $this->cachedBaseRouteName = sprintf(
1022
                '%s_%s',
1023
                $this->getParent()->getBaseRouteName(),
1024
                $baseRouteName
1025
            );
1026
        } elseif ($this->baseRouteName) {
1027
            $this->cachedBaseRouteName = $this->baseRouteName;
1028
        } else {
1029
            preg_match(self::CLASS_REGEX, $this->class, $matches);
1030
1031
            if (!$matches) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matches of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1032
                throw new \RuntimeException(sprintf('Cannot automatically determine base route name, please define a default `baseRouteName` value for the admin class `%s`', static::class));
1033
            }
1034
1035
            $this->cachedBaseRouteName = sprintf(
1036
                'admin_%s%s_%s',
1037
                empty($matches[1]) ? '' : $this->urlize($matches[1]).'_',
1038
                $this->urlize($matches[3]),
1039
                $this->urlize($matches[5])
1040
            );
1041
        }
1042
1043
        return $this->cachedBaseRouteName;
1044
    }
1045
1046
    /**
1047
     * urlize the given word.
1048
     *
1049
     * @param string $word
1050
     * @param string $sep  the separator
1051
     *
1052
     * @return string
1053
     */
1054
    public function urlize($word, $sep = '_')
1055
    {
1056
        return strtolower(preg_replace('/[^a-z0-9_]/i', $sep.'$1', $word));
1057
    }
1058
1059
    public function getClass()
1060
    {
1061
        if ($this->hasActiveSubClass()) {
1062
            if ($this->hasParentFieldDescription()) {
1063
                throw new \RuntimeException('Feature not implemented: an embedded admin cannot have subclass');
1064
            }
1065
1066
            $subClass = $this->getRequest()->query->get('subclass');
1067
1068
            if (!$this->hasSubClass($subClass)) {
1069
                throw new \RuntimeException(sprintf('Subclass "%s" is not defined.', $subClass));
1070
            }
1071
1072
            return $this->getSubClass($subClass);
1073
        }
1074
1075
        // see https://github.com/sonata-project/SonataCoreBundle/commit/247eeb0a7ca7211142e101754769d70bc402a5b4
1076
        if ($this->subject && \is_object($this->subject)) {
1077
            return ClassUtils::getClass($this->subject);
1078
        }
1079
1080
        return $this->class;
1081
    }
1082
1083
    public function getSubClasses()
1084
    {
1085
        return $this->subClasses;
1086
    }
1087
1088
    /**
1089
     * NEXT_MAJOR: remove this method.
1090
     */
1091
    public function addSubClass($subClass)
1092
    {
1093
        @trigger_error(sprintf(
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...
1094
            'Method "%s" is deprecated since sonata-project/admin-bundle 3.30 and will be removed in 4.0.',
1095
            __METHOD__
1096
        ), E_USER_DEPRECATED);
1097
1098
        if (!\in_array($subClass, $this->subClasses, true)) {
1099
            $this->subClasses[] = $subClass;
1100
        }
1101
    }
1102
1103
    public function setSubClasses(array $subClasses)
1104
    {
1105
        $this->subClasses = $subClasses;
1106
    }
1107
1108
    public function hasSubClass($name)
1109
    {
1110
        return isset($this->subClasses[$name]);
1111
    }
1112
1113
    public function hasActiveSubClass()
1114
    {
1115
        if (\count($this->subClasses) > 0 && $this->request) {
1116
            return null !== $this->getRequest()->query->get('subclass');
1117
        }
1118
1119
        return false;
1120
    }
1121
1122
    public function getActiveSubClass()
1123
    {
1124
        if (!$this->hasActiveSubClass()) {
1125
            @trigger_error(sprintf(
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...
1126
                'Calling %s() when there is no active subclass is deprecated since sonata-project/admin-bundle 3.52 and will throw an exception in 4.0. '.
1127
                'Use %s::hasActiveSubClass() to know if there is an active subclass.',
1128
                __METHOD__,
1129
                __CLASS__
1130
            ), E_USER_DEPRECATED);
1131
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare string as return type
1132
            // throw new \LogicException(sprintf(
1133
            //    'Admin "%s" has no active subclass.',
1134
            //    static::class
1135
            // ));
1136
1137
            return null;
1138
        }
1139
1140
        return $this->getSubClass($this->getActiveSubclassCode());
1141
    }
1142
1143
    public function getActiveSubclassCode()
1144
    {
1145
        if (!$this->hasActiveSubClass()) {
1146
            @trigger_error(sprintf(
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...
1147
                'Calling %s() when there is no active subclass is deprecated since sonata-project/admin-bundle 3.52 and will throw an exception in 4.0. '.
1148
                'Use %s::hasActiveSubClass() to know if there is an active subclass.',
1149
                __METHOD__,
1150
                __CLASS__
1151
            ), E_USER_DEPRECATED);
1152
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare string as return type
1153
            // throw new \LogicException(sprintf(
1154
            //    'Admin "%s" has no active subclass.',
1155
            //    static::class
1156
            // ));
1157
1158
            return null;
1159
        }
1160
1161
        $subClass = $this->getRequest()->query->get('subclass');
1162
1163
        if (!$this->hasSubClass($subClass)) {
1164
            @trigger_error(sprintf(
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...
1165
                'Calling %s() when there is no active subclass is deprecated since sonata-project/admin-bundle 3.52 and will throw an exception in 4.0. '.
1166
                'Use %s::hasActiveSubClass() to know if there is an active subclass.',
1167
                __METHOD__,
1168
                __CLASS__
1169
            ), E_USER_DEPRECATED);
1170
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare string as return type
1171
            // throw new \LogicException(sprintf(
1172
            //    'Admin "%s" has no active subclass.',
1173
            //    static::class
1174
            // ));
1175
1176
            return null;
1177
        }
1178
1179
        return $subClass;
1180
    }
1181
1182
    public function getBatchActions()
1183
    {
1184
        $actions = [];
1185
1186
        if ($this->hasRoute('delete') && $this->hasAccess('delete')) {
1187
            $actions['delete'] = [
1188
                'label' => 'action_delete',
1189
                'translation_domain' => 'SonataAdminBundle',
1190
                'ask_confirmation' => true, // by default always true
1191
            ];
1192
        }
1193
1194
        $actions = $this->configureBatchActions($actions);
1195
1196
        foreach ($this->getExtensions() as $extension) {
1197
            // NEXT_MAJOR: remove method check
1198
            if (method_exists($extension, 'configureBatchActions')) {
1199
                $actions = $extension->configureBatchActions($this, $actions);
1200
            }
1201
        }
1202
1203
        foreach ($actions  as $name => &$action) {
1204
            if (!\array_key_exists('label', $action)) {
1205
                $action['label'] = $this->getTranslationLabel($name, 'batch', 'label');
1206
            }
1207
1208
            if (!\array_key_exists('translation_domain', $action)) {
1209
                $action['translation_domain'] = $this->getTranslationDomain();
1210
            }
1211
        }
1212
1213
        return $actions;
1214
    }
1215
1216
    public function getRoutes()
1217
    {
1218
        $this->buildRoutes();
1219
1220
        return $this->routes;
1221
    }
1222
1223
    public function getRouterIdParameter()
1224
    {
1225
        return '{'.$this->getIdParameter().'}';
1226
    }
1227
1228
    public function getIdParameter()
1229
    {
1230
        $parameter = 'id';
1231
1232
        for ($i = 0; $i < $this->getChildDepth(); ++$i) {
1233
            $parameter = 'child'.ucfirst($parameter);
1234
        }
1235
1236
        return $parameter;
1237
    }
1238
1239
    public function hasRoute($name)
1240
    {
1241
        if (!$this->routeGenerator) {
1242
            throw new \RuntimeException('RouteGenerator cannot be null');
1243
        }
1244
1245
        return $this->routeGenerator->hasAdminRoute($this, $name);
1246
    }
1247
1248
    /**
1249
     * @param string      $name
1250
     * @param string|null $adminCode
1251
     *
1252
     * @return bool
1253
     */
1254
    public function isCurrentRoute($name, $adminCode = null)
1255
    {
1256
        if (!$this->hasRequest()) {
1257
            return false;
1258
        }
1259
1260
        $request = $this->getRequest();
1261
        $route = $request->get('_route');
1262
1263
        if ($adminCode) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $adminCode of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null 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...
1264
            $admin = $this->getConfigurationPool()->getAdminByAdminCode($adminCode);
1265
        } else {
1266
            $admin = $this;
1267
        }
1268
1269
        if (!$admin) {
1270
            return false;
1271
        }
1272
1273
        return ($admin->getBaseRouteName().'_'.$name) === $route;
1274
    }
1275
1276
    public function generateObjectUrl($name, $object, array $parameters = [], $referenceType = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
1277
    {
1278
        $parameters['id'] = $this->getUrlSafeIdentifier($object);
1279
1280
        return $this->generateUrl($name, $parameters, $referenceType);
1281
    }
1282
1283
    public function generateUrl($name, array $parameters = [], $referenceType = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
1284
    {
1285
        return $this->routeGenerator->generateUrl($this, $name, $parameters, $referenceType);
1286
    }
1287
1288
    public function generateMenuUrl($name, array $parameters = [], $referenceType = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
1289
    {
1290
        return $this->routeGenerator->generateMenuUrl($this, $name, $parameters, $referenceType);
1291
    }
1292
1293
    final public function setTemplateRegistry(MutableTemplateRegistryInterface $templateRegistry)
1294
    {
1295
        $this->templateRegistry = $templateRegistry;
1296
    }
1297
1298
    /**
1299
     * @param array<string, string> $templates
1300
     */
1301
    public function setTemplates(array $templates)
1302
    {
1303
        // NEXT_MAJOR: Remove this line
1304
        $this->templates = $templates;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin\AbstractAdmin::$templates has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
1305
1306
        $this->getTemplateRegistry()->setTemplates($templates);
1307
    }
1308
1309
    /**
1310
     * @param string $name
1311
     * @param string $template
1312
     */
1313
    public function setTemplate($name, $template)
1314
    {
1315
        // NEXT_MAJOR: Remove this line
1316
        $this->templates[$name] = $template;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin\AbstractAdmin::$templates has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
1317
1318
        $this->getTemplateRegistry()->setTemplate($name, $template);
1319
    }
1320
1321
    /**
1322
     * @deprecated since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead
1323
     *
1324
     * @return array<string, string>
0 ignored issues
show
Documentation introduced by
The doc-type array<string, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1325
     */
1326
    public function getTemplates()
1327
    {
1328
        return $this->getTemplateRegistry()->getTemplates();
1329
    }
1330
1331
    /**
1332
     * @deprecated since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead
1333
     *
1334
     * @param string $name
1335
     *
1336
     * @return string|null
1337
     */
1338
    public function getTemplate($name)
1339
    {
1340
        return $this->getTemplateRegistry()->getTemplate($name);
1341
    }
1342
1343
    public function getNewInstance()
1344
    {
1345
        $object = $this->getModelManager()->getModelInstance($this->getClass());
1346
        foreach ($this->getExtensions() as $extension) {
1347
            $extension->alterNewInstance($this, $object);
1348
        }
1349
1350
        return $object;
1351
    }
1352
1353
    public function getFormBuilder()
1354
    {
1355
        $this->formOptions['data_class'] = $this->getClass();
1356
1357
        $formBuilder = $this->getFormContractor()->getFormBuilder(
1358
            $this->getUniqid(),
1359
            $this->formOptions
1360
        );
1361
1362
        $this->defineFormBuilder($formBuilder);
1363
1364
        return $formBuilder;
1365
    }
1366
1367
    /**
1368
     * This method is being called by the main admin class and the child class,
1369
     * the getFormBuilder is only call by the main admin class.
1370
     */
1371
    public function defineFormBuilder(FormBuilderInterface $formBuilder)
1372
    {
1373
        if (!$this->hasSubject()) {
1374
            @trigger_error(sprintf(
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...
1375
                'Calling %s() when there is no subject is deprecated since sonata-project/admin-bundle 3.65 and will throw an exception in 4.0. '.
1376
                'Use %s::setSubject() to set the subject.',
1377
                __METHOD__,
1378
                __CLASS__
1379
            ), E_USER_DEPRECATED);
1380
            // NEXT_MAJOR : remove the previous `trigger_error()` call and uncomment the following exception
1381
            // throw new \LogicException(sprintf(
1382
            //    'Admin "%s" has no subject.',
1383
            //    static::class
1384
            // ));
1385
        }
1386
1387
        $mapper = new FormMapper($this->getFormContractor(), $formBuilder, $this);
1388
1389
        $this->configureFormFields($mapper);
1390
1391
        foreach ($this->getExtensions() as $extension) {
1392
            $extension->configureFormFields($mapper);
1393
        }
1394
1395
        $this->attachInlineValidator();
1396
    }
1397
1398
    public function attachAdminClass(FieldDescriptionInterface $fieldDescription)
1399
    {
1400
        $pool = $this->getConfigurationPool();
1401
1402
        $adminCode = $fieldDescription->getOption('admin_code');
1403
1404
        if (null !== $adminCode) {
1405
            if (!$pool->hasAdminByAdminCode($adminCode)) {
1406
                return;
1407
            }
1408
1409
            $admin = $pool->getAdminByAdminCode($adminCode);
1410
        } else {
1411
            if (!$pool->hasAdminByClass($fieldDescription->getTargetEntity())) {
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...face::getTargetEntity() has been deprecated with message: since sonata-project/admin-bundle 3.69. Use `getTargetModel()` 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...
1412
                return;
1413
            }
1414
1415
            $admin = $pool->getAdminByClass($fieldDescription->getTargetEntity());
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...face::getTargetEntity() has been deprecated with message: since sonata-project/admin-bundle 3.69. Use `getTargetModel()` 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...
1416
        }
1417
1418
        if ($this->hasRequest()) {
1419
            $admin->setRequest($this->getRequest());
1420
        }
1421
1422
        $fieldDescription->setAssociationAdmin($admin);
0 ignored issues
show
Bug introduced by
It seems like $admin can also be of type false or null; however, Sonata\AdminBundle\Admin...::setAssociationAdmin() does only seem to accept object<Sonata\AdminBundle\Admin\AdminInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1423
    }
1424
1425
    public function getObject($id)
1426
    {
1427
        $object = $this->getModelManager()->find($this->getClass(), $id);
1428
        foreach ($this->getExtensions() as $extension) {
1429
            $extension->alterObject($this, $object);
0 ignored issues
show
Bug introduced by
It seems like $object defined by $this->getModelManager()...$this->getClass(), $id) on line 1427 can also be of type null; however, Sonata\AdminBundle\Admin...nterface::alterObject() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1430
        }
1431
1432
        return $object;
1433
    }
1434
1435
    public function getForm()
1436
    {
1437
        $this->buildForm();
1438
1439
        return $this->form;
1440
    }
1441
1442
    public function getList()
1443
    {
1444
        $this->buildList();
1445
1446
        return $this->list;
1447
    }
1448
1449
    /**
1450
     * @final since sonata-project/admin-bundle 3.63.0
1451
     */
1452
    public function createQuery($context = 'list')
1453
    {
1454
        if (\func_num_args() > 0) {
1455
            @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...
1456
                'The $context argument of '.__METHOD__.' is deprecated since 3.3, to be removed in 4.0.',
1457
                E_USER_DEPRECATED
1458
            );
1459
        }
1460
1461
        $query = $this->getModelManager()->createQuery($this->getClass());
1462
1463
        $query = $this->configureQuery($query);
1464
        foreach ($this->extensions as $extension) {
1465
            $extension->configureQuery($this, $query, $context);
1466
        }
1467
1468
        return $query;
1469
    }
1470
1471
    public function getDatagrid()
1472
    {
1473
        $this->buildDatagrid();
1474
1475
        return $this->datagrid;
1476
    }
1477
1478
    public function buildTabMenu($action, ?AdminInterface $childAdmin = null)
1479
    {
1480
        if ($this->loaded['tab_menu']) {
1481
            return $this->menu;
1482
        }
1483
1484
        $this->loaded['tab_menu'] = true;
1485
1486
        $menu = $this->menuFactory->createItem('root');
1487
        $menu->setChildrenAttribute('class', 'nav navbar-nav');
1488
        $menu->setExtra('translation_domain', $this->translationDomain);
1489
1490
        // Prevents BC break with KnpMenuBundle v1.x
1491
        if (method_exists($menu, 'setCurrentUri')) {
1492
            $menu->setCurrentUri($this->getRequest()->getBaseUrl().$this->getRequest()->getPathInfo());
0 ignored issues
show
Bug introduced by
The method setCurrentUri() does not exist on Knp\Menu\ItemInterface. Did you maybe mean setCurrent()?

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...
1493
        }
1494
1495
        $this->configureTabMenu($menu, $action, $childAdmin);
1496
1497
        foreach ($this->getExtensions() as $extension) {
1498
            $extension->configureTabMenu($this, $menu, $action, $childAdmin);
1499
        }
1500
1501
        $this->menu = $menu;
1502
1503
        return $this->menu;
1504
    }
1505
1506
    public function buildSideMenu($action, ?AdminInterface $childAdmin = null)
1507
    {
1508
        return $this->buildTabMenu($action, $childAdmin);
1509
    }
1510
1511
    /**
1512
     * @param string $action
1513
     *
1514
     * @return ItemInterface
1515
     */
1516
    public function getSideMenu($action, ?AdminInterface $childAdmin = null)
1517
    {
1518
        if ($this->isChild()) {
1519
            return $this->getParent()->getSideMenu($action, $this);
1520
        }
1521
1522
        $this->buildSideMenu($action, $childAdmin);
1523
1524
        return $this->menu;
1525
    }
1526
1527
    /**
1528
     * Returns the root code.
1529
     *
1530
     * @return string the root code
1531
     */
1532
    public function getRootCode()
1533
    {
1534
        return $this->getRoot()->getCode();
1535
    }
1536
1537
    /**
1538
     * Returns the master admin.
1539
     *
1540
     * @return AbstractAdmin the root admin class
1541
     */
1542
    public function getRoot()
1543
    {
1544
        if (!$this->hasParentFieldDescription()) {
1545
            return $this;
1546
        }
1547
1548
        return $this->getParentFieldDescription()->getAdmin()->getRoot();
1549
    }
1550
1551
    public function setBaseControllerName($baseControllerName)
1552
    {
1553
        $this->baseControllerName = $baseControllerName;
1554
    }
1555
1556
    public function getBaseControllerName()
1557
    {
1558
        return $this->baseControllerName;
1559
    }
1560
1561
    /**
1562
     * @param string $label
1563
     */
1564
    public function setLabel($label)
1565
    {
1566
        $this->label = $label;
1567
    }
1568
1569
    public function getLabel()
1570
    {
1571
        return $this->label;
1572
    }
1573
1574
    /**
1575
     * @param bool $persist
1576
     *
1577
     * NEXT_MAJOR: remove this method
1578
     *
1579
     * @deprecated since sonata-project/admin-bundle 3.34, to be removed in 4.0.
1580
     */
1581
    public function setPersistFilters($persist)
1582
    {
1583
        @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...
1584
            'The '.__METHOD__.' method is deprecated since version 3.34 and will be removed in 4.0.',
1585
            E_USER_DEPRECATED
1586
        );
1587
1588
        $this->persistFilters = $persist;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$persistFilters has been deprecated with message: since sonata-project/admin-bundle 3.34, to be removed in 4.0.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
1589
    }
1590
1591
    public function setFilterPersister(?FilterPersisterInterface $filterPersister = null)
1592
    {
1593
        $this->filterPersister = $filterPersister;
1594
        // NEXT_MAJOR remove the deprecated property will be removed. Needed for persisted filter condition.
1595
        $this->persistFilters = true;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$persistFilters has been deprecated with message: since sonata-project/admin-bundle 3.34, to be removed in 4.0.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
1596
    }
1597
1598
    /**
1599
     * NEXT_MAJOR: Remove this method.
1600
     *
1601
     * @deprecated since sonata-project/admin-bundle 3.67, to be removed in 4.0.
1602
     *
1603
     * @param int $maxPerPage
1604
     */
1605
    public function setMaxPerPage($maxPerPage)
1606
    {
1607
        @trigger_error(sprintf(
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...
1608
            'The method %s is deprecated since sonata-project/admin-bundle 3.67 and will be removed in 4.0.',
1609
            __METHOD__
1610
        ), E_USER_DEPRECATED);
1611
1612
        $this->maxPerPage = $maxPerPage;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$maxPerPage has been deprecated with message: since sonata-project/admin-bundle 3.67.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
1613
    }
1614
1615
    /**
1616
     * @return int
1617
     */
1618
    public function getMaxPerPage()
1619
    {
1620
        // NEXT_MAJOR: Remove this line and uncomment the following.
1621
        return $this->maxPerPage;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$maxPerPage has been deprecated with message: since sonata-project/admin-bundle 3.67.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
1622
        // $sortValues = $this->getModelManager()->getDefaultSortValues($this->class);
1623
1624
        // return $sortValues['_per_page'] ?? 25;
1625
    }
1626
1627
    /**
1628
     * @param int $maxPageLinks
1629
     */
1630
    public function setMaxPageLinks($maxPageLinks)
1631
    {
1632
        $this->maxPageLinks = $maxPageLinks;
1633
    }
1634
1635
    /**
1636
     * @return int
1637
     */
1638
    public function getMaxPageLinks()
1639
    {
1640
        return $this->maxPageLinks;
1641
    }
1642
1643
    public function getFormGroups()
1644
    {
1645
        if (!\is_array($this->formGroups) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
1646
            @trigger_error(sprintf(
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...
1647
                'Returning other type than array in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only array in version 4.0.',
1648
                __METHOD__
1649
            ), E_USER_DEPRECATED);
1650
        }
1651
1652
        return $this->formGroups;
1653
    }
1654
1655
    public function setFormGroups(array $formGroups)
1656
    {
1657
        $this->formGroups = $formGroups;
1658
    }
1659
1660
    public function removeFieldFromFormGroup($key)
1661
    {
1662
        foreach ($this->formGroups as $name => $formGroup) {
0 ignored issues
show
Bug introduced by
The expression $this->formGroups of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1663
            unset($this->formGroups[$name]['fields'][$key]);
1664
1665
            if (empty($this->formGroups[$name]['fields'])) {
1666
                unset($this->formGroups[$name]);
1667
            }
1668
        }
1669
    }
1670
1671
    /**
1672
     * @param string $group
1673
     */
1674
    public function reorderFormGroup($group, array $keys)
1675
    {
1676
        // NEXT_MAJOR: Remove the argument "sonata_deprecation_mute" in the following call.
1677
        $formGroups = $this->getFormGroups('sonata_deprecation_mute');
1678
        $formGroups[$group]['fields'] = array_merge(array_flip($keys), $formGroups[$group]['fields']);
1679
        $this->setFormGroups($formGroups);
0 ignored issues
show
Bug introduced by
It seems like $formGroups defined by $this->getFormGroups('sonata_deprecation_mute') on line 1677 can also be of type boolean; however, Sonata\AdminBundle\Admin...tAdmin::setFormGroups() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1680
    }
1681
1682
    public function getFormTabs()
1683
    {
1684
        if (!\is_array($this->formTabs) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
1685
            @trigger_error(sprintf(
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...
1686
                'Returning other type than array in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only array in version 4.0.',
1687
                __METHOD__
1688
            ), E_USER_DEPRECATED);
1689
        }
1690
1691
        return $this->formTabs;
1692
    }
1693
1694
    public function setFormTabs(array $formTabs)
1695
    {
1696
        $this->formTabs = $formTabs;
1697
    }
1698
1699
    public function getShowTabs()
1700
    {
1701
        if (!\is_array($this->showTabs) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
1702
            @trigger_error(sprintf(
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...
1703
                'Returning other type than array in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only array in version 4.0.',
1704
                __METHOD__
1705
            ), E_USER_DEPRECATED);
1706
        }
1707
1708
        return $this->showTabs;
1709
    }
1710
1711
    public function setShowTabs(array $showTabs)
1712
    {
1713
        $this->showTabs = $showTabs;
1714
    }
1715
1716
    public function getShowGroups()
1717
    {
1718
        if (!\is_array($this->showGroups) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
1719
            @trigger_error(sprintf(
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...
1720
                'Returning other type than array in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only array in version 4.0.',
1721
                __METHOD__
1722
            ), E_USER_DEPRECATED);
1723
        }
1724
1725
        return $this->showGroups;
1726
    }
1727
1728
    public function setShowGroups(array $showGroups)
1729
    {
1730
        $this->showGroups = $showGroups;
1731
    }
1732
1733
    public function reorderShowGroup($group, array $keys)
1734
    {
1735
        // NEXT_MAJOR: Remove the argument "sonata_deprecation_mute" in the following call.
1736
        $showGroups = $this->getShowGroups('sonata_deprecation_mute');
1737
        $showGroups[$group]['fields'] = array_merge(array_flip($keys), $showGroups[$group]['fields']);
1738
        $this->setShowGroups($showGroups);
0 ignored issues
show
Bug introduced by
It seems like $showGroups defined by $this->getShowGroups('sonata_deprecation_mute') on line 1736 can also be of type boolean; however, Sonata\AdminBundle\Admin...tAdmin::setShowGroups() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1739
    }
1740
1741
    public function setParentFieldDescription(FieldDescriptionInterface $parentFieldDescription)
1742
    {
1743
        $this->parentFieldDescription = $parentFieldDescription;
1744
    }
1745
1746
    public function getParentFieldDescription()
1747
    {
1748
        if (!$this->hasParentFieldDescription()) {
1749
            @trigger_error(sprintf(
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...
1750
                'Calling %s() when there is no parent field description is deprecated since sonata-project/admin-bundle 3.66 and will throw an exception in 4.0. '.
1751
                'Use %s::hasParentFieldDescription() to know if there is a parent field description.',
1752
                __METHOD__,
1753
                __CLASS__
1754
            ), E_USER_DEPRECATED);
1755
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare FieldDescriptionInterface as return type
1756
            // throw new \LogicException(sprintf(
1757
            //    'Admin "%s" has no parent field description.',
1758
            //    static::class
1759
            // ));
1760
1761
            return null;
1762
        }
1763
1764
        return $this->parentFieldDescription;
1765
    }
1766
1767
    public function hasParentFieldDescription()
1768
    {
1769
        return $this->parentFieldDescription instanceof FieldDescriptionInterface;
1770
    }
1771
1772
    public function setSubject($subject)
1773
    {
1774
        if (\is_object($subject) && !is_a($subject, $this->getClass(), true)) {
1775
            $message = <<<'EOT'
1776
You are trying to set entity an instance of "%s",
1777
which is not the one registered with this admin class ("%s").
1778
This is deprecated since 3.5 and will no longer be supported in 4.0.
1779
EOT;
1780
1781
            @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...
1782
                sprintf($message, \get_class($subject), $this->getClass()),
1783
                E_USER_DEPRECATED
1784
            ); // NEXT_MAJOR : throw an exception instead
1785
        }
1786
1787
        $this->subject = $subject;
1788
    }
1789
1790
    public function getSubject()
1791
    {
1792
        if (!$this->hasSubject()) {
1793
            @trigger_error(sprintf(
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...
1794
                'Calling %s() when there is no subject is deprecated since sonata-project/admin-bundle 3.66 and will throw an exception in 4.0. '.
1795
                'Use %s::hasSubject() to know if there is a subject.',
1796
                __METHOD__,
1797
                __CLASS__
1798
            ), E_USER_DEPRECATED);
1799
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and update the return type
1800
            // throw new \LogicException(sprintf(
1801
            //    'Admin "%s" has no subject.',
1802
            //    static::class
1803
            // ));
1804
1805
            return null;
1806
        }
1807
1808
        return $this->subject;
1809
    }
1810
1811
    public function hasSubject()
1812
    {
1813
        if (null === $this->subject && $this->hasRequest() && !$this->hasParentFieldDescription()) {
1814
            $id = $this->request->get($this->getIdParameter());
1815
1816
            if (null !== $id) {
1817
                $this->subject = $this->getObject($id);
1818
            }
1819
        }
1820
1821
        return null !== $this->subject;
1822
    }
1823
1824
    public function getFormFieldDescriptions()
1825
    {
1826
        $this->buildForm();
1827
1828
        return $this->formFieldDescriptions;
1829
    }
1830
1831
    public function getFormFieldDescription($name)
1832
    {
1833
        $this->buildForm();
1834
1835
        if (!$this->hasFormFieldDescription($name)) {
1836
            @trigger_error(sprintf(
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...
1837
                'Calling %s() when there is no form field description is deprecated since sonata-project/admin-bundle 3.69 and will throw an exception in 4.0. '.
1838
                'Use %s::hasFormFieldDescription() to know if there is a form field description.',
1839
                __METHOD__,
1840
                __CLASS__
1841
            ), E_USER_DEPRECATED);
1842
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare FieldDescriptionInterface as return type
1843
            // throw new \LogicException(sprintf(
1844
            //    'Admin "%s" has no form field description for the field %s.',
1845
            //    static::class,
1846
            //    $name
1847
            // ));
1848
1849
            return null;
1850
        }
1851
1852
        return $this->formFieldDescriptions[$name];
1853
    }
1854
1855
    /**
1856
     * Returns true if the admin has a FieldDescription with the given $name.
1857
     *
1858
     * @param string $name
1859
     *
1860
     * @return bool
1861
     */
1862
    public function hasFormFieldDescription($name)
1863
    {
1864
        $this->buildForm();
1865
1866
        return \array_key_exists($name, $this->formFieldDescriptions) ? true : false;
1867
    }
1868
1869
    public function addFormFieldDescription($name, FieldDescriptionInterface $fieldDescription)
1870
    {
1871
        $this->formFieldDescriptions[$name] = $fieldDescription;
1872
    }
1873
1874
    /**
1875
     * remove a FieldDescription.
1876
     *
1877
     * @param string $name
1878
     */
1879
    public function removeFormFieldDescription($name)
1880
    {
1881
        unset($this->formFieldDescriptions[$name]);
1882
    }
1883
1884
    /**
1885
     * build and return the collection of form FieldDescription.
1886
     *
1887
     * @return FieldDescriptionInterface[] collection of form FieldDescription
1888
     */
1889
    public function getShowFieldDescriptions()
1890
    {
1891
        $this->buildShow();
1892
1893
        return $this->showFieldDescriptions;
1894
    }
1895
1896
    /**
1897
     * Returns the form FieldDescription with the given $name.
1898
     *
1899
     * @param string $name
1900
     *
1901
     * @return FieldDescriptionInterface
1902
     */
1903
    public function getShowFieldDescription($name)
1904
    {
1905
        $this->buildShow();
1906
1907
        if (!$this->hasShowFieldDescription($name)) {
1908
            @trigger_error(sprintf(
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...
1909
                'Calling %s() when there is no show field description is deprecated since sonata-project/admin-bundle 3.69 and will throw an exception in 4.0. '.
1910
                'Use %s::hasFormFieldDescription() to know if there is a show field description.',
1911
                __METHOD__,
1912
                __CLASS__
1913
            ), E_USER_DEPRECATED);
1914
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare FieldDescriptionInterface as return type
1915
            // throw new \LogicException(sprintf(
1916
            //    'Admin "%s" has no show field description for the field %s.',
1917
            //    static::class,
1918
            //    $name
1919
            // ));
1920
1921
            return null;
1922
        }
1923
1924
        return $this->showFieldDescriptions[$name];
1925
    }
1926
1927
    public function hasShowFieldDescription($name)
1928
    {
1929
        $this->buildShow();
1930
1931
        return \array_key_exists($name, $this->showFieldDescriptions);
1932
    }
1933
1934
    public function addShowFieldDescription($name, FieldDescriptionInterface $fieldDescription)
1935
    {
1936
        $this->showFieldDescriptions[$name] = $fieldDescription;
1937
    }
1938
1939
    public function removeShowFieldDescription($name)
1940
    {
1941
        unset($this->showFieldDescriptions[$name]);
1942
    }
1943
1944
    public function getListFieldDescriptions()
1945
    {
1946
        $this->buildList();
1947
1948
        return $this->listFieldDescriptions;
1949
    }
1950
1951
    public function getListFieldDescription($name)
1952
    {
1953
        $this->buildList();
1954
1955
        if (!$this->hasListFieldDescription($name)) {
1956
            @trigger_error(sprintf(
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...
1957
                'Calling %s() when there is no list field description is deprecated since sonata-project/admin-bundle 3.66 and will throw an exception in 4.0. '.
1958
                'Use %s::hasListFieldDescription(\'%s\') to know if there is a list field description.',
1959
                __METHOD__,
1960
                __CLASS__,
1961
                $name
1962
            ), E_USER_DEPRECATED);
1963
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare FieldDescriptionInterface as return type
1964
            // throw new \LogicException(sprintf(
1965
            //    'Admin "%s" has no list field description for %s.',
1966
            //    static::class,
1967
            //    $name
1968
            // ));
1969
1970
            return null;
1971
        }
1972
1973
        return $this->listFieldDescriptions[$name];
1974
    }
1975
1976
    public function hasListFieldDescription($name)
1977
    {
1978
        $this->buildList();
1979
1980
        return \array_key_exists($name, $this->listFieldDescriptions) ? true : false;
1981
    }
1982
1983
    public function addListFieldDescription($name, FieldDescriptionInterface $fieldDescription)
1984
    {
1985
        $this->listFieldDescriptions[$name] = $fieldDescription;
1986
    }
1987
1988
    public function removeListFieldDescription($name)
1989
    {
1990
        unset($this->listFieldDescriptions[$name]);
1991
    }
1992
1993
    public function getFilterFieldDescription($name)
1994
    {
1995
        $this->buildDatagrid();
1996
1997
        if (!$this->hasFilterFieldDescription($name)) {
1998
            @trigger_error(sprintf(
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...
1999
                'Calling %s() when there is no filter field description is deprecated since sonata-project/admin-bundle 3.69 and will throw an exception in 4.0. '.
2000
                'Use %s::hasFilterFieldDescription() to know if there is a filter field description.',
2001
                __METHOD__,
2002
                __CLASS__
2003
            ), E_USER_DEPRECATED);
2004
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare FieldDescriptionInterface as return type
2005
            // throw new \LogicException(sprintf(
2006
            //    'Admin "%s" has no filter field description for the field %s.',
2007
            //    static::class,
2008
            //    $name
2009
            // ));
2010
2011
            return null;
2012
        }
2013
2014
        return $this->filterFieldDescriptions[$name];
2015
    }
2016
2017
    public function hasFilterFieldDescription($name)
2018
    {
2019
        $this->buildDatagrid();
2020
2021
        return \array_key_exists($name, $this->filterFieldDescriptions) ? true : false;
2022
    }
2023
2024
    public function addFilterFieldDescription($name, FieldDescriptionInterface $fieldDescription)
2025
    {
2026
        $this->filterFieldDescriptions[$name] = $fieldDescription;
2027
    }
2028
2029
    public function removeFilterFieldDescription($name)
2030
    {
2031
        unset($this->filterFieldDescriptions[$name]);
2032
    }
2033
2034
    public function getFilterFieldDescriptions()
2035
    {
2036
        $this->buildDatagrid();
2037
2038
        return $this->filterFieldDescriptions;
2039
    }
2040
2041
    public function addChild(AdminInterface $child)
2042
    {
2043
        $parentAdmin = $this;
2044
        while ($parentAdmin->isChild() && $parentAdmin->getCode() !== $child->getCode()) {
2045
            $parentAdmin = $parentAdmin->getParent();
2046
        }
2047
2048
        if ($parentAdmin->getCode() === $child->getCode()) {
2049
            throw new \RuntimeException(sprintf(
2050
                'Circular reference detected! The child admin `%s` is already in the parent tree of the `%s` admin.',
2051
                $child->getCode(),
2052
                $this->getCode()
2053
            ));
2054
        }
2055
2056
        $this->children[$child->getCode()] = $child;
2057
2058
        $child->setParent($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<Sonata\AdminBundle\Admin\AbstractAdmin>, but the function expects a object<self>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2059
2060
        // NEXT_MAJOR: remove $args and add $field parameter to this function on next Major
2061
2062
        $args = \func_get_args();
2063
2064
        if (isset($args[1])) {
2065
            $child->addParentAssociationMapping($this->getCode(), $args[1]);
2066
        } else {
2067
            @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...
2068
                'Calling "addChild" without second argument is deprecated since'
2069
                .' sonata-project/admin-bundle 3.35 and will not be allowed in 4.0.',
2070
                E_USER_DEPRECATED
2071
            );
2072
        }
2073
    }
2074
2075
    public function hasChild($code)
2076
    {
2077
        return isset($this->children[$code]);
2078
    }
2079
2080
    public function getChildren()
2081
    {
2082
        return $this->children;
2083
    }
2084
2085
    public function getChild($code)
2086
    {
2087
        if (!$this->hasChild($code)) {
2088
            @trigger_error(sprintf(
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...
2089
                'Calling %s() when there is no child is deprecated since sonata-project/admin-bundle 3.69'
2090
                .' and will throw an exception in 4.0. Use %s::hasChild() to know if the child exists.',
2091
                __METHOD__,
2092
                __CLASS__
2093
            ), E_USER_DEPRECATED);
2094
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare AdminInterface as return type
2095
            // throw new \LogicException(sprintf(
2096
            //    'Admin "%s" has no child for the code %s.',
2097
            //    static::class,
2098
            //    $code
2099
            // ));
2100
2101
            return null;
2102
        }
2103
2104
        return $this->children[$code];
2105
    }
2106
2107
    public function setParent(AdminInterface $parent)
2108
    {
2109
        $this->parent = $parent;
2110
    }
2111
2112
    public function getParent()
2113
    {
2114
        if (!$this->isChild()) {
2115
            @trigger_error(sprintf(
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...
2116
                'Calling %s() when there is no parent is deprecated since sonata-project/admin-bundle 3.66 and will throw an exception in 4.0. '.
2117
                'Use %s::isChild() to know if there is a parent.',
2118
                __METHOD__,
2119
                __CLASS__
2120
            ), E_USER_DEPRECATED);
2121
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare AdminInterface as return type
2122
            // throw new \LogicException(sprintf(
2123
            //    'Admin "%s" has no parent.',
2124
            //    static::class
2125
            // ));
2126
2127
            return null;
2128
        }
2129
2130
        return $this->parent;
2131
    }
2132
2133
    final public function getRootAncestor()
2134
    {
2135
        $parent = $this;
2136
2137
        while ($parent->isChild()) {
2138
            $parent = $parent->getParent();
2139
        }
2140
2141
        return $parent;
2142
    }
2143
2144
    final public function getChildDepth()
2145
    {
2146
        $parent = $this;
2147
        $depth = 0;
2148
2149
        while ($parent->isChild()) {
2150
            $parent = $parent->getParent();
2151
            ++$depth;
2152
        }
2153
2154
        return $depth;
2155
    }
2156
2157
    final public function getCurrentLeafChildAdmin()
2158
    {
2159
        $child = $this->getCurrentChildAdmin();
2160
2161
        if (null === $child) {
2162
            return null;
2163
        }
2164
2165
        for ($c = $child; null !== $c; $c = $child->getCurrentChildAdmin()) {
2166
            $child = $c;
2167
        }
2168
2169
        return $child;
2170
    }
2171
2172
    public function isChild()
2173
    {
2174
        return $this->parent instanceof AdminInterface;
2175
    }
2176
2177
    /**
2178
     * Returns true if the admin has children, false otherwise.
2179
     *
2180
     * @return bool if the admin has children
2181
     */
2182
    public function hasChildren()
2183
    {
2184
        return \count($this->children) > 0;
2185
    }
2186
2187
    public function setUniqid($uniqid)
2188
    {
2189
        $this->uniqid = $uniqid;
2190
    }
2191
2192
    public function getUniqid()
2193
    {
2194
        if (!$this->uniqid) {
2195
            $this->uniqid = 's'.uniqid();
2196
        }
2197
2198
        return $this->uniqid;
2199
    }
2200
2201
    /**
2202
     * Returns the classname label.
2203
     *
2204
     * @return string the classname label
2205
     */
2206
    public function getClassnameLabel()
2207
    {
2208
        return $this->classnameLabel;
2209
    }
2210
2211
    public function getPersistentParameters()
2212
    {
2213
        $parameters = [];
2214
2215
        foreach ($this->getExtensions() as $extension) {
2216
            $params = $extension->getPersistentParameters($this);
2217
2218
            if (!\is_array($params)) {
2219
                throw new \RuntimeException(sprintf('The %s::getPersistentParameters must return an array', \get_class($extension)));
2220
            }
2221
2222
            $parameters = array_merge($parameters, $params);
2223
        }
2224
2225
        return $parameters;
2226
    }
2227
2228
    /**
2229
     * @param string $name
2230
     *
2231
     * @return mixed|null
2232
     */
2233
    public function getPersistentParameter($name)
2234
    {
2235
        $parameters = $this->getPersistentParameters();
2236
2237
        return $parameters[$name] ?? null;
2238
    }
2239
2240
    public function getBreadcrumbs($action)
2241
    {
2242
        @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...
2243
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
2244
            ' Use Sonata\AdminBundle\Admin\BreadcrumbsBuilder::getBreadcrumbs instead.',
2245
            E_USER_DEPRECATED
2246
        );
2247
2248
        return $this->getBreadcrumbsBuilder()->getBreadcrumbs($this, $action);
2249
    }
2250
2251
    /**
2252
     * Generates the breadcrumbs array.
2253
     *
2254
     * Note: the method will be called by the top admin instance (parent => child)
2255
     *
2256
     * @param string $action
2257
     *
2258
     * @return array
2259
     */
2260
    public function buildBreadcrumbs($action, ?ItemInterface $menu = null)
2261
    {
2262
        @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...
2263
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.',
2264
            E_USER_DEPRECATED
2265
        );
2266
2267
        if (isset($this->breadcrumbs[$action])) {
2268
            return $this->breadcrumbs[$action];
2269
        }
2270
2271
        return $this->breadcrumbs[$action] = $this->getBreadcrumbsBuilder()
2272
            ->buildBreadcrumbs($this, $action, $menu);
2273
    }
2274
2275
    /**
2276
     * NEXT_MAJOR : remove this method.
2277
     *
2278
     * @return BreadcrumbsBuilderInterface
2279
     */
2280
    final public function getBreadcrumbsBuilder()
2281
    {
2282
        @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...
2283
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
2284
            ' Use the sonata.admin.breadcrumbs_builder service instead.',
2285
            E_USER_DEPRECATED
2286
        );
2287
        if (null === $this->breadcrumbsBuilder) {
2288
            $this->breadcrumbsBuilder = new BreadcrumbsBuilder(
2289
                $this->getConfigurationPool()->getContainer()->getParameter('sonata.admin.configuration.breadcrumbs')
2290
            );
2291
        }
2292
2293
        return $this->breadcrumbsBuilder;
2294
    }
2295
2296
    /**
2297
     * NEXT_MAJOR : remove this method.
2298
     *
2299
     * @return AbstractAdmin
2300
     */
2301
    final public function setBreadcrumbsBuilder(BreadcrumbsBuilderInterface $value)
2302
    {
2303
        @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...
2304
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
2305
            ' Use the sonata.admin.breadcrumbs_builder service instead.',
2306
            E_USER_DEPRECATED
2307
        );
2308
        $this->breadcrumbsBuilder = $value;
2309
2310
        return $this;
2311
    }
2312
2313
    public function setCurrentChild($currentChild)
2314
    {
2315
        $this->currentChild = $currentChild;
2316
    }
2317
2318
    /**
2319
     * NEXT_MAJOR: Remove this method.
2320
     *
2321
     * @deprecated since sonata-project/admin-bundle 3.65, to be removed in 4.0
2322
     */
2323
    public function getCurrentChild()
2324
    {
2325
        @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...
2326
            sprintf(
2327
                'The %s() method is deprecated since version 3.65 and will be removed in 4.0. Use %s::isCurrentChild() instead.',
2328
                __METHOD__,
2329
                __CLASS__
2330
            ),
2331
            E_USER_DEPRECATED
2332
        );
2333
2334
        return $this->currentChild;
2335
    }
2336
2337
    public function isCurrentChild(): bool
2338
    {
2339
        return $this->currentChild;
2340
    }
2341
2342
    /**
2343
     * Returns the current child admin instance.
2344
     *
2345
     * @return AdminInterface|null the current child admin instance
2346
     */
2347
    public function getCurrentChildAdmin()
2348
    {
2349
        foreach ($this->children as $children) {
2350
            if ($children->isCurrentChild()) {
2351
                return $children;
2352
            }
2353
        }
2354
2355
        return null;
2356
    }
2357
2358
    public function trans($id, array $parameters = [], $domain = null, $locale = null)
2359
    {
2360
        @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...
2361
            'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2362
            E_USER_DEPRECATED
2363
        );
2364
2365
        $domain = $domain ?: $this->getTranslationDomain();
2366
2367
        return $this->translator->trans($id, $parameters, $domain, $locale);
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$translator has been deprecated with message: since sonata-project/admin-bundle 3.9, to be removed with 4.0

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
2368
    }
2369
2370
    /**
2371
     * Translate a message id.
2372
     *
2373
     * NEXT_MAJOR: remove this method
2374
     *
2375
     * @param string      $id
2376
     * @param int         $count
2377
     * @param string|null $domain
2378
     * @param string|null $locale
2379
     *
2380
     * @return string the translated string
2381
     *
2382
     * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
2383
     */
2384
    public function transChoice($id, $count, array $parameters = [], $domain = null, $locale = null)
2385
    {
2386
        @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...
2387
            'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2388
            E_USER_DEPRECATED
2389
        );
2390
2391
        $domain = $domain ?: $this->getTranslationDomain();
2392
2393
        return $this->translator->transChoice($id, $count, $parameters, $domain, $locale);
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$translator has been deprecated with message: since sonata-project/admin-bundle 3.9, to be removed with 4.0

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
2394
    }
2395
2396
    public function setTranslationDomain($translationDomain)
2397
    {
2398
        $this->translationDomain = $translationDomain;
2399
    }
2400
2401
    public function getTranslationDomain()
2402
    {
2403
        return $this->translationDomain;
2404
    }
2405
2406
    /**
2407
     * {@inheritdoc}
2408
     *
2409
     * NEXT_MAJOR: remove this method
2410
     *
2411
     * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
2412
     */
2413
    public function setTranslator(TranslatorInterface $translator)
2414
    {
2415
        $args = \func_get_args();
2416
        if (isset($args[1]) && $args[1]) {
2417
            @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...
2418
                'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2419
                E_USER_DEPRECATED
2420
            );
2421
        }
2422
2423
        $this->translator = $translator;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$translator has been deprecated with message: since sonata-project/admin-bundle 3.9, to be removed with 4.0

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
2424
    }
2425
2426
    /**
2427
     * {@inheritdoc}
2428
     *
2429
     * NEXT_MAJOR: remove this method
2430
     *
2431
     * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
2432
     */
2433
    public function getTranslator()
2434
    {
2435
        @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...
2436
            'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2437
            E_USER_DEPRECATED
2438
        );
2439
2440
        return $this->translator;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$translator has been deprecated with message: since sonata-project/admin-bundle 3.9, to be removed with 4.0

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
2441
    }
2442
2443
    public function getTranslationLabel($label, $context = '', $type = '')
2444
    {
2445
        return $this->getLabelTranslatorStrategy()->getLabel($label, $context, $type);
2446
    }
2447
2448
    public function setRequest(Request $request)
0 ignored issues
show
Bug introduced by
You have injected the Request via parameter $request. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
2449
    {
2450
        $this->request = $request;
2451
2452
        foreach ($this->getChildren() as $children) {
2453
            $children->setRequest($request);
2454
        }
2455
    }
2456
2457
    public function getRequest()
2458
    {
2459
        if (!$this->request) {
2460
            // NEXT_MAJOR: Throw \LogicException instead.
2461
            throw new \RuntimeException('The Request object has not been set');
2462
        }
2463
2464
        return $this->request;
2465
    }
2466
2467
    public function hasRequest()
2468
    {
2469
        return null !== $this->request;
2470
    }
2471
2472
    public function setFormContractor(FormContractorInterface $formBuilder)
2473
    {
2474
        $this->formContractor = $formBuilder;
2475
    }
2476
2477
    /**
2478
     * @return FormContractorInterface
2479
     */
2480
    public function getFormContractor()
2481
    {
2482
        return $this->formContractor;
2483
    }
2484
2485
    public function setDatagridBuilder(DatagridBuilderInterface $datagridBuilder)
2486
    {
2487
        $this->datagridBuilder = $datagridBuilder;
2488
    }
2489
2490
    public function getDatagridBuilder()
2491
    {
2492
        return $this->datagridBuilder;
2493
    }
2494
2495
    public function setListBuilder(ListBuilderInterface $listBuilder)
2496
    {
2497
        $this->listBuilder = $listBuilder;
2498
    }
2499
2500
    public function getListBuilder()
2501
    {
2502
        return $this->listBuilder;
2503
    }
2504
2505
    public function setShowBuilder(ShowBuilderInterface $showBuilder)
2506
    {
2507
        $this->showBuilder = $showBuilder;
2508
    }
2509
2510
    /**
2511
     * @return ShowBuilderInterface
2512
     */
2513
    public function getShowBuilder()
2514
    {
2515
        return $this->showBuilder;
2516
    }
2517
2518
    public function setConfigurationPool(Pool $configurationPool)
2519
    {
2520
        $this->configurationPool = $configurationPool;
2521
    }
2522
2523
    /**
2524
     * @return Pool
2525
     */
2526
    public function getConfigurationPool()
2527
    {
2528
        return $this->configurationPool;
2529
    }
2530
2531
    public function setRouteGenerator(RouteGeneratorInterface $routeGenerator)
2532
    {
2533
        $this->routeGenerator = $routeGenerator;
2534
    }
2535
2536
    /**
2537
     * @return RouteGeneratorInterface
2538
     */
2539
    public function getRouteGenerator()
2540
    {
2541
        return $this->routeGenerator;
2542
    }
2543
2544
    public function getCode()
2545
    {
2546
        return $this->code;
2547
    }
2548
2549
    /**
2550
     * NEXT_MAJOR: Remove this function.
2551
     *
2552
     * @deprecated This method is deprecated since sonata-project/admin-bundle 3.24 and will be removed in 4.0
2553
     *
2554
     * @param string $baseCodeRoute
2555
     */
2556
    public function setBaseCodeRoute($baseCodeRoute)
2557
    {
2558
        @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...
2559
            'The '.__METHOD__.' is deprecated since 3.24 and will be removed in 4.0.',
2560
            E_USER_DEPRECATED
2561
        );
2562
2563
        $this->baseCodeRoute = $baseCodeRoute;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...ctAdmin::$baseCodeRoute has been deprecated with message: This attribute is deprecated since sonata-project/admin-bundle 3.24 and will be removed in 4.0

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
2564
    }
2565
2566
    public function getBaseCodeRoute()
2567
    {
2568
        // NEXT_MAJOR: Uncomment the following lines.
2569
        // if ($this->isChild()) {
2570
        //     return $this->getParent()->getBaseCodeRoute().'|'.$this->getCode();
2571
        // }
2572
        //
2573
        // return $this->getCode();
2574
2575
        // NEXT_MAJOR: Remove all the code below.
2576
        if ($this->isChild()) {
2577
            $parentCode = $this->getParent()->getCode();
2578
2579
            if ($this->getParent()->isChild()) {
2580
                $parentCode = $this->getParent()->getBaseCodeRoute();
2581
            }
2582
2583
            return $parentCode.'|'.$this->getCode();
2584
        }
2585
2586
        return $this->baseCodeRoute;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...ctAdmin::$baseCodeRoute has been deprecated with message: This attribute is deprecated since sonata-project/admin-bundle 3.24 and will be removed in 4.0

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
2587
    }
2588
2589
    public function getModelManager()
2590
    {
2591
        return $this->modelManager;
2592
    }
2593
2594
    public function setModelManager(ModelManagerInterface $modelManager)
2595
    {
2596
        $this->modelManager = $modelManager;
2597
    }
2598
2599
    public function getManagerType()
2600
    {
2601
        return $this->managerType;
2602
    }
2603
2604
    /**
2605
     * @param string $type
2606
     */
2607
    public function setManagerType($type)
2608
    {
2609
        $this->managerType = $type;
2610
    }
2611
2612
    public function getObjectIdentifier()
2613
    {
2614
        return $this->getCode();
2615
    }
2616
2617
    /**
2618
     * Set the roles and permissions per role.
2619
     */
2620
    public function setSecurityInformation(array $information)
2621
    {
2622
        $this->securityInformation = $information;
2623
    }
2624
2625
    public function getSecurityInformation()
2626
    {
2627
        return $this->securityInformation;
2628
    }
2629
2630
    /**
2631
     * Return the list of permissions the user should have in order to display the admin.
2632
     *
2633
     * @param string $context
2634
     *
2635
     * @return array
2636
     */
2637
    public function getPermissionsShow($context)
2638
    {
2639
        switch ($context) {
2640
            case self::CONTEXT_DASHBOARD:
2641
            case self::CONTEXT_MENU:
2642
            default:
2643
                return ['LIST'];
2644
        }
2645
    }
2646
2647
    public function showIn($context)
2648
    {
2649
        switch ($context) {
2650
            case self::CONTEXT_DASHBOARD:
2651
            case self::CONTEXT_MENU:
2652
            default:
2653
                return $this->isGranted($this->getPermissionsShow($context));
0 ignored issues
show
Documentation introduced by
$this->getPermissionsShow($context) is of type array<integer,string,{"0":"string"}>, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2654
        }
2655
    }
2656
2657
    public function createObjectSecurity($object)
2658
    {
2659
        $this->getSecurityHandler()->createObjectSecurity($this, $object);
2660
    }
2661
2662
    public function setSecurityHandler(SecurityHandlerInterface $securityHandler)
2663
    {
2664
        $this->securityHandler = $securityHandler;
2665
    }
2666
2667
    public function getSecurityHandler()
2668
    {
2669
        return $this->securityHandler;
2670
    }
2671
2672
    public function isGranted($name, $object = null)
2673
    {
2674
        $objectRef = $object ? '/'.spl_object_hash($object).'#'.$this->id($object) : '';
2675
        $key = md5(json_encode($name).$objectRef);
2676
2677
        if (!\array_key_exists($key, $this->cacheIsGranted)) {
2678
            $this->cacheIsGranted[$key] = $this->securityHandler->isGranted($this, $name, $object ?: $this);
2679
        }
2680
2681
        return $this->cacheIsGranted[$key];
2682
    }
2683
2684
    public function getUrlSafeIdentifier($model)
2685
    {
2686
        return $this->getModelManager()->getUrlSafeIdentifier($model);
2687
    }
2688
2689
    public function getNormalizedIdentifier($model)
2690
    {
2691
        return $this->getModelManager()->getNormalizedIdentifier($model);
2692
    }
2693
2694
    public function id($model)
2695
    {
2696
        return $this->getNormalizedIdentifier($model);
2697
    }
2698
2699
    public function setValidator($validator)
2700
    {
2701
        // NEXT_MAJOR: Move ValidatorInterface check to method signature
2702
        if (!$validator instanceof ValidatorInterface) {
2703
            throw new \InvalidArgumentException(
2704
                'Argument 1 must be an instance of Symfony\Component\Validator\Validator\ValidatorInterface'
2705
            );
2706
        }
2707
2708
        $this->validator = $validator;
2709
    }
2710
2711
    public function getValidator()
2712
    {
2713
        return $this->validator;
2714
    }
2715
2716
    public function getShow()
2717
    {
2718
        $this->buildShow();
2719
2720
        return $this->show;
2721
    }
2722
2723
    public function setFormTheme(array $formTheme)
2724
    {
2725
        $this->formTheme = $formTheme;
2726
    }
2727
2728
    public function getFormTheme()
2729
    {
2730
        return $this->formTheme;
2731
    }
2732
2733
    public function setFilterTheme(array $filterTheme)
2734
    {
2735
        $this->filterTheme = $filterTheme;
2736
    }
2737
2738
    public function getFilterTheme()
2739
    {
2740
        return $this->filterTheme;
2741
    }
2742
2743
    public function addExtension(AdminExtensionInterface $extension)
2744
    {
2745
        $this->extensions[] = $extension;
2746
    }
2747
2748
    public function getExtensions()
2749
    {
2750
        return $this->extensions;
2751
    }
2752
2753
    public function setMenuFactory(FactoryInterface $menuFactory)
2754
    {
2755
        $this->menuFactory = $menuFactory;
2756
    }
2757
2758
    public function getMenuFactory()
2759
    {
2760
        return $this->menuFactory;
2761
    }
2762
2763
    public function setRouteBuilder(RouteBuilderInterface $routeBuilder)
2764
    {
2765
        $this->routeBuilder = $routeBuilder;
2766
    }
2767
2768
    public function getRouteBuilder()
2769
    {
2770
        return $this->routeBuilder;
2771
    }
2772
2773
    public function toString($object)
2774
    {
2775
        if (!\is_object($object)) {
2776
            return '';
2777
        }
2778
2779
        if (method_exists($object, '__toString') && null !== $object->__toString()) {
2780
            return (string) $object;
2781
        }
2782
2783
        return sprintf('%s:%s', ClassUtils::getClass($object), spl_object_hash($object));
2784
    }
2785
2786
    public function setLabelTranslatorStrategy(LabelTranslatorStrategyInterface $labelTranslatorStrategy)
2787
    {
2788
        $this->labelTranslatorStrategy = $labelTranslatorStrategy;
2789
    }
2790
2791
    public function getLabelTranslatorStrategy()
2792
    {
2793
        return $this->labelTranslatorStrategy;
2794
    }
2795
2796
    public function supportsPreviewMode()
2797
    {
2798
        return $this->supportsPreviewMode;
2799
    }
2800
2801
    /**
2802
     * NEXT_MAJOR: Remove this.
2803
     *
2804
     * @deprecated since sonata-project/admin-bundle 3.67, to be removed in 4.0.
2805
     *
2806
     * Set custom per page options.
2807
     */
2808
    public function setPerPageOptions(array $options)
2809
    {
2810
        @trigger_error(sprintf(
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...
2811
            'The method %s is deprecated since sonata-project/admin-bundle 3.67 and will be removed in 4.0.',
2812
            __METHOD__
2813
        ), E_USER_DEPRECATED);
2814
2815
        $this->perPageOptions = $options;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$perPageOptions has been deprecated with message: since sonata-project/admin-bundle 3.67.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
2816
    }
2817
2818
    /**
2819
     * Returns predefined per page options.
2820
     *
2821
     * @return array
2822
     */
2823
    public function getPerPageOptions()
2824
    {
2825
        // NEXT_MAJOR: Remove this line and uncomment the following
2826
        return $this->perPageOptions;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$perPageOptions has been deprecated with message: since sonata-project/admin-bundle 3.67.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
2827
//        $perPageOptions = $this->getModelManager()->getDefaultPerPageOptions($this->class);
2828
//        $perPageOptions[] = $this->getMaxPerPage();
2829
//
2830
//        $perPageOptions = array_unique($perPageOptions);
2831
//        sort($perPageOptions);
2832
//
2833
//        return $perPageOptions;
2834
    }
2835
2836
    /**
2837
     * Set pager type.
2838
     *
2839
     * @param string $pagerType
2840
     */
2841
    public function setPagerType($pagerType)
2842
    {
2843
        $this->pagerType = $pagerType;
2844
    }
2845
2846
    /**
2847
     * Get pager type.
2848
     *
2849
     * @return string
2850
     */
2851
    public function getPagerType()
2852
    {
2853
        return $this->pagerType;
2854
    }
2855
2856
    /**
2857
     * Returns true if the per page value is allowed, false otherwise.
2858
     *
2859
     * @param int $perPage
2860
     *
2861
     * @return bool
2862
     */
2863
    public function determinedPerPageValue($perPage)
2864
    {
2865
        return \in_array($perPage, $this->getPerPageOptions(), true);
2866
    }
2867
2868
    public function isAclEnabled()
2869
    {
2870
        return $this->getSecurityHandler() instanceof AclSecurityHandlerInterface;
2871
    }
2872
2873
    public function getObjectMetadata($object)
2874
    {
2875
        return new Metadata($this->toString($object));
2876
    }
2877
2878
    public function getListModes()
2879
    {
2880
        return $this->listModes;
2881
    }
2882
2883
    public function setListMode($mode)
2884
    {
2885
        if (!$this->hasRequest()) {
2886
            throw new \RuntimeException(sprintf('No request attached to the current admin: %s', $this->getCode()));
2887
        }
2888
2889
        $this->getRequest()->getSession()->set(sprintf('%s.list_mode', $this->getCode()), $mode);
2890
    }
2891
2892
    public function getListMode()
2893
    {
2894
        if (!$this->hasRequest()) {
2895
            return 'list';
2896
        }
2897
2898
        return $this->getRequest()->getSession()->get(sprintf('%s.list_mode', $this->getCode()), 'list');
2899
    }
2900
2901
    public function getAccessMapping()
2902
    {
2903
        return $this->accessMapping;
2904
    }
2905
2906
    public function checkAccess($action, $object = null)
2907
    {
2908
        $access = $this->getAccess();
2909
2910
        if (!\array_key_exists($action, $access)) {
2911
            throw new \InvalidArgumentException(sprintf(
2912
                'Action "%s" could not be found in access mapping.'
2913
                .' Please make sure your action is defined into your admin class accessMapping property.',
2914
                $action
2915
            ));
2916
        }
2917
2918
        if (!\is_array($access[$action])) {
2919
            $access[$action] = [$access[$action]];
2920
        }
2921
2922
        foreach ($access[$action] as $role) {
2923
            if (false === $this->isGranted($role, $object)) {
2924
                throw new AccessDeniedException(sprintf('Access Denied to the action %s and role %s', $action, $role));
2925
            }
2926
        }
2927
    }
2928
2929
    /**
2930
     * Hook to handle access authorization, without throw Exception.
2931
     *
2932
     * @param string $action
2933
     * @param object $object
2934
     *
2935
     * @return bool
2936
     */
2937
    public function hasAccess($action, $object = null)
2938
    {
2939
        $access = $this->getAccess();
2940
2941
        if (!\array_key_exists($action, $access)) {
2942
            return false;
2943
        }
2944
2945
        if (!\is_array($access[$action])) {
2946
            $access[$action] = [$access[$action]];
2947
        }
2948
2949
        foreach ($access[$action] as $role) {
2950
            if (false === $this->isGranted($role, $object)) {
2951
                return false;
2952
            }
2953
        }
2954
2955
        return true;
2956
    }
2957
2958
    /**
2959
     * @param string      $action
2960
     * @param object|null $object
2961
     *
2962
     * @return array
2963
     */
2964
    public function configureActionButtons($action, $object = null)
2965
    {
2966
        $list = [];
2967
2968
        if (\in_array($action, ['tree', 'show', 'edit', 'delete', 'list', 'batch'], true)
2969
            && $this->hasAccess('create')
2970
            && $this->hasRoute('create')
2971
        ) {
2972
            $list['create'] = [
2973
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2974
                'template' => $this->getTemplate('button_create'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped 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...
2975
//                'template' => $this->getTemplateRegistry()->getTemplate('button_create'),
2976
            ];
2977
        }
2978
2979
        if (\in_array($action, ['show', 'delete', 'acl', 'history'], true)
2980
            && $this->canAccessObject('edit', $object)
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 2964 can also be of type null; however, Sonata\AdminBundle\Admin...dmin::canAccessObject() does only seem to accept object, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
2981
            && $this->hasRoute('edit')
2982
        ) {
2983
            $list['edit'] = [
2984
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2985
                'template' => $this->getTemplate('button_edit'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped 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...
2986
                //'template' => $this->getTemplateRegistry()->getTemplate('button_edit'),
2987
            ];
2988
        }
2989
2990
        if (\in_array($action, ['show', 'edit', 'acl'], true)
2991
            && $this->canAccessObject('history', $object)
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 2964 can also be of type null; however, Sonata\AdminBundle\Admin...dmin::canAccessObject() does only seem to accept object, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
2992
            && $this->hasRoute('history')
2993
        ) {
2994
            $list['history'] = [
2995
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2996
                'template' => $this->getTemplate('button_history'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped 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...
2997
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_history'),
2998
            ];
2999
        }
3000
3001
        if (\in_array($action, ['edit', 'history'], true)
3002
            && $this->isAclEnabled()
3003
            && $this->canAccessObject('acl', $object)
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 2964 can also be of type null; however, Sonata\AdminBundle\Admin...dmin::canAccessObject() does only seem to accept object, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
3004
            && $this->hasRoute('acl')
3005
        ) {
3006
            $list['acl'] = [
3007
                // NEXT_MAJOR: Remove this line and use commented line below it instead
3008
                'template' => $this->getTemplate('button_acl'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped 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...
3009
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_acl'),
3010
            ];
3011
        }
3012
3013
        if (\in_array($action, ['edit', 'history', 'acl'], true)
3014
            && $this->canAccessObject('show', $object)
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 2964 can also be of type null; however, Sonata\AdminBundle\Admin...dmin::canAccessObject() does only seem to accept object, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
3015
            && \count($this->getShow()) > 0
3016
            && $this->hasRoute('show')
3017
        ) {
3018
            $list['show'] = [
3019
                // NEXT_MAJOR: Remove this line and use commented line below it instead
3020
                'template' => $this->getTemplate('button_show'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped 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...
3021
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_show'),
3022
            ];
3023
        }
3024
3025
        if (\in_array($action, ['show', 'edit', 'delete', 'acl', 'batch'], true)
3026
            && $this->hasAccess('list')
3027
            && $this->hasRoute('list')
3028
        ) {
3029
            $list['list'] = [
3030
                // NEXT_MAJOR: Remove this line and use commented line below it instead
3031
                'template' => $this->getTemplate('button_list'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped 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...
3032
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_list'),
3033
            ];
3034
        }
3035
3036
        return $list;
3037
    }
3038
3039
    /**
3040
     * @param string $action
3041
     * @param object $object
3042
     *
3043
     * @return array
3044
     */
3045
    public function getActionButtons($action, $object = null)
3046
    {
3047
        $list = $this->configureActionButtons($action, $object);
3048
3049
        foreach ($this->getExtensions() as $extension) {
3050
            // NEXT_MAJOR: remove method check
3051
            if (method_exists($extension, 'configureActionButtons')) {
3052
                $list = $extension->configureActionButtons($this, $list, $action, $object);
3053
            }
3054
        }
3055
3056
        return $list;
3057
    }
3058
3059
    /**
3060
     * Get the list of actions that can be accessed directly from the dashboard.
3061
     *
3062
     * @return array
3063
     */
3064
    public function getDashboardActions()
3065
    {
3066
        $actions = [];
3067
3068
        if ($this->hasRoute('create') && $this->hasAccess('create')) {
3069
            $actions['create'] = [
3070
                'label' => 'link_add',
3071
                'translation_domain' => 'SonataAdminBundle',
3072
                // NEXT_MAJOR: Remove this line and use commented line below it instead
3073
                'template' => $this->getTemplate('action_create'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped 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...
3074
                // 'template' => $this->getTemplateRegistry()->getTemplate('action_create'),
3075
                'url' => $this->generateUrl('create'),
3076
                'icon' => 'plus-circle',
3077
            ];
3078
        }
3079
3080
        if ($this->hasRoute('list') && $this->hasAccess('list')) {
3081
            $actions['list'] = [
3082
                'label' => 'link_list',
3083
                'translation_domain' => 'SonataAdminBundle',
3084
                'url' => $this->generateUrl('list'),
3085
                'icon' => 'list',
3086
            ];
3087
        }
3088
3089
        return $actions;
3090
    }
3091
3092
    /**
3093
     * Setting to true will enable mosaic button for the admin screen.
3094
     * Setting to false will hide mosaic button for the admin screen.
3095
     *
3096
     * @param bool $isShown
3097
     */
3098
    final public function showMosaicButton($isShown)
3099
    {
3100
        if ($isShown) {
3101
            $this->listModes['mosaic'] = ['class' => static::MOSAIC_ICON_CLASS];
3102
        } else {
3103
            unset($this->listModes['mosaic']);
3104
        }
3105
    }
3106
3107
    /**
3108
     * @param object $object
3109
     */
3110
    final public function getSearchResultLink($object)
3111
    {
3112
        foreach ($this->searchResultActions as $action) {
3113
            if ($this->hasRoute($action) && $this->hasAccess($action, $object)) {
3114
                return $this->generateObjectUrl($action, $object);
3115
            }
3116
        }
3117
3118
        return null;
3119
    }
3120
3121
    /**
3122
     * Checks if a filter type is set to a default value.
3123
     *
3124
     * @param string $name
3125
     *
3126
     * @return bool
3127
     */
3128
    final public function isDefaultFilter($name)
3129
    {
3130
        $filter = $this->getFilterParameters();
3131
        $default = $this->getDefaultFilterValues();
3132
3133
        if (!\array_key_exists($name, $filter) || !\array_key_exists($name, $default)) {
3134
            return false;
3135
        }
3136
3137
        return $filter[$name] === $default[$name];
3138
    }
3139
3140
    /**
3141
     * Check object existence and access, without throw Exception.
3142
     *
3143
     * @param string $action
3144
     * @param object $object
3145
     *
3146
     * @return bool
3147
     */
3148
    public function canAccessObject($action, $object)
3149
    {
3150
        return $object && $this->id($object) && $this->hasAccess($action, $object);
3151
    }
3152
3153
    protected function configureQuery(ProxyQueryInterface $query): ProxyQueryInterface
3154
    {
3155
        return $query;
3156
    }
3157
3158
    /**
3159
     * @return MutableTemplateRegistryInterface
3160
     */
3161
    final protected function getTemplateRegistry()
3162
    {
3163
        return $this->templateRegistry;
3164
    }
3165
3166
    /**
3167
     * Returns a list of default sort values.
3168
     *
3169
     * @return array{_page?: int, _per_page?: int, _sort_by?: string, _sort_order?: string}
0 ignored issues
show
Documentation introduced by
The doc-type array{_page?: could not be parsed: Unknown type name "array{_page?:" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
3170
     */
3171
    final protected function getDefaultSortValues(): array
3172
    {
3173
        $defaultSortValues = [];
3174
3175
        $this->configureDefaultSortValues($defaultSortValues);
3176
3177
        foreach ($this->getExtensions() as $extension) {
3178
            // NEXT_MAJOR: remove method check
3179
            if (method_exists($extension, 'configureDefaultSortValues')) {
3180
                $extension->configureDefaultSortValues($this, $defaultSortValues);
3181
            }
3182
        }
3183
3184
        return $defaultSortValues;
3185
    }
3186
3187
    /**
3188
     * Returns a list of default filters.
3189
     *
3190
     * @return array
3191
     */
3192
    final protected function getDefaultFilterValues()
3193
    {
3194
        $defaultFilterValues = [];
3195
3196
        $this->configureDefaultFilterValues($defaultFilterValues);
3197
3198
        foreach ($this->getExtensions() as $extension) {
3199
            // NEXT_MAJOR: remove method check
3200
            if (method_exists($extension, 'configureDefaultFilterValues')) {
3201
                $extension->configureDefaultFilterValues($this, $defaultFilterValues);
3202
            }
3203
        }
3204
3205
        return $defaultFilterValues;
3206
    }
3207
3208
    protected function configureFormFields(FormMapper $form)
3209
    {
3210
    }
3211
3212
    protected function configureListFields(ListMapper $list)
3213
    {
3214
    }
3215
3216
    protected function configureDatagridFilters(DatagridMapper $filter)
3217
    {
3218
    }
3219
3220
    protected function configureShowFields(ShowMapper $show)
3221
    {
3222
    }
3223
3224
    protected function configureRoutes(RouteCollection $collection)
3225
    {
3226
    }
3227
3228
    /**
3229
     * Allows you to customize batch actions.
3230
     *
3231
     * @param array $actions List of actions
3232
     *
3233
     * @return array
3234
     */
3235
    protected function configureBatchActions($actions)
3236
    {
3237
        return $actions;
3238
    }
3239
3240
    /**
3241
     * NEXT_MAJOR: remove this method.
3242
     *
3243
     * @deprecated Use configureTabMenu instead
3244
     */
3245
    protected function configureSideMenu(ItemInterface $menu, $action, ?AdminInterface $childAdmin = null)
0 ignored issues
show
Unused Code introduced by
The parameter $menu 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...
Unused Code introduced by
The parameter $action 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...
Unused Code introduced by
The parameter $childAdmin 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...
3246
    {
3247
    }
3248
3249
    /**
3250
     * Configures the tab menu in your admin.
3251
     *
3252
     * @param string $action
3253
     */
3254
    protected function configureTabMenu(ItemInterface $menu, $action, ?AdminInterface $childAdmin = null)
3255
    {
3256
        // Use configureSideMenu not to mess with previous overrides
3257
        // NEXT_MAJOR: remove this line
3258
        $this->configureSideMenu($menu, $action, $childAdmin);
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...in::configureSideMenu() has been deprecated with message: Use configureTabMenu 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...
3259
    }
3260
3261
    /**
3262
     * build the view FieldDescription array.
3263
     */
3264
    protected function buildShow()
3265
    {
3266
        if ($this->loaded['show']) {
3267
            return;
3268
        }
3269
3270
        $this->loaded['show'] = true;
3271
3272
        $this->show = $this->getShowBuilder()->getBaseList();
3273
        $mapper = new ShowMapper($this->getShowBuilder(), $this->show, $this);
3274
3275
        $this->configureShowFields($mapper);
3276
3277
        foreach ($this->getExtensions() as $extension) {
3278
            $extension->configureShowFields($mapper);
3279
        }
3280
    }
3281
3282
    /**
3283
     * build the list FieldDescription array.
3284
     */
3285
    protected function buildList()
3286
    {
3287
        if ($this->loaded['list']) {
3288
            return;
3289
        }
3290
3291
        $this->loaded['list'] = true;
3292
3293
        $this->list = $this->getListBuilder()->getBaseList();
3294
        $mapper = new ListMapper($this->getListBuilder(), $this->list, $this);
3295
3296
        if (\count($this->getBatchActions()) > 0 && $this->hasRequest() && !$this->getRequest()->isXmlHttpRequest()) {
3297
            $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance(
3298
                $this->getClass(),
3299
                'batch',
3300
                [
3301
                    'label' => 'batch',
3302
                    'code' => '_batch',
3303
                    'sortable' => false,
3304
                    'virtual_field' => true,
3305
                ]
3306
            );
3307
3308
            $fieldDescription->setAdmin($this);
3309
            // NEXT_MAJOR: Remove this line and use commented line below it instead
3310
            $fieldDescription->setTemplate($this->getTemplate('batch'));
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped 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...
3311
            // $fieldDescription->setTemplate($this->getTemplateRegistry()->getTemplate('batch'));
3312
3313
            $mapper->add($fieldDescription, ListMapper::TYPE_BATCH);
3314
        }
3315
3316
        $this->configureListFields($mapper);
3317
3318
        foreach ($this->getExtensions() as $extension) {
3319
            $extension->configureListFields($mapper);
3320
        }
3321
3322
        if ($this->hasRequest() && $this->getRequest()->isXmlHttpRequest()) {
3323
            $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance(
3324
                $this->getClass(),
3325
                'select',
3326
                [
3327
                    'label' => false,
3328
                    'code' => '_select',
3329
                    'sortable' => false,
3330
                    'virtual_field' => false,
3331
                ]
3332
            );
3333
3334
            $fieldDescription->setAdmin($this);
3335
            // NEXT_MAJOR: Remove this line and use commented line below it instead
3336
            $fieldDescription->setTemplate($this->getTemplate('select'));
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped 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...
3337
            // $fieldDescription->setTemplate($this->getTemplateRegistry()->getTemplate('select'));
3338
3339
            $mapper->add($fieldDescription, ListMapper::TYPE_SELECT);
3340
        }
3341
    }
3342
3343
    /**
3344
     * Build the form FieldDescription collection.
3345
     */
3346
    protected function buildForm()
3347
    {
3348
        if ($this->loaded['form']) {
3349
            return;
3350
        }
3351
3352
        $this->loaded['form'] = true;
3353
3354
        // Append parent object if any
3355
        if ($this->hasParentFieldDescription()) {
3356
            $parentAdmin = $this->getParentFieldDescription()->getAdmin();
3357
            $parent = $parentAdmin->getObject($this->request->get($parentAdmin->getIdParameter()));
3358
3359
            if (null !== $parent) {
3360
                AdminHelper::addInstance($parent, $this->getParentFieldDescription(), $this->getSubject());
0 ignored issues
show
Bug introduced by
It seems like $this->getParentFieldDescription() can be null; however, addInstance() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Documentation introduced by
$this->getSubject() is of type null|object, but the function expects a object<Sonata\AdminBundle\Admin\object>.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3361
            }
3362
        }
3363
3364
        $formBuilder = $this->getFormBuilder();
3365
        $formBuilder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
3366
            $this->preValidate($event->getData());
3367
        }, 100);
3368
3369
        $this->form = $formBuilder->getForm();
0 ignored issues
show
Documentation Bug introduced by
It seems like $formBuilder->getForm() of type object<Symfony\Component\Form\FormInterface> is incompatible with the declared type object<Symfony\Component\Form\Form>|null of property $form.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
3370
    }
3371
3372
    /**
3373
     * Gets the subclass corresponding to the given name.
3374
     *
3375
     * @param string $name The name of the sub class
3376
     *
3377
     * @return string the subclass
3378
     */
3379
    protected function getSubClass($name)
3380
    {
3381
        if ($this->hasSubClass($name)) {
3382
            return $this->subClasses[$name];
3383
        }
3384
3385
        // NEXT_MAJOR: Throw \LogicException instead.
3386
        throw new \RuntimeException(sprintf(
3387
            'Unable to find the subclass `%s` for admin `%s`',
3388
            $name,
3389
            static::class
3390
        ));
3391
    }
3392
3393
    /**
3394
     * Attach the inline validator to the model metadata, this must be done once per admin.
3395
     */
3396
    protected function attachInlineValidator()
3397
    {
3398
        $admin = $this;
3399
3400
        // add the custom inline validation option
3401
        $metadata = $this->validator->getMetadataFor($this->getClass());
3402
3403
        $metadata->addConstraint(new InlineConstraint([
3404
            'service' => $this,
3405
            'method' => static function (ErrorElement $errorElement, $object) use ($admin) {
3406
                /* @var \Sonata\AdminBundle\Admin\AdminInterface $admin */
3407
3408
                // This avoid the main validation to be cascaded to children
3409
                // The problem occurs when a model Page has a collection of Page as property
3410
                if ($admin->hasSubject() && spl_object_hash($object) !== spl_object_hash($admin->getSubject())) {
3411
                    return;
3412
                }
3413
3414
                $admin->validate($errorElement, $object);
3415
3416
                foreach ($admin->getExtensions() as $extension) {
3417
                    $extension->validate($admin, $errorElement, $object);
3418
                }
3419
            },
3420
            'serializingWarning' => true,
3421
        ]));
3422
    }
3423
3424
    /**
3425
     * NEXT_MAJOR: Remove this function.
3426
     *
3427
     * @deprecated since sonata-project/admin-bundle 3.67, to be removed in 4.0.
3428
     *
3429
     * Predefine per page options.
3430
     */
3431
    protected function predefinePerPageOptions()
3432
    {
3433
        array_unshift($this->perPageOptions, $this->maxPerPage);
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$perPageOptions has been deprecated with message: since sonata-project/admin-bundle 3.67.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$maxPerPage has been deprecated with message: since sonata-project/admin-bundle 3.67.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
3434
        $this->perPageOptions = array_unique($this->perPageOptions);
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$perPageOptions has been deprecated with message: since sonata-project/admin-bundle 3.67.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
3435
        sort($this->perPageOptions);
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$perPageOptions has been deprecated with message: since sonata-project/admin-bundle 3.67.

This property 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 property will be removed from the class and what other property to use instead.

Loading history...
3436
    }
3437
3438
    /**
3439
     * Return list routes with permissions name.
3440
     *
3441
     * @return array<string, string>
0 ignored issues
show
Documentation introduced by
The doc-type array<string, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
3442
     */
3443
    protected function getAccess()
3444
    {
3445
        $access = array_merge([
3446
            'acl' => 'MASTER',
3447
            'export' => 'EXPORT',
3448
            'historyCompareRevisions' => 'EDIT',
3449
            'historyViewRevision' => 'EDIT',
3450
            'history' => 'EDIT',
3451
            'edit' => 'EDIT',
3452
            'show' => 'VIEW',
3453
            'create' => 'CREATE',
3454
            'delete' => 'DELETE',
3455
            'batchDelete' => 'DELETE',
3456
            'list' => 'LIST',
3457
        ], $this->getAccessMapping());
3458
3459
        foreach ($this->extensions as $extension) {
3460
            // NEXT_MAJOR: remove method check
3461
            if (method_exists($extension, 'getAccessMapping')) {
3462
                $access = array_merge($access, $extension->getAccessMapping($this));
3463
            }
3464
        }
3465
3466
        return $access;
3467
    }
3468
3469
    /**
3470
     * Configures a list of default filters.
3471
     */
3472
    protected function configureDefaultFilterValues(array &$filterValues)
3473
    {
3474
    }
3475
3476
    /**
3477
     * Configures a list of default sort values.
3478
     *
3479
     * Example:
3480
     *   $sortValues['_sort_by'] = 'foo'
3481
     *   $sortValues['_sort_order'] = 'DESC'
3482
     */
3483
    protected function configureDefaultSortValues(array &$sortValues)
0 ignored issues
show
Unused Code introduced by
The parameter $sortValues 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...
3484
    {
3485
    }
3486
3487
    /**
3488
     * Build all the related urls to the current admin.
3489
     */
3490
    private function buildRoutes(): void
3491
    {
3492
        if ($this->loaded['routes']) {
3493
            return;
3494
        }
3495
3496
        $this->loaded['routes'] = true;
3497
3498
        $this->routes = new RouteCollection(
3499
            $this->getBaseCodeRoute(),
3500
            $this->getBaseRouteName(),
3501
            $this->getBaseRoutePattern(),
3502
            $this->getBaseControllerName()
3503
        );
3504
3505
        $this->routeBuilder->build($this, $this->routes);
3506
3507
        $this->configureRoutes($this->routes);
3508
3509
        foreach ($this->getExtensions() as $extension) {
3510
            $extension->configureRoutes($this, $this->routes);
3511
        }
3512
    }
3513
}
3514
3515
class_exists(\Sonata\Form\Validator\ErrorElement::class);
3516