Completed
Push — 3.x ( 63d463...5fc593 )
by Oskar
05:11
created

AbstractAdmin::getFilterParameters()   C

Complexity

Conditions 11
Paths 81

Size

Total Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 51
rs 6.9224
c 0
b 0
f 0
cc 11
nc 81
nop 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 60 and the first side effect is on line 3128.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Admin;
15
16
use Doctrine\Common\Util\ClassUtils;
17
use Knp\Menu\FactoryInterface as MenuFactoryInterface;
18
use Knp\Menu\ItemInterface;
19
use Knp\Menu\ItemInterface as MenuItemInterface;
20
use Sonata\AdminBundle\Builder\DatagridBuilderInterface;
21
use Sonata\AdminBundle\Builder\FormContractorInterface;
22
use Sonata\AdminBundle\Builder\ListBuilderInterface;
23
use Sonata\AdminBundle\Builder\RouteBuilderInterface;
24
use Sonata\AdminBundle\Builder\ShowBuilderInterface;
25
use Sonata\AdminBundle\Datagrid\DatagridInterface;
26
use Sonata\AdminBundle\Datagrid\DatagridMapper;
27
use Sonata\AdminBundle\Datagrid\ListMapper;
28
use Sonata\AdminBundle\Datagrid\Pager;
29
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
30
use Sonata\AdminBundle\Filter\Persister\FilterPersisterInterface;
31
use Sonata\AdminBundle\Form\FormMapper;
32
use Sonata\AdminBundle\Form\Type\ModelHiddenType;
33
use Sonata\AdminBundle\Model\ModelManagerInterface;
34
use Sonata\AdminBundle\Object\Metadata;
35
use Sonata\AdminBundle\Route\RouteCollection;
36
use Sonata\AdminBundle\Route\RouteGeneratorInterface;
37
use Sonata\AdminBundle\Security\Handler\AclSecurityHandlerInterface;
38
use Sonata\AdminBundle\Security\Handler\SecurityHandlerInterface;
39
use Sonata\AdminBundle\Show\ShowMapper;
40
use Sonata\AdminBundle\Templating\MutableTemplateRegistryInterface;
41
use Sonata\AdminBundle\Translator\LabelTranslatorStrategyInterface;
42
use Sonata\Form\Validator\Constraints\InlineConstraint;
43
use Sonata\Form\Validator\ErrorElement;
44
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
45
use Symfony\Component\Form\Form;
46
use Symfony\Component\Form\FormBuilderInterface;
47
use Symfony\Component\Form\FormEvent;
48
use Symfony\Component\Form\FormEvents;
49
use Symfony\Component\HttpFoundation\Request;
50
use Symfony\Component\PropertyAccess\PropertyPath;
51
use Symfony\Component\Routing\Generator\UrlGeneratorInterface as RoutingUrlGeneratorInterface;
52
use Symfony\Component\Security\Acl\Model\DomainObjectInterface;
53
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
54
use Symfony\Component\Translation\TranslatorInterface;
55
use Symfony\Component\Validator\Validator\ValidatorInterface;
56
57
/**
58
 * @author Thomas Rabaix <[email protected]>
59
 */
60
abstract class AbstractAdmin implements AdminInterface, DomainObjectInterface, AdminTreeInterface
61
{
62
    public const CONTEXT_MENU = 'menu';
63
    public const CONTEXT_DASHBOARD = 'dashboard';
64
65
    public const CLASS_REGEX =
66
        '@
67
        (?:([A-Za-z0-9]*)\\\)?        # vendor name / app name
68
        (Bundle\\\)?                  # optional bundle directory
69
        ([A-Za-z0-9]+?)(?:Bundle)?\\\ # bundle name, with optional suffix
70
        (
71
            Entity|Document|Model|PHPCR|CouchDocument|Phpcr|
72
            Doctrine\\\Orm|Doctrine\\\Phpcr|Doctrine\\\MongoDB|Doctrine\\\CouchDB
73
        )\\\(.*)@x';
74
75
    public const MOSAIC_ICON_CLASS = 'fa fa-th-large fa-fw';
76
77
    /**
78
     * The list FieldDescription constructed from the configureListField method.
79
     *
80
     * @var array
81
     */
82
    protected $listFieldDescriptions = [];
83
84
    /**
85
     * The show FieldDescription constructed from the configureShowFields method.
86
     *
87
     * @var array
88
     */
89
    protected $showFieldDescriptions = [];
90
91
    /**
92
     * The list FieldDescription constructed from the configureFormField method.
93
     *
94
     * @var array
95
     */
96
    protected $formFieldDescriptions = [];
97
98
    /**
99
     * The filter FieldDescription constructed from the configureFilterField method.
100
     *
101
     * @var array
102
     */
103
    protected $filterFieldDescriptions = [];
104
105
    /**
106
     * The number of result to display in the list.
107
     *
108
     * @var int
109
     */
110
    protected $maxPerPage = 32;
111
112
    /**
113
     * The maximum number of page numbers to display in the list.
114
     *
115
     * @var int
116
     */
117
    protected $maxPageLinks = 25;
118
119
    /**
120
     * The base route name used to generate the routing information.
121
     *
122
     * @var string
123
     */
124
    protected $baseRouteName;
125
126
    /**
127
     * The base route pattern used to generate the routing information.
128
     *
129
     * @var string
130
     */
131
    protected $baseRoutePattern;
132
133
    /**
134
     * The base name controller used to generate the routing information.
135
     *
136
     * @var string
137
     */
138
    protected $baseControllerName;
139
140
    /**
141
     * The label class name  (used in the title/breadcrumb ...).
142
     *
143
     * @var string
144
     */
145
    protected $classnameLabel;
146
147
    /**
148
     * The translation domain to be used to translate messages.
149
     *
150
     * @var string
151
     */
152
    protected $translationDomain = 'messages';
153
154
    /**
155
     * Options to set to the form (ie, validation_groups).
156
     *
157
     * @var array
158
     */
159
    protected $formOptions = [];
160
161
    /**
162
     * Default values to the datagrid.
163
     *
164
     * @var array
165
     */
166
    protected $datagridValues = [
167
        '_page' => 1,
168
        '_per_page' => 32,
169
    ];
170
171
    /**
172
     * Predefined per page options.
173
     *
174
     * @var array
175
     */
176
    protected $perPageOptions = [16, 32, 64, 128, 256];
177
178
    /**
179
     * Pager type.
180
     *
181
     * @var string
182
     */
183
    protected $pagerType = Pager::TYPE_DEFAULT;
184
185
    /**
186
     * The code related to the admin.
187
     *
188
     * @var string
189
     */
190
    protected $code;
191
192
    /**
193
     * The label.
194
     *
195
     * @var string
196
     */
197
    protected $label;
198
199
    /**
200
     * Whether or not to persist the filters in the session.
201
     *
202
     * NEXT_MAJOR: remove this property
203
     *
204
     * @var bool
205
     *
206
     * @deprecated since 3.34, to be removed in 4.0.
207
     */
208
    protected $persistFilters = false;
209
210
    /**
211
     * Array of routes related to this admin.
212
     *
213
     * @var RouteCollection
214
     */
215
    protected $routes;
216
217
    /**
218
     * The subject only set in edit/update/create mode.
219
     *
220
     * @var object|null
221
     */
222
    protected $subject;
223
224
    /**
225
     * Define a Collection of child admin, ie /admin/order/{id}/order-element/{childId}.
226
     *
227
     * @var array
228
     */
229
    protected $children = [];
230
231
    /**
232
     * Reference the parent collection.
233
     *
234
     * @var AdminInterface|null
235
     */
236
    protected $parent = null;
237
238
    /**
239
     * The base code route refer to the prefix used to generate the route name.
240
     *
241
     * NEXT_MAJOR: remove this attribute.
242
     *
243
     * @deprecated This attribute is deprecated since 3.24 and will be removed in 4.0
244
     *
245
     * @var string
246
     */
247
    protected $baseCodeRoute = '';
248
249
    /**
250
     * NEXT_MAJOR: should be default array and private.
251
     *
252
     * @var string|array
253
     */
254
    protected $parentAssociationMapping = null;
255
256
    /**
257
     * Reference the parent FieldDescription related to this admin
258
     * only set for FieldDescription which is associated to an Sub Admin instance.
259
     *
260
     * @var FieldDescriptionInterface
261
     */
262
    protected $parentFieldDescription;
263
264
    /**
265
     * If true then the current admin is part of the nested admin set (from the url).
266
     *
267
     * @var bool
268
     */
269
    protected $currentChild = false;
270
271
    /**
272
     * The uniqid is used to avoid clashing with 2 admin related to the code
273
     * ie: a Block linked to a Block.
274
     *
275
     * @var string
276
     */
277
    protected $uniqid;
278
279
    /**
280
     * The Entity or Document manager.
281
     *
282
     * @var ModelManagerInterface
283
     */
284
    protected $modelManager;
285
286
    /**
287
     * The current request object.
288
     *
289
     * @var Request|null
290
     */
291
    protected $request;
292
293
    /**
294
     * The translator component.
295
     *
296
     * NEXT_MAJOR: remove this property
297
     *
298
     * @var \Symfony\Component\Translation\TranslatorInterface
299
     *
300
     * @deprecated since 3.9, to be removed with 4.0
301
     */
302
    protected $translator;
303
304
    /**
305
     * The related form contractor.
306
     *
307
     * @var FormContractorInterface
308
     */
309
    protected $formContractor;
310
311
    /**
312
     * The related list builder.
313
     *
314
     * @var ListBuilderInterface
315
     */
316
    protected $listBuilder;
317
318
    /**
319
     * The related view builder.
320
     *
321
     * @var ShowBuilderInterface
322
     */
323
    protected $showBuilder;
324
325
    /**
326
     * The related datagrid builder.
327
     *
328
     * @var DatagridBuilderInterface
329
     */
330
    protected $datagridBuilder;
331
332
    /**
333
     * @var RouteBuilderInterface
334
     */
335
    protected $routeBuilder;
336
337
    /**
338
     * The datagrid instance.
339
     *
340
     * @var DatagridInterface|null
341
     */
342
    protected $datagrid;
343
344
    /**
345
     * The router instance.
346
     *
347
     * @var RouteGeneratorInterface|null
348
     */
349
    protected $routeGenerator;
350
351
    /**
352
     * The generated breadcrumbs.
353
     *
354
     * NEXT_MAJOR : remove this property
355
     *
356
     * @var array
357
     */
358
    protected $breadcrumbs = [];
359
360
    /**
361
     * @var SecurityHandlerInterface
362
     */
363
    protected $securityHandler = null;
364
365
    /**
366
     * @var ValidatorInterface
367
     */
368
    protected $validator = null;
369
370
    /**
371
     * The configuration pool.
372
     *
373
     * @var Pool
374
     */
375
    protected $configurationPool;
376
377
    /**
378
     * @var MenuItemInterface
379
     */
380
    protected $menu;
381
382
    /**
383
     * @var MenuFactoryInterface
384
     */
385
    protected $menuFactory;
386
387
    /**
388
     * @var array
389
     */
390
    protected $loaded = [
391
        'view_fields' => false,
392
        'view_groups' => false,
393
        'routes' => false,
394
        'tab_menu' => false,
395
    ];
396
397
    /**
398
     * @var array
399
     */
400
    protected $formTheme = [];
401
402
    /**
403
     * @var array
404
     */
405
    protected $filterTheme = [];
406
407
    /**
408
     * @var array
409
     *
410
     * @deprecated since 3.34, will be dropped in 4.0. Use TemplateRegistry services instead
411
     */
412
    protected $templates = [];
413
414
    /**
415
     * @var AdminExtensionInterface[]
416
     */
417
    protected $extensions = [];
418
419
    /**
420
     * @var LabelTranslatorStrategyInterface
421
     */
422
    protected $labelTranslatorStrategy;
423
424
    /**
425
     * Setting to true will enable preview mode for
426
     * the entity and show a preview button in the
427
     * edit/create forms.
428
     *
429
     * @var bool
430
     */
431
    protected $supportsPreviewMode = false;
432
433
    /**
434
     * Roles and permissions per role.
435
     *
436
     * @var array 'role' => ['permission', 'permission']
437
     */
438
    protected $securityInformation = [];
439
440
    protected $cacheIsGranted = [];
441
442
    /**
443
     * Action list for the search result.
444
     *
445
     * @var string[]
446
     */
447
    protected $searchResultActions = ['edit', 'show'];
448
449
    protected $listModes = [
450
        'list' => [
451
            'class' => 'fa fa-list fa-fw',
452
        ],
453
        'mosaic' => [
454
            'class' => self::MOSAIC_ICON_CLASS,
455
        ],
456
    ];
457
458
    /**
459
     * The Access mapping.
460
     *
461
     * @var array [action1 => requiredRole1, action2 => [requiredRole2, requiredRole3]]
462
     */
463
    protected $accessMapping = [];
464
465
    /**
466
     * @var MutableTemplateRegistryInterface
467
     */
468
    private $templateRegistry;
469
470
    /**
471
     * The class name managed by the admin class.
472
     *
473
     * @var string
474
     */
475
    private $class;
476
477
    /**
478
     * The subclasses supported by the admin class.
479
     *
480
     * @var array
481
     */
482
    private $subClasses = [];
483
484
    /**
485
     * The list collection.
486
     *
487
     * @var array
488
     */
489
    private $list;
490
491
    /**
492
     * @var FieldDescriptionCollection|null
493
     */
494
    private $show;
495
496
    /**
497
     * @var Form|null
498
     */
499
    private $form;
500
501
    /**
502
     * @var DatagridInterface
503
     */
504
    private $filter;
505
506
    /**
507
     * The cached base route name.
508
     *
509
     * @var string
510
     */
511
    private $cachedBaseRouteName;
512
513
    /**
514
     * The cached base route pattern.
515
     *
516
     * @var string
517
     */
518
    private $cachedBaseRoutePattern;
519
520
    /**
521
     * The form group disposition.
522
     *
523
     * @var array|bool
524
     */
525
    private $formGroups = false;
526
527
    /**
528
     * The form tabs disposition.
529
     *
530
     * @var array|bool
531
     */
532
    private $formTabs = false;
533
534
    /**
535
     * The view group disposition.
536
     *
537
     * @var array|bool
538
     */
539
    private $showGroups = false;
540
541
    /**
542
     * The view tab disposition.
543
     *
544
     * @var array|bool
545
     */
546
    private $showTabs = false;
547
548
    /**
549
     * The manager type to use for the admin.
550
     *
551
     * @var string
552
     */
553
    private $managerType;
554
555
    /**
556
     * The breadcrumbsBuilder component.
557
     *
558
     * @var BreadcrumbsBuilderInterface
559
     */
560
    private $breadcrumbsBuilder;
561
562
    /**
563
     * Component responsible for persisting filters.
564
     *
565
     * @var FilterPersisterInterface|null
566
     */
567
    private $filterPersister;
568
569
    /**
570
     * @param string $code
571
     * @param string $class
572
     * @param string $baseControllerName
573
     */
574
    public function __construct($code, $class, $baseControllerName)
575
    {
576
        $this->code = $code;
577
        $this->class = $class;
578
        $this->baseControllerName = $baseControllerName;
579
580
        $this->predefinePerPageOptions();
581
        $this->datagridValues['_per_page'] = $this->maxPerPage;
582
    }
583
584
    /**
585
     * {@inheritdoc}
586
     *
587
     * NEXT_MAJOR: return null to indicate no override
588
     */
589
    public function getExportFormats()
590
    {
591
        return [
592
            'json', 'xml', 'csv', 'xls',
593
        ];
594
    }
595
596
    /**
597
     * @return array
598
     */
599
    public function getExportFields()
600
    {
601
        $fields = $this->getModelManager()->getExportFields($this->getClass());
602
603
        foreach ($this->getExtensions() as $extension) {
604
            if (method_exists($extension, 'configureExportFields')) {
605
                $fields = $extension->configureExportFields($this, $fields);
606
            }
607
        }
608
609
        return $fields;
610
    }
611
612
    public function getDataSourceIterator()
613
    {
614
        $datagrid = $this->getDatagrid();
615
        $datagrid->buildPager();
616
617
        $fields = [];
618
619
        foreach ($this->getExportFields() as $key => $field) {
620
            $label = $this->getTranslationLabel($field, 'export', 'label');
621
            $transLabel = $this->trans($label);
622
623
            // NEXT_MAJOR: Remove this hack, because all field labels will be translated with the major release
624
            // No translation key exists
625
            if ($transLabel === $label) {
626
                $fields[$key] = $field;
627
            } else {
628
                $fields[$transLabel] = $field;
629
            }
630
        }
631
632
        return $this->getModelManager()->getDataSourceIterator($datagrid, $fields);
0 ignored issues
show
Bug introduced by
It seems like $datagrid defined by $this->getDatagrid() on line 614 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...
633
    }
634
635
    public function validate(ErrorElement $errorElement, $object)
636
    {
637
    }
638
639
    /**
640
     * define custom variable.
641
     */
642
    public function initialize()
643
    {
644
        if (!$this->classnameLabel) {
645
            /* NEXT_MAJOR: remove cast to string, null is not supposed to be
646
            supported but was documented as such */
647
            $this->classnameLabel = substr(
648
                (string) $this->getClass(),
649
                strrpos((string) $this->getClass(), '\\') + 1
650
            );
651
        }
652
653
        // NEXT_MAJOR: Remove this line.
654
        $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 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...
655
656
        $this->configure();
657
    }
658
659
    public function configure()
660
    {
661
    }
662
663
    public function update($object)
664
    {
665
        $this->preUpdate($object);
666
        foreach ($this->extensions as $extension) {
667
            $extension->preUpdate($this, $object);
668
        }
669
670
        $result = $this->getModelManager()->update($object);
671
        // BC compatibility
672
        if (null !== $result) {
673
            $object = $result;
674
        }
675
676
        $this->postUpdate($object);
677
        foreach ($this->extensions as $extension) {
678
            $extension->postUpdate($this, $object);
679
        }
680
681
        return $object;
682
    }
683
684
    public function create($object)
685
    {
686
        $this->prePersist($object);
687
        foreach ($this->extensions as $extension) {
688
            $extension->prePersist($this, $object);
689
        }
690
691
        $result = $this->getModelManager()->create($object);
692
        // BC compatibility
693
        if (null !== $result) {
694
            $object = $result;
695
        }
696
697
        $this->postPersist($object);
698
        foreach ($this->extensions as $extension) {
699
            $extension->postPersist($this, $object);
700
        }
701
702
        $this->createObjectSecurity($object);
703
704
        return $object;
705
    }
706
707
    public function delete($object)
708
    {
709
        $this->preRemove($object);
710
        foreach ($this->extensions as $extension) {
711
            $extension->preRemove($this, $object);
712
        }
713
714
        $this->getSecurityHandler()->deleteObjectSecurity($this, $object);
715
        $this->getModelManager()->delete($object);
716
717
        $this->postRemove($object);
718
        foreach ($this->extensions as $extension) {
719
            $extension->postRemove($this, $object);
720
        }
721
    }
722
723
    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...
724
    {
725
    }
726
727
    public function preUpdate($object)
728
    {
729
    }
730
731
    public function postUpdate($object)
732
    {
733
    }
734
735
    public function prePersist($object)
736
    {
737
    }
738
739
    public function postPersist($object)
740
    {
741
    }
742
743
    public function preRemove($object)
744
    {
745
    }
746
747
    public function postRemove($object)
748
    {
749
    }
750
751
    public function preBatchAction($actionName, ProxyQueryInterface $query, array &$idx, $allElements)
752
    {
753
    }
754
755
    public function getFilterParameters()
756
    {
757
        $parameters = [];
758
759
        // build the values array
760
        if ($this->hasRequest()) {
761
            $filters = $this->request->query->get('filter', []);
762
            if (isset($filters['_page'])) {
763
                $filters['_page'] = (int) $filters['_page'];
764
            }
765
            if (isset($filters['_per_page'])) {
766
                $filters['_per_page'] = (int) $filters['_per_page'];
767
            }
768
769
            // if filter persistence is configured
770
            // NEXT_MAJOR: remove `$this->persistFilters !== false` from the condition
771
            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 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...
772
                // if reset filters is asked, remove from storage
773
                if ('reset' === $this->request->query->get('filters')) {
774
                    $this->filterPersister->reset($this->getCode());
775
                }
776
777
                // if no filters, fetch from storage
778
                // otherwise save to storage
779
                if (empty($filters)) {
780
                    $filters = $this->filterPersister->get($this->getCode());
781
                } else {
782
                    $this->filterPersister->set($this->getCode(), $filters);
783
                }
784
            }
785
786
            $parameters = array_merge(
787
                $this->getModelManager()->getDefaultSortValues($this->getClass()),
788
                $this->datagridValues,
789
                $this->getDefaultFilterValues(),
790
                $filters
791
            );
792
793
            if (!$this->determinedPerPageValue($parameters['_per_page'])) {
794
                $parameters['_per_page'] = $this->maxPerPage;
795
            }
796
797
            // always force the parent value
798
            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...
799
                $name = str_replace('.', '__', $this->getParentAssociationMapping());
800
                $parameters[$name] = ['value' => $this->request->get($this->getParent()->getIdParameter())];
801
            }
802
        }
803
804
        return $parameters;
805
    }
806
807
    public function buildDatagrid()
808
    {
809
        if ($this->datagrid) {
810
            return;
811
        }
812
813
        $filterParameters = $this->getFilterParameters();
814
815
        // transform _sort_by from a string to a FieldDescriptionInterface for the datagrid.
816
        if (isset($filterParameters['_sort_by']) && \is_string($filterParameters['_sort_by'])) {
817
            if ($this->hasListFieldDescription($filterParameters['_sort_by'])) {
818
                $filterParameters['_sort_by'] = $this->getListFieldDescription($filterParameters['_sort_by']);
819
            } else {
820
                $filterParameters['_sort_by'] = $this->getModelManager()->getNewFieldDescriptionInstance(
821
                    $this->getClass(),
822
                    $filterParameters['_sort_by'],
823
                    []
824
                );
825
826
                $this->getListBuilder()->buildField(null, $filterParameters['_sort_by'], $this);
827
            }
828
        }
829
830
        // initialize the datagrid
831
        $this->datagrid = $this->getDatagridBuilder()->getBaseDatagrid($this, $filterParameters);
832
833
        $this->datagrid->getPager()->setMaxPageLinks($this->maxPageLinks);
834
835
        $mapper = new DatagridMapper($this->getDatagridBuilder(), $this->datagrid, $this);
836
837
        // build the datagrid filter
838
        $this->configureDatagridFilters($mapper);
839
840
        // ok, try to limit to add parent filter
841
        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...
842
            $mapper->add($this->getParentAssociationMapping(), null, [
843
                'show_filter' => false,
844
                'label' => false,
845
                'field_type' => ModelHiddenType::class,
846
                'field_options' => [
847
                    'model_manager' => $this->getModelManager(),
848
                ],
849
                'operator_type' => HiddenType::class,
850
            ], null, null, [
851
                'admin_code' => $this->getParent()->getCode(),
852
            ]);
853
        }
854
855
        foreach ($this->getExtensions() as $extension) {
856
            $extension->configureDatagridFilters($mapper);
857
        }
858
    }
859
860
    /**
861
     * Returns the name of the parent related field, so the field can be use to set the default
862
     * value (ie the parent object) or to filter the object.
863
     *
864
     * @throws \InvalidArgumentException
865
     *
866
     * @return string|null
867
     */
868
    public function getParentAssociationMapping()
869
    {
870
        // NEXT_MAJOR: remove array check
871
        if (\is_array($this->parentAssociationMapping) && $this->getParent()) {
872
            $parent = $this->getParent()->getCode();
873
874
            if (\array_key_exists($parent, $this->parentAssociationMapping)) {
875
                return $this->parentAssociationMapping[$parent];
876
            }
877
878
            throw new \InvalidArgumentException(sprintf(
879
                "There's no association between %s and %s.",
880
                $this->getCode(),
881
                $this->getParent()->getCode()
882
            ));
883
        }
884
885
        // NEXT_MAJOR: remove this line
886
        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 886 which is incompatible with the return type documented by Sonata\AdminBundle\Admin...arentAssociationMapping of type string|null.
Loading history...
887
    }
888
889
    /**
890
     * @param string $code
891
     * @param string $value
892
     */
893
    final public function addParentAssociationMapping($code, $value)
894
    {
895
        $this->parentAssociationMapping[$code] = $value;
896
    }
897
898
    /**
899
     * Returns the baseRoutePattern used to generate the routing information.
900
     *
901
     * @throws \RuntimeException
902
     *
903
     * @return string the baseRoutePattern used to generate the routing information
904
     */
905
    public function getBaseRoutePattern()
906
    {
907
        if (null !== $this->cachedBaseRoutePattern) {
908
            return $this->cachedBaseRoutePattern;
909
        }
910
911
        if ($this->isChild()) { // the admin class is a child, prefix it with the parent route pattern
912
            $baseRoutePattern = $this->baseRoutePattern;
913
            if (!$this->baseRoutePattern) {
914
                preg_match(self::CLASS_REGEX, $this->class, $matches);
915
916
                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...
917
                    throw new \RuntimeException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`', \get_class($this)));
918
                }
919
                $baseRoutePattern = $this->urlize($matches[5], '-');
920
            }
921
922
            $this->cachedBaseRoutePattern = sprintf(
923
                '%s/%s/%s',
924
                $this->getParent()->getBaseRoutePattern(),
925
                $this->getParent()->getRouterIdParameter(),
926
                $baseRoutePattern
927
            );
928
        } elseif ($this->baseRoutePattern) {
929
            $this->cachedBaseRoutePattern = $this->baseRoutePattern;
930
        } else {
931
            preg_match(self::CLASS_REGEX, $this->class, $matches);
932
933
            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...
934
                throw new \RuntimeException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`', \get_class($this)));
935
            }
936
937
            $this->cachedBaseRoutePattern = sprintf(
938
                '/%s%s/%s',
939
                empty($matches[1]) ? '' : $this->urlize($matches[1], '-').'/',
940
                $this->urlize($matches[3], '-'),
941
                $this->urlize($matches[5], '-')
942
            );
943
        }
944
945
        return $this->cachedBaseRoutePattern;
946
    }
947
948
    /**
949
     * Returns the baseRouteName used to generate the routing information.
950
     *
951
     * @throws \RuntimeException
952
     *
953
     * @return string the baseRouteName used to generate the routing information
954
     */
955
    public function getBaseRouteName()
956
    {
957
        if (null !== $this->cachedBaseRouteName) {
958
            return $this->cachedBaseRouteName;
959
        }
960
961
        if ($this->isChild()) { // the admin class is a child, prefix it with the parent route name
962
            $baseRouteName = $this->baseRouteName;
963
            if (!$this->baseRouteName) {
964
                preg_match(self::CLASS_REGEX, $this->class, $matches);
965
966
                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...
967
                    throw new \RuntimeException(sprintf('Cannot automatically determine base route name, please define a default `baseRouteName` value for the admin class `%s`', \get_class($this)));
968
                }
969
                $baseRouteName = $this->urlize($matches[5]);
970
            }
971
972
            $this->cachedBaseRouteName = sprintf(
973
                '%s_%s',
974
                $this->getParent()->getBaseRouteName(),
975
                $baseRouteName
976
            );
977
        } elseif ($this->baseRouteName) {
978
            $this->cachedBaseRouteName = $this->baseRouteName;
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('Cannot automatically determine base route name, please define a default `baseRouteName` value for the admin class `%s`', \get_class($this)));
984
            }
985
986
            $this->cachedBaseRouteName = sprintf('admin_%s%s_%s',
987
                empty($matches[1]) ? '' : $this->urlize($matches[1]).'_',
988
                $this->urlize($matches[3]),
989
                $this->urlize($matches[5])
990
            );
991
        }
992
993
        return $this->cachedBaseRouteName;
994
    }
995
996
    /**
997
     * urlize the given word.
998
     *
999
     * @param string $word
1000
     * @param string $sep  the separator
1001
     *
1002
     * @return string
1003
     */
1004
    public function urlize($word, $sep = '_')
1005
    {
1006
        return strtolower(preg_replace('/[^a-z0-9_]/i', $sep.'$1', $word));
1007
    }
1008
1009
    public function getClass()
1010
    {
1011
        if ($this->hasActiveSubClass()) {
1012
            if ($this->getParentFieldDescription()) {
1013
                throw new \RuntimeException('Feature not implemented: an embedded admin cannot have subclass');
1014
            }
1015
1016
            $subClass = $this->getRequest()->query->get('subclass');
1017
1018
            if (!$this->hasSubClass($subClass)) {
1019
                throw new \RuntimeException(sprintf('Subclass "%s" is not defined.', $subClass));
1020
            }
1021
1022
            return $this->getSubClass($subClass);
1023
        }
1024
1025
        // see https://github.com/sonata-project/SonataCoreBundle/commit/247eeb0a7ca7211142e101754769d70bc402a5b4
1026
        if ($this->subject && \is_object($this->subject)) {
1027
            return ClassUtils::getClass($this->subject);
1028
        }
1029
1030
        return $this->class;
1031
    }
1032
1033
    public function getSubClasses()
1034
    {
1035
        return $this->subClasses;
1036
    }
1037
1038
    /**
1039
     * NEXT_MAJOR: remove this method.
1040
     */
1041
    public function addSubClass($subClass)
1042
    {
1043
        @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...
1044
            'Method "%s" is deprecated since 3.30 and will be removed in 4.0.',
1045
            __METHOD__
1046
        ), E_USER_DEPRECATED);
1047
1048
        if (!\in_array($subClass, $this->subClasses, true)) {
1049
            $this->subClasses[] = $subClass;
1050
        }
1051
    }
1052
1053
    public function setSubClasses(array $subClasses)
1054
    {
1055
        $this->subClasses = $subClasses;
1056
    }
1057
1058
    public function hasSubClass($name)
1059
    {
1060
        return isset($this->subClasses[$name]);
1061
    }
1062
1063
    public function hasActiveSubClass()
1064
    {
1065
        if (\count($this->subClasses) > 0 && $this->request) {
1066
            return null !== $this->getRequest()->query->get('subclass');
1067
        }
1068
1069
        return false;
1070
    }
1071
1072
    public function getActiveSubClass()
1073
    {
1074
        if (!$this->hasActiveSubClass()) {
1075
            return;
1076
        }
1077
1078
        return $this->getSubClass($this->getActiveSubclassCode());
1079
    }
1080
1081
    public function getActiveSubclassCode()
1082
    {
1083
        if (!$this->hasActiveSubClass()) {
1084
            return;
1085
        }
1086
1087
        $subClass = $this->getRequest()->query->get('subclass');
1088
1089
        if (!$this->hasSubClass($subClass)) {
1090
            return;
1091
        }
1092
1093
        return $subClass;
1094
    }
1095
1096
    public function getBatchActions()
1097
    {
1098
        $actions = [];
1099
1100
        if ($this->hasRoute('delete') && $this->hasAccess('delete')) {
1101
            $actions['delete'] = [
1102
                'label' => 'action_delete',
1103
                'translation_domain' => 'SonataAdminBundle',
1104
                'ask_confirmation' => true, // by default always true
1105
            ];
1106
        }
1107
1108
        $actions = $this->configureBatchActions($actions);
1109
1110
        foreach ($this->getExtensions() as $extension) {
1111
            // TODO: remove method check in next major release
1112
            if (method_exists($extension, 'configureBatchActions')) {
1113
                $actions = $extension->configureBatchActions($this, $actions);
1114
            }
1115
        }
1116
1117
        foreach ($actions  as $name => &$action) {
1118
            if (!\array_key_exists('label', $action)) {
1119
                $action['label'] = $this->getTranslationLabel($name, 'batch', 'label');
1120
            }
1121
1122
            if (!\array_key_exists('translation_domain', $action)) {
1123
                $action['translation_domain'] = $this->getTranslationDomain();
1124
            }
1125
        }
1126
1127
        return $actions;
1128
    }
1129
1130
    public function getRoutes()
1131
    {
1132
        $this->buildRoutes();
1133
1134
        return $this->routes;
1135
    }
1136
1137
    public function getRouterIdParameter()
1138
    {
1139
        return '{'.$this->getIdParameter().'}';
1140
    }
1141
1142
    public function getIdParameter()
1143
    {
1144
        $parameter = 'id';
1145
1146
        for ($i = 0; $i < $this->getChildDepth(); ++$i) {
1147
            $parameter = 'child'.ucfirst($parameter);
1148
        }
1149
1150
        return $parameter;
1151
    }
1152
1153
    public function hasRoute($name)
1154
    {
1155
        if (!$this->routeGenerator) {
1156
            throw new \RuntimeException('RouteGenerator cannot be null');
1157
        }
1158
1159
        return $this->routeGenerator->hasAdminRoute($this, $name);
1160
    }
1161
1162
    public function isCurrentRoute($name, $adminCode = null)
1163
    {
1164
        if (!$this->hasRequest()) {
1165
            return false;
1166
        }
1167
1168
        $request = $this->getRequest();
1169
        $route = $request->get('_route');
1170
1171
        if ($adminCode) {
1172
            $admin = $this->getConfigurationPool()->getAdminByAdminCode($adminCode);
1173
        } else {
1174
            $admin = $this;
1175
        }
1176
1177
        if (!$admin) {
1178
            return false;
1179
        }
1180
1181
        return ($admin->getBaseRouteName().'_'.$name) === $route;
1182
    }
1183
1184
    public function generateObjectUrl($name, $object, array $parameters = [], $absolute = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
1185
    {
1186
        $parameters['id'] = $this->getUrlsafeIdentifier($object);
1187
1188
        return $this->generateUrl($name, $parameters, $absolute);
1189
    }
1190
1191
    public function generateUrl($name, array $parameters = [], $absolute = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
1192
    {
1193
        return $this->routeGenerator->generateUrl($this, $name, $parameters, $absolute);
0 ignored issues
show
Documentation introduced by
$absolute is of type integer, but the function expects a boolean.

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...
1194
    }
1195
1196
    public function generateMenuUrl($name, array $parameters = [], $absolute = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
1197
    {
1198
        return $this->routeGenerator->generateMenuUrl($this, $name, $parameters, $absolute);
0 ignored issues
show
Documentation introduced by
$absolute is of type integer, but the function expects a boolean.

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...
1199
    }
1200
1201
    final public function setTemplateRegistry(MutableTemplateRegistryInterface $templateRegistry)
1202
    {
1203
        $this->templateRegistry = $templateRegistry;
1204
    }
1205
1206
    public function setTemplates(array $templates)
1207
    {
1208
        // NEXT_MAJOR: Remove this line
1209
        $this->templates = $templates;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin\AbstractAdmin::$templates has been deprecated with message: since 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...
1210
1211
        $this->getTemplateRegistry()->setTemplates($templates);
1212
    }
1213
1214
    /**
1215
     * @param string $name
1216
     * @param string $template
1217
     */
1218
    public function setTemplate($name, $template)
1219
    {
1220
        // NEXT_MAJOR: Remove this line
1221
        $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 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...
1222
1223
        $this->getTemplateRegistry()->setTemplate($name, $template);
1224
    }
1225
1226
    /**
1227
     * @deprecated since 3.34, will be dropped in 4.0. Use TemplateRegistry services instead
1228
     *
1229
     * @return array
1230
     */
1231
    public function getTemplates()
1232
    {
1233
        return $this->getTemplateRegistry()->getTemplates();
1234
    }
1235
1236
    /**
1237
     * @deprecated since 3.34, will be dropped in 4.0. Use TemplateRegistry services instead
1238
     *
1239
     * @param string $name
1240
     *
1241
     * @return string|null
1242
     */
1243
    public function getTemplate($name)
1244
    {
1245
        return $this->getTemplateRegistry()->getTemplate($name);
1246
    }
1247
1248
    public function getNewInstance()
1249
    {
1250
        $object = $this->getModelManager()->getModelInstance($this->getClass());
1251
        foreach ($this->getExtensions() as $extension) {
1252
            $extension->alterNewInstance($this, $object);
1253
        }
1254
1255
        return $object;
1256
    }
1257
1258
    public function getFormBuilder()
1259
    {
1260
        $this->formOptions['data_class'] = $this->getClass();
1261
1262
        $formBuilder = $this->getFormContractor()->getFormBuilder(
1263
            $this->getUniqid(),
1264
            $this->formOptions
1265
        );
1266
1267
        $this->defineFormBuilder($formBuilder);
1268
1269
        return $formBuilder;
1270
    }
1271
1272
    /**
1273
     * This method is being called by the main admin class and the child class,
1274
     * the getFormBuilder is only call by the main admin class.
1275
     */
1276
    public function defineFormBuilder(FormBuilderInterface $formBuilder)
1277
    {
1278
        $mapper = new FormMapper($this->getFormContractor(), $formBuilder, $this);
1279
1280
        $this->configureFormFields($mapper);
1281
1282
        foreach ($this->getExtensions() as $extension) {
1283
            $extension->configureFormFields($mapper);
1284
        }
1285
1286
        $this->attachInlineValidator();
1287
    }
1288
1289
    public function attachAdminClass(FieldDescriptionInterface $fieldDescription)
1290
    {
1291
        $pool = $this->getConfigurationPool();
1292
1293
        $adminCode = $fieldDescription->getOption('admin_code');
1294
1295
        if (null !== $adminCode) {
1296
            $admin = $pool->getAdminByAdminCode($adminCode);
1297
        } else {
1298
            $admin = $pool->getAdminByClass($fieldDescription->getTargetEntity());
1299
        }
1300
1301
        if (!$admin) {
1302
            return;
1303
        }
1304
1305
        if ($this->hasRequest()) {
1306
            $admin->setRequest($this->getRequest());
1307
        }
1308
1309
        $fieldDescription->setAssociationAdmin($admin);
1310
    }
1311
1312
    public function getObject($id)
1313
    {
1314
        $object = $this->getModelManager()->find($this->getClass(), $id);
1315
        foreach ($this->getExtensions() as $extension) {
1316
            $extension->alterObject($this, $object);
1317
        }
1318
1319
        return $object;
1320
    }
1321
1322
    public function getForm()
1323
    {
1324
        $this->buildForm();
1325
1326
        return $this->form;
1327
    }
1328
1329
    public function getList()
1330
    {
1331
        $this->buildList();
1332
1333
        return $this->list;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->list; (array) is incompatible with the return type declared by the interface Sonata\AdminBundle\Admin...istryInterface::getList of type Sonata\AdminBundle\Admin...ldDescriptionCollection.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1334
    }
1335
1336
    public function createQuery($context = 'list')
1337
    {
1338
        if (\func_num_args() > 0) {
1339
            @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...
1340
                'The $context argument of '.__METHOD__.' is deprecated since 3.3, to be removed in 4.0.',
1341
                E_USER_DEPRECATED
1342
            );
1343
        }
1344
        $query = $this->getModelManager()->createQuery($this->getClass());
1345
1346
        foreach ($this->extensions as $extension) {
1347
            $extension->configureQuery($this, $query, $context);
1348
        }
1349
1350
        return $query;
1351
    }
1352
1353
    public function getDatagrid()
1354
    {
1355
        $this->buildDatagrid();
1356
1357
        return $this->datagrid;
1358
    }
1359
1360
    public function buildTabMenu($action, AdminInterface $childAdmin = null)
1361
    {
1362
        if ($this->loaded['tab_menu']) {
1363
            return;
1364
        }
1365
1366
        $this->loaded['tab_menu'] = true;
1367
1368
        $menu = $this->menuFactory->createItem('root');
1369
        $menu->setChildrenAttribute('class', 'nav navbar-nav');
1370
        $menu->setExtra('translation_domain', $this->translationDomain);
1371
1372
        // Prevents BC break with KnpMenuBundle v1.x
1373
        if (method_exists($menu, 'setCurrentUri')) {
1374
            $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...
1375
        }
1376
1377
        $this->configureTabMenu($menu, $action, $childAdmin);
1378
1379
        foreach ($this->getExtensions() as $extension) {
1380
            $extension->configureTabMenu($this, $menu, $action, $childAdmin);
1381
        }
1382
1383
        $this->menu = $menu;
1384
    }
1385
1386
    public function buildSideMenu($action, AdminInterface $childAdmin = null)
1387
    {
1388
        return $this->buildTabMenu($action, $childAdmin);
1389
    }
1390
1391
    /**
1392
     * @param string $action
1393
     *
1394
     * @return ItemInterface
1395
     */
1396
    public function getSideMenu($action, AdminInterface $childAdmin = null)
1397
    {
1398
        if ($this->isChild()) {
1399
            return $this->getParent()->getSideMenu($action, $this);
1400
        }
1401
1402
        $this->buildSideMenu($action, $childAdmin);
1403
1404
        return $this->menu;
1405
    }
1406
1407
    /**
1408
     * Returns the root code.
1409
     *
1410
     * @return string the root code
1411
     */
1412
    public function getRootCode()
1413
    {
1414
        return $this->getRoot()->getCode();
1415
    }
1416
1417
    /**
1418
     * Returns the master admin.
1419
     *
1420
     * @return AbstractAdmin the root admin class
1421
     */
1422
    public function getRoot()
1423
    {
1424
        $parentFieldDescription = $this->getParentFieldDescription();
1425
1426
        if (!$parentFieldDescription) {
1427
            return $this;
1428
        }
1429
1430
        return $parentFieldDescription->getAdmin()->getRoot();
1431
    }
1432
1433
    public function setBaseControllerName($baseControllerName)
1434
    {
1435
        $this->baseControllerName = $baseControllerName;
1436
    }
1437
1438
    public function getBaseControllerName()
1439
    {
1440
        return $this->baseControllerName;
1441
    }
1442
1443
    /**
1444
     * @param string $label
1445
     */
1446
    public function setLabel($label)
1447
    {
1448
        $this->label = $label;
1449
    }
1450
1451
    public function getLabel()
1452
    {
1453
        return $this->label;
1454
    }
1455
1456
    /**
1457
     * @param bool $persist
1458
     *
1459
     * NEXT_MAJOR: remove this method
1460
     *
1461
     * @deprecated since 3.34, to be removed in 4.0.
1462
     */
1463
    public function setPersistFilters($persist)
1464
    {
1465
        @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...
1466
            'The '.__METHOD__.' method is deprecated since version 3.34 and will be removed in 4.0.',
1467
            E_USER_DEPRECATED
1468
        );
1469
1470
        $this->persistFilters = $persist;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$persistFilters has been deprecated with message: since 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...
1471
    }
1472
1473
    /**
1474
     * @param FilterPersisterInterface|null $filterPersister
1475
     */
1476
    public function setFilterPersister(FilterPersisterInterface $filterPersister = null)
1477
    {
1478
        $this->filterPersister = $filterPersister;
1479
        // NEXT_MAJOR remove the deprecated property will be removed. Needed for persisted filter condition.
1480
        $this->persistFilters = true;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$persistFilters has been deprecated with message: since 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...
1481
    }
1482
1483
    /**
1484
     * @param int $maxPerPage
1485
     */
1486
    public function setMaxPerPage($maxPerPage)
1487
    {
1488
        $this->maxPerPage = $maxPerPage;
1489
    }
1490
1491
    /**
1492
     * @return int
1493
     */
1494
    public function getMaxPerPage()
1495
    {
1496
        return $this->maxPerPage;
1497
    }
1498
1499
    /**
1500
     * @param int $maxPageLinks
1501
     */
1502
    public function setMaxPageLinks($maxPageLinks)
1503
    {
1504
        $this->maxPageLinks = $maxPageLinks;
1505
    }
1506
1507
    /**
1508
     * @return int
1509
     */
1510
    public function getMaxPageLinks()
1511
    {
1512
        return $this->maxPageLinks;
1513
    }
1514
1515
    public function getFormGroups()
1516
    {
1517
        return $this->formGroups;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->formGroups; of type array|boolean adds the type boolean to the return on line 1517 which is incompatible with the return type declared by the interface Sonata\AdminBundle\Admin...nterface::getFormGroups of type array.
Loading history...
1518
    }
1519
1520
    public function setFormGroups(array $formGroups)
1521
    {
1522
        $this->formGroups = $formGroups;
1523
    }
1524
1525
    public function removeFieldFromFormGroup($key)
1526
    {
1527
        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...
1528
            unset($this->formGroups[$name]['fields'][$key]);
1529
1530
            if (empty($this->formGroups[$name]['fields'])) {
1531
                unset($this->formGroups[$name]);
1532
            }
1533
        }
1534
    }
1535
1536
    /**
1537
     * @param array $group
1538
     */
1539
    public function reorderFormGroup($group, array $keys)
1540
    {
1541
        $formGroups = $this->getFormGroups();
1542
        $formGroups[$group]['fields'] = array_merge(array_flip($keys), $formGroups[$group]['fields']);
1543
        $this->setFormGroups($formGroups);
0 ignored issues
show
Bug introduced by
It seems like $formGroups defined by $this->getFormGroups() on line 1541 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...
1544
    }
1545
1546
    public function getFormTabs()
1547
    {
1548
        return $this->formTabs;
1549
    }
1550
1551
    public function setFormTabs(array $formTabs)
1552
    {
1553
        $this->formTabs = $formTabs;
1554
    }
1555
1556
    public function getShowTabs()
1557
    {
1558
        return $this->showTabs;
1559
    }
1560
1561
    public function setShowTabs(array $showTabs)
1562
    {
1563
        $this->showTabs = $showTabs;
1564
    }
1565
1566
    public function getShowGroups()
1567
    {
1568
        return $this->showGroups;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->showGroups; of type array|boolean adds the type boolean to the return on line 1568 which is incompatible with the return type declared by the interface Sonata\AdminBundle\Admin...nterface::getShowGroups of type array.
Loading history...
1569
    }
1570
1571
    public function setShowGroups(array $showGroups)
1572
    {
1573
        $this->showGroups = $showGroups;
1574
    }
1575
1576
    public function reorderShowGroup($group, array $keys)
1577
    {
1578
        $showGroups = $this->getShowGroups();
1579
        $showGroups[$group]['fields'] = array_merge(array_flip($keys), $showGroups[$group]['fields']);
1580
        $this->setShowGroups($showGroups);
0 ignored issues
show
Bug introduced by
It seems like $showGroups defined by $this->getShowGroups() on line 1578 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...
1581
    }
1582
1583
    public function setParentFieldDescription(FieldDescriptionInterface $parentFieldDescription)
1584
    {
1585
        $this->parentFieldDescription = $parentFieldDescription;
1586
    }
1587
1588
    public function getParentFieldDescription()
1589
    {
1590
        return $this->parentFieldDescription;
1591
    }
1592
1593
    public function hasParentFieldDescription()
1594
    {
1595
        return $this->parentFieldDescription instanceof FieldDescriptionInterface;
1596
    }
1597
1598
    public function setSubject($subject)
1599
    {
1600
        if (\is_object($subject) && !is_a($subject, $this->getClass(), true)) {
1601
            $message = <<<'EOT'
1602
You are trying to set entity an instance of "%s",
1603
which is not the one registered with this admin class ("%s").
1604
This is deprecated since 3.5 and will no longer be supported in 4.0.
1605
EOT;
1606
1607
            @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...
1608
                sprintf($message, \get_class($subject), $this->getClass()),
1609
                E_USER_DEPRECATED
1610
            ); // NEXT_MAJOR : throw an exception instead
1611
        }
1612
1613
        $this->subject = $subject;
1614
    }
1615
1616
    public function getSubject()
1617
    {
1618
        if (null === $this->subject && $this->request && !$this->hasParentFieldDescription()) {
1619
            $id = $this->request->get($this->getIdParameter());
1620
1621
            if (null !== $id) {
1622
                $this->subject = $this->getObject($id);
1623
            }
1624
        }
1625
1626
        return $this->subject;
1627
    }
1628
1629
    public function hasSubject()
1630
    {
1631
        return (bool) $this->getSubject();
1632
    }
1633
1634
    public function getFormFieldDescriptions()
1635
    {
1636
        $this->buildForm();
1637
1638
        return $this->formFieldDescriptions;
1639
    }
1640
1641
    public function getFormFieldDescription($name)
1642
    {
1643
        return $this->hasFormFieldDescription($name) ? $this->formFieldDescriptions[$name] : null;
1644
    }
1645
1646
    /**
1647
     * Returns true if the admin has a FieldDescription with the given $name.
1648
     *
1649
     * @param string $name
1650
     *
1651
     * @return bool
1652
     */
1653
    public function hasFormFieldDescription($name)
1654
    {
1655
        return \array_key_exists($name, $this->formFieldDescriptions) ? true : false;
1656
    }
1657
1658
    public function addFormFieldDescription($name, FieldDescriptionInterface $fieldDescription)
1659
    {
1660
        $this->formFieldDescriptions[$name] = $fieldDescription;
1661
    }
1662
1663
    /**
1664
     * remove a FieldDescription.
1665
     *
1666
     * @param string $name
1667
     */
1668
    public function removeFormFieldDescription($name)
1669
    {
1670
        unset($this->formFieldDescriptions[$name]);
1671
    }
1672
1673
    /**
1674
     * build and return the collection of form FieldDescription.
1675
     *
1676
     * @return array collection of form FieldDescription
1677
     */
1678
    public function getShowFieldDescriptions()
1679
    {
1680
        $this->buildShow();
1681
1682
        return $this->showFieldDescriptions;
1683
    }
1684
1685
    /**
1686
     * Returns the form FieldDescription with the given $name.
1687
     *
1688
     * @param string $name
1689
     *
1690
     * @return FieldDescriptionInterface
1691
     */
1692
    public function getShowFieldDescription($name)
1693
    {
1694
        $this->buildShow();
1695
1696
        return $this->hasShowFieldDescription($name) ? $this->showFieldDescriptions[$name] : null;
1697
    }
1698
1699
    public function hasShowFieldDescription($name)
1700
    {
1701
        return \array_key_exists($name, $this->showFieldDescriptions);
1702
    }
1703
1704
    public function addShowFieldDescription($name, FieldDescriptionInterface $fieldDescription)
1705
    {
1706
        $this->showFieldDescriptions[$name] = $fieldDescription;
1707
    }
1708
1709
    public function removeShowFieldDescription($name)
1710
    {
1711
        unset($this->showFieldDescriptions[$name]);
1712
    }
1713
1714
    public function getListFieldDescriptions()
1715
    {
1716
        $this->buildList();
1717
1718
        return $this->listFieldDescriptions;
1719
    }
1720
1721
    public function getListFieldDescription($name)
1722
    {
1723
        return $this->hasListFieldDescription($name) ? $this->listFieldDescriptions[$name] : null;
1724
    }
1725
1726
    public function hasListFieldDescription($name)
1727
    {
1728
        $this->buildList();
1729
1730
        return \array_key_exists($name, $this->listFieldDescriptions) ? true : false;
1731
    }
1732
1733
    public function addListFieldDescription($name, FieldDescriptionInterface $fieldDescription)
1734
    {
1735
        $this->listFieldDescriptions[$name] = $fieldDescription;
1736
    }
1737
1738
    public function removeListFieldDescription($name)
1739
    {
1740
        unset($this->listFieldDescriptions[$name]);
1741
    }
1742
1743
    public function getFilterFieldDescription($name)
1744
    {
1745
        return $this->hasFilterFieldDescription($name) ? $this->filterFieldDescriptions[$name] : null;
1746
    }
1747
1748
    public function hasFilterFieldDescription($name)
1749
    {
1750
        return \array_key_exists($name, $this->filterFieldDescriptions) ? true : false;
1751
    }
1752
1753
    public function addFilterFieldDescription($name, FieldDescriptionInterface $fieldDescription)
1754
    {
1755
        $this->filterFieldDescriptions[$name] = $fieldDescription;
1756
    }
1757
1758
    public function removeFilterFieldDescription($name)
1759
    {
1760
        unset($this->filterFieldDescriptions[$name]);
1761
    }
1762
1763
    public function getFilterFieldDescriptions()
1764
    {
1765
        $this->buildDatagrid();
1766
1767
        return $this->filterFieldDescriptions;
1768
    }
1769
1770
    public function addChild(AdminInterface $child)
1771
    {
1772
        for ($parentAdmin = $this; null !== $parentAdmin; $parentAdmin = $parentAdmin->getParent()) {
1773
            if ($parentAdmin->getCode() !== $child->getCode()) {
1774
                continue;
1775
            }
1776
1777
            throw new \RuntimeException(sprintf(
1778
                'Circular reference detected! The child admin `%s` is already in the parent tree of the `%s` admin.',
1779
                $child->getCode(), $this->getCode()
1780
            ));
1781
        }
1782
1783
        $this->children[$child->getCode()] = $child;
1784
1785
        $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...
1786
1787
        // NEXT_MAJOR: remove $args and add $field parameter to this function on next Major
1788
1789
        $args = \func_get_args();
1790
1791
        if (isset($args[1])) {
1792
            $child->addParentAssociationMapping($this->getCode(), $args[1]);
1793
        } else {
1794
            @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...
1795
                'Calling "addChild" without second argument is deprecated since 3.35'
1796
                .' and will not be allowed in 4.0.',
1797
                E_USER_DEPRECATED
1798
            );
1799
        }
1800
    }
1801
1802
    public function hasChild($code)
1803
    {
1804
        return isset($this->children[$code]);
1805
    }
1806
1807
    public function getChildren()
1808
    {
1809
        return $this->children;
1810
    }
1811
1812
    public function getChild($code)
1813
    {
1814
        return $this->hasChild($code) ? $this->children[$code] : null;
1815
    }
1816
1817
    public function setParent(AdminInterface $parent)
1818
    {
1819
        $this->parent = $parent;
1820
    }
1821
1822
    public function getParent()
1823
    {
1824
        return $this->parent;
1825
    }
1826
1827
    final public function getRootAncestor()
1828
    {
1829
        $parent = $this;
1830
1831
        while ($parent->isChild()) {
1832
            $parent = $parent->getParent();
1833
        }
1834
1835
        return $parent;
1836
    }
1837
1838
    final public function getChildDepth()
1839
    {
1840
        $parent = $this;
1841
        $depth = 0;
1842
1843
        while ($parent->isChild()) {
1844
            $parent = $parent->getParent();
1845
            ++$depth;
1846
        }
1847
1848
        return $depth;
1849
    }
1850
1851
    final public function getCurrentLeafChildAdmin()
1852
    {
1853
        $child = $this->getCurrentChildAdmin();
1854
1855
        if (null === $child) {
1856
            return;
1857
        }
1858
1859
        for ($c = $child; null !== $c; $c = $child->getCurrentChildAdmin()) {
1860
            $child = $c;
1861
        }
1862
1863
        return $child;
1864
    }
1865
1866
    public function isChild()
1867
    {
1868
        return $this->parent instanceof AdminInterface;
1869
    }
1870
1871
    /**
1872
     * Returns true if the admin has children, false otherwise.
1873
     *
1874
     * @return bool if the admin has children
1875
     */
1876
    public function hasChildren()
1877
    {
1878
        return \count($this->children) > 0;
1879
    }
1880
1881
    public function setUniqid($uniqid)
1882
    {
1883
        $this->uniqid = $uniqid;
1884
    }
1885
1886
    public function getUniqid()
1887
    {
1888
        if (!$this->uniqid) {
1889
            $this->uniqid = 's'.substr(md5($this->getBaseCodeRoute()), 0, 10);
1890
        }
1891
1892
        return $this->uniqid;
1893
    }
1894
1895
    /**
1896
     * Returns the classname label.
1897
     *
1898
     * @return string the classname label
1899
     */
1900
    public function getClassnameLabel()
1901
    {
1902
        return $this->classnameLabel;
1903
    }
1904
1905
    public function getPersistentParameters()
1906
    {
1907
        $parameters = [];
1908
1909
        foreach ($this->getExtensions() as $extension) {
1910
            $params = $extension->getPersistentParameters($this);
1911
1912
            if (!\is_array($params)) {
1913
                throw new \RuntimeException(sprintf('The %s::getPersistentParameters must return an array', \get_class($extension)));
1914
            }
1915
1916
            $parameters = array_merge($parameters, $params);
1917
        }
1918
1919
        return $parameters;
1920
    }
1921
1922
    /**
1923
     * @param string $name
1924
     *
1925
     * @return mixed|null
1926
     */
1927
    public function getPersistentParameter($name)
1928
    {
1929
        $parameters = $this->getPersistentParameters();
1930
1931
        return $parameters[$name] ?? null;
1932
    }
1933
1934
    public function getBreadcrumbs($action)
1935
    {
1936
        @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...
1937
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
1938
            ' Use Sonata\AdminBundle\Admin\BreadcrumbsBuilder::getBreadcrumbs instead.',
1939
            E_USER_DEPRECATED
1940
        );
1941
1942
        return $this->getBreadcrumbsBuilder()->getBreadcrumbs($this, $action);
1943
    }
1944
1945
    /**
1946
     * Generates the breadcrumbs array.
1947
     *
1948
     * Note: the method will be called by the top admin instance (parent => child)
1949
     *
1950
     * @param string $action
1951
     *
1952
     * @return array
1953
     */
1954
    public function buildBreadcrumbs($action, MenuItemInterface $menu = null)
1955
    {
1956
        @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...
1957
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.',
1958
            E_USER_DEPRECATED
1959
        );
1960
1961
        if (isset($this->breadcrumbs[$action])) {
1962
            return $this->breadcrumbs[$action];
1963
        }
1964
1965
        return $this->breadcrumbs[$action] = $this->getBreadcrumbsBuilder()
1966
            ->buildBreadcrumbs($this, $action, $menu);
1967
    }
1968
1969
    /**
1970
     * NEXT_MAJOR : remove this method.
1971
     *
1972
     * @return BreadcrumbsBuilderInterface
1973
     */
1974
    final public function getBreadcrumbsBuilder()
1975
    {
1976
        @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...
1977
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
1978
            ' Use the sonata.admin.breadcrumbs_builder service instead.',
1979
            E_USER_DEPRECATED
1980
        );
1981
        if (null === $this->breadcrumbsBuilder) {
1982
            $this->breadcrumbsBuilder = new BreadcrumbsBuilder(
1983
                $this->getConfigurationPool()->getContainer()->getParameter('sonata.admin.configuration.breadcrumbs')
1984
            );
1985
        }
1986
1987
        return $this->breadcrumbsBuilder;
1988
    }
1989
1990
    /**
1991
     * NEXT_MAJOR : remove this method.
1992
     *
1993
     * @return AbstractAdmin
1994
     */
1995
    final public function setBreadcrumbsBuilder(BreadcrumbsBuilderInterface $value)
1996
    {
1997
        @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...
1998
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
1999
            ' Use the sonata.admin.breadcrumbs_builder service instead.',
2000
            E_USER_DEPRECATED
2001
        );
2002
        $this->breadcrumbsBuilder = $value;
2003
2004
        return $this;
2005
    }
2006
2007
    public function setCurrentChild($currentChild)
2008
    {
2009
        $this->currentChild = $currentChild;
2010
    }
2011
2012
    public function getCurrentChild()
2013
    {
2014
        return $this->currentChild;
2015
    }
2016
2017
    /**
2018
     * Returns the current child admin instance.
2019
     *
2020
     * @return AdminInterface|null the current child admin instance
2021
     */
2022
    public function getCurrentChildAdmin()
2023
    {
2024
        foreach ($this->children as $children) {
2025
            if ($children->getCurrentChild()) {
2026
                return $children;
2027
            }
2028
        }
2029
    }
2030
2031
    public function trans($id, array $parameters = [], $domain = null, $locale = null)
2032
    {
2033
        @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...
2034
            'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2035
            E_USER_DEPRECATED
2036
        );
2037
2038
        $domain = $domain ?: $this->getTranslationDomain();
2039
2040
        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 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...
2041
    }
2042
2043
    /**
2044
     * Translate a message id.
2045
     *
2046
     * NEXT_MAJOR: remove this method
2047
     *
2048
     * @param string      $id
2049
     * @param int         $count
2050
     * @param string|null $domain
2051
     * @param string|null $locale
2052
     *
2053
     * @return string the translated string
2054
     *
2055
     * @deprecated since 3.9, to be removed with 4.0
2056
     */
2057
    public function transChoice($id, $count, array $parameters = [], $domain = null, $locale = null)
2058
    {
2059
        @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...
2060
            'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2061
            E_USER_DEPRECATED
2062
        );
2063
2064
        $domain = $domain ?: $this->getTranslationDomain();
2065
2066
        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 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...
2067
    }
2068
2069
    public function setTranslationDomain($translationDomain)
2070
    {
2071
        $this->translationDomain = $translationDomain;
2072
    }
2073
2074
    public function getTranslationDomain()
2075
    {
2076
        return $this->translationDomain;
2077
    }
2078
2079
    /**
2080
     * {@inheritdoc}
2081
     *
2082
     * NEXT_MAJOR: remove this method
2083
     *
2084
     * @deprecated since 3.9, to be removed with 4.0
2085
     */
2086
    public function setTranslator(TranslatorInterface $translator)
2087
    {
2088
        $args = \func_get_args();
2089
        if (isset($args[1]) && $args[1]) {
2090
            @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...
2091
                'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2092
                E_USER_DEPRECATED
2093
            );
2094
        }
2095
2096
        $this->translator = $translator;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$translator has been deprecated with message: since 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...
2097
    }
2098
2099
    /**
2100
     * {@inheritdoc}
2101
     *
2102
     * NEXT_MAJOR: remove this method
2103
     *
2104
     * @deprecated since 3.9, to be removed with 4.0
2105
     */
2106
    public function getTranslator()
2107
    {
2108
        @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...
2109
            'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2110
            E_USER_DEPRECATED
2111
        );
2112
2113
        return $this->translator;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$translator has been deprecated with message: since 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...
2114
    }
2115
2116
    public function getTranslationLabel($label, $context = '', $type = '')
2117
    {
2118
        return $this->getLabelTranslatorStrategy()->getLabel($label, $context, $type);
2119
    }
2120
2121
    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...
2122
    {
2123
        $this->request = $request;
2124
2125
        foreach ($this->getChildren() as $children) {
2126
            $children->setRequest($request);
2127
        }
2128
    }
2129
2130
    public function getRequest()
2131
    {
2132
        if (!$this->request) {
2133
            throw new \RuntimeException('The Request object has not been set');
2134
        }
2135
2136
        return $this->request;
2137
    }
2138
2139
    public function hasRequest()
2140
    {
2141
        return null !== $this->request;
2142
    }
2143
2144
    public function setFormContractor(FormContractorInterface $formBuilder)
2145
    {
2146
        $this->formContractor = $formBuilder;
2147
    }
2148
2149
    /**
2150
     * @return FormContractorInterface
2151
     */
2152
    public function getFormContractor()
2153
    {
2154
        return $this->formContractor;
2155
    }
2156
2157
    public function setDatagridBuilder(DatagridBuilderInterface $datagridBuilder)
2158
    {
2159
        $this->datagridBuilder = $datagridBuilder;
2160
    }
2161
2162
    public function getDatagridBuilder()
2163
    {
2164
        return $this->datagridBuilder;
2165
    }
2166
2167
    public function setListBuilder(ListBuilderInterface $listBuilder)
2168
    {
2169
        $this->listBuilder = $listBuilder;
2170
    }
2171
2172
    public function getListBuilder()
2173
    {
2174
        return $this->listBuilder;
2175
    }
2176
2177
    public function setShowBuilder(ShowBuilderInterface $showBuilder)
2178
    {
2179
        $this->showBuilder = $showBuilder;
2180
    }
2181
2182
    /**
2183
     * @return ShowBuilderInterface
2184
     */
2185
    public function getShowBuilder()
2186
    {
2187
        return $this->showBuilder;
2188
    }
2189
2190
    public function setConfigurationPool(Pool $configurationPool)
2191
    {
2192
        $this->configurationPool = $configurationPool;
2193
    }
2194
2195
    /**
2196
     * @return Pool
2197
     */
2198
    public function getConfigurationPool()
2199
    {
2200
        return $this->configurationPool;
2201
    }
2202
2203
    public function setRouteGenerator(RouteGeneratorInterface $routeGenerator)
2204
    {
2205
        $this->routeGenerator = $routeGenerator;
2206
    }
2207
2208
    /**
2209
     * @return RouteGeneratorInterface
2210
     */
2211
    public function getRouteGenerator()
2212
    {
2213
        return $this->routeGenerator;
2214
    }
2215
2216
    public function getCode()
2217
    {
2218
        return $this->code;
2219
    }
2220
2221
    /**
2222
     * NEXT_MAJOR: Remove this function.
2223
     *
2224
     * @deprecated This method is deprecated since 3.24 and will be removed in 4.0
2225
     *
2226
     * @param string $baseCodeRoute
2227
     */
2228
    public function setBaseCodeRoute($baseCodeRoute)
2229
    {
2230
        @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...
2231
            'The '.__METHOD__.' is deprecated since 3.24 and will be removed in 4.0.',
2232
            E_USER_DEPRECATED
2233
        );
2234
2235
        $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 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...
2236
    }
2237
2238
    public function getBaseCodeRoute()
2239
    {
2240
        // NEXT_MAJOR: Uncomment the following lines.
2241
        // if ($this->isChild()) {
2242
        //     return $this->getParent()->getBaseCodeRoute().'|'.$this->getCode();
2243
        // }
2244
        //
2245
        // return $this->getCode();
2246
2247
        // NEXT_MAJOR: Remove all the code below.
2248
        if ($this->isChild()) {
2249
            $parentCode = $this->getParent()->getCode();
2250
2251
            if ($this->getParent()->isChild()) {
2252
                $parentCode = $this->getParent()->getBaseCodeRoute();
2253
            }
2254
2255
            return $parentCode.'|'.$this->getCode();
2256
        }
2257
2258
        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 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...
2259
    }
2260
2261
    public function getModelManager()
2262
    {
2263
        return $this->modelManager;
2264
    }
2265
2266
    public function setModelManager(ModelManagerInterface $modelManager)
2267
    {
2268
        $this->modelManager = $modelManager;
2269
    }
2270
2271
    public function getManagerType()
2272
    {
2273
        return $this->managerType;
2274
    }
2275
2276
    /**
2277
     * @param string $type
2278
     */
2279
    public function setManagerType($type)
2280
    {
2281
        $this->managerType = $type;
2282
    }
2283
2284
    public function getObjectIdentifier()
2285
    {
2286
        return $this->getCode();
2287
    }
2288
2289
    /**
2290
     * Set the roles and permissions per role.
2291
     */
2292
    public function setSecurityInformation(array $information)
2293
    {
2294
        $this->securityInformation = $information;
2295
    }
2296
2297
    public function getSecurityInformation()
2298
    {
2299
        return $this->securityInformation;
2300
    }
2301
2302
    /**
2303
     * Return the list of permissions the user should have in order to display the admin.
2304
     *
2305
     * @param string $context
2306
     *
2307
     * @return array
2308
     */
2309
    public function getPermissionsShow($context)
2310
    {
2311
        switch ($context) {
2312
            case self::CONTEXT_DASHBOARD:
2313
            case self::CONTEXT_MENU:
2314
            default:
2315
                return ['LIST'];
2316
        }
2317
    }
2318
2319
    public function showIn($context)
2320
    {
2321
        switch ($context) {
2322
            case self::CONTEXT_DASHBOARD:
2323
            case self::CONTEXT_MENU:
2324
            default:
2325
                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...
2326
        }
2327
    }
2328
2329
    public function createObjectSecurity($object)
2330
    {
2331
        $this->getSecurityHandler()->createObjectSecurity($this, $object);
2332
    }
2333
2334
    public function setSecurityHandler(SecurityHandlerInterface $securityHandler)
2335
    {
2336
        $this->securityHandler = $securityHandler;
2337
    }
2338
2339
    public function getSecurityHandler()
2340
    {
2341
        return $this->securityHandler;
2342
    }
2343
2344
    public function isGranted($name, $object = null)
2345
    {
2346
        $key = md5(json_encode($name).($object ? '/'.spl_object_hash($object) : ''));
2347
2348
        if (!\array_key_exists($key, $this->cacheIsGranted)) {
2349
            $this->cacheIsGranted[$key] = $this->securityHandler->isGranted($this, $name, $object ?: $this);
2350
        }
2351
2352
        return $this->cacheIsGranted[$key];
2353
    }
2354
2355
    public function getUrlsafeIdentifier($entity)
2356
    {
2357
        return $this->getModelManager()->getUrlsafeIdentifier($entity);
2358
    }
2359
2360
    public function getNormalizedIdentifier($entity)
2361
    {
2362
        return $this->getModelManager()->getNormalizedIdentifier($entity);
2363
    }
2364
2365
    public function id($entity)
2366
    {
2367
        return $this->getNormalizedIdentifier($entity);
2368
    }
2369
2370
    public function setValidator($validator)
2371
    {
2372
        // NEXT_MAJOR: Move ValidatorInterface check to method signature
2373
        if (!$validator instanceof ValidatorInterface) {
2374
            throw new \InvalidArgumentException(
2375
                'Argument 1 must be an instance of Symfony\Component\Validator\Validator\ValidatorInterface'
2376
            );
2377
        }
2378
2379
        $this->validator = $validator;
2380
    }
2381
2382
    public function getValidator()
2383
    {
2384
        return $this->validator;
2385
    }
2386
2387
    public function getShow()
2388
    {
2389
        $this->buildShow();
2390
2391
        return $this->show;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->show; of type Sonata\AdminBundle\Admin...criptionCollection|null adds the type Sonata\AdminBundle\Admin...ldDescriptionCollection to the return on line 2391 which is incompatible with the return type declared by the interface Sonata\AdminBundle\Admin\AdminInterface::getShow of type array.
Loading history...
2392
    }
2393
2394
    public function setFormTheme(array $formTheme)
2395
    {
2396
        $this->formTheme = $formTheme;
2397
    }
2398
2399
    public function getFormTheme()
2400
    {
2401
        return $this->formTheme;
2402
    }
2403
2404
    public function setFilterTheme(array $filterTheme)
2405
    {
2406
        $this->filterTheme = $filterTheme;
2407
    }
2408
2409
    public function getFilterTheme()
2410
    {
2411
        return $this->filterTheme;
2412
    }
2413
2414
    public function addExtension(AdminExtensionInterface $extension)
2415
    {
2416
        $this->extensions[] = $extension;
2417
    }
2418
2419
    public function getExtensions()
2420
    {
2421
        return $this->extensions;
2422
    }
2423
2424
    public function setMenuFactory(MenuFactoryInterface $menuFactory)
2425
    {
2426
        $this->menuFactory = $menuFactory;
2427
    }
2428
2429
    public function getMenuFactory()
2430
    {
2431
        return $this->menuFactory;
2432
    }
2433
2434
    public function setRouteBuilder(RouteBuilderInterface $routeBuilder)
2435
    {
2436
        $this->routeBuilder = $routeBuilder;
2437
    }
2438
2439
    public function getRouteBuilder()
2440
    {
2441
        return $this->routeBuilder;
2442
    }
2443
2444
    public function toString($object)
2445
    {
2446
        if (!\is_object($object)) {
2447
            return '';
2448
        }
2449
2450
        if (method_exists($object, '__toString') && null !== $object->__toString()) {
2451
            return (string) $object;
2452
        }
2453
2454
        return sprintf('%s:%s', ClassUtils::getClass($object), spl_object_hash($object));
2455
    }
2456
2457
    public function setLabelTranslatorStrategy(LabelTranslatorStrategyInterface $labelTranslatorStrategy)
2458
    {
2459
        $this->labelTranslatorStrategy = $labelTranslatorStrategy;
2460
    }
2461
2462
    public function getLabelTranslatorStrategy()
2463
    {
2464
        return $this->labelTranslatorStrategy;
2465
    }
2466
2467
    public function supportsPreviewMode()
2468
    {
2469
        return $this->supportsPreviewMode;
2470
    }
2471
2472
    /**
2473
     * Set custom per page options.
2474
     */
2475
    public function setPerPageOptions(array $options)
2476
    {
2477
        $this->perPageOptions = $options;
2478
    }
2479
2480
    /**
2481
     * Returns predefined per page options.
2482
     *
2483
     * @return array
2484
     */
2485
    public function getPerPageOptions()
2486
    {
2487
        return $this->perPageOptions;
2488
    }
2489
2490
    /**
2491
     * Set pager type.
2492
     *
2493
     * @param string $pagerType
2494
     */
2495
    public function setPagerType($pagerType)
2496
    {
2497
        $this->pagerType = $pagerType;
2498
    }
2499
2500
    /**
2501
     * Get pager type.
2502
     *
2503
     * @return string
2504
     */
2505
    public function getPagerType()
2506
    {
2507
        return $this->pagerType;
2508
    }
2509
2510
    /**
2511
     * Returns true if the per page value is allowed, false otherwise.
2512
     *
2513
     * @param int $perPage
2514
     *
2515
     * @return bool
2516
     */
2517
    public function determinedPerPageValue($perPage)
2518
    {
2519
        return \in_array($perPage, $this->perPageOptions, true);
2520
    }
2521
2522
    public function isAclEnabled()
2523
    {
2524
        return $this->getSecurityHandler() instanceof AclSecurityHandlerInterface;
2525
    }
2526
2527
    public function getObjectMetadata($object)
2528
    {
2529
        return new Metadata($this->toString($object));
2530
    }
2531
2532
    public function getListModes()
2533
    {
2534
        return $this->listModes;
2535
    }
2536
2537
    public function setListMode($mode)
2538
    {
2539
        if (!$this->hasRequest()) {
2540
            throw new \RuntimeException(sprintf('No request attached to the current admin: %s', $this->getCode()));
2541
        }
2542
2543
        $this->getRequest()->getSession()->set(sprintf('%s.list_mode', $this->getCode()), $mode);
2544
    }
2545
2546
    public function getListMode()
2547
    {
2548
        if (!$this->hasRequest()) {
2549
            return 'list';
2550
        }
2551
2552
        return $this->getRequest()->getSession()->get(sprintf('%s.list_mode', $this->getCode()), 'list');
2553
    }
2554
2555
    public function getAccessMapping()
2556
    {
2557
        return $this->accessMapping;
2558
    }
2559
2560
    public function checkAccess($action, $object = null)
2561
    {
2562
        $access = $this->getAccess();
2563
2564
        if (!\array_key_exists($action, $access)) {
2565
            throw new \InvalidArgumentException(sprintf(
2566
                'Action "%s" could not be found in access mapping.'
2567
                .' Please make sure your action is defined into your admin class accessMapping property.',
2568
                $action
2569
            ));
2570
        }
2571
2572
        if (!\is_array($access[$action])) {
2573
            $access[$action] = [$access[$action]];
2574
        }
2575
2576
        foreach ($access[$action] as $role) {
2577
            if (false === $this->isGranted($role, $object)) {
2578
                throw new AccessDeniedException(sprintf('Access Denied to the action %s and role %s', $action, $role));
2579
            }
2580
        }
2581
    }
2582
2583
    /**
2584
     * Hook to handle access authorization, without throw Exception.
2585
     *
2586
     * @param string $action
2587
     * @param object $object
2588
     *
2589
     * @return bool
2590
     */
2591
    public function hasAccess($action, $object = null)
2592
    {
2593
        $access = $this->getAccess();
2594
2595
        if (!\array_key_exists($action, $access)) {
2596
            return false;
2597
        }
2598
2599
        if (!\is_array($access[$action])) {
2600
            $access[$action] = [$access[$action]];
2601
        }
2602
2603
        foreach ($access[$action] as $role) {
2604
            if (false === $this->isGranted($role, $object)) {
2605
                return false;
2606
            }
2607
        }
2608
2609
        return true;
2610
    }
2611
2612
    public function configureActionButtons($action, $object = null)
2613
    {
2614
        $list = [];
2615
2616
        if (\in_array($action, ['tree', 'show', 'edit', 'delete', 'list', 'batch'], true)
2617
            && $this->hasAccess('create')
2618
            && $this->hasRoute('create')
2619
        ) {
2620
            $list['create'] = [
2621
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2622
                '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 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...
2623
//                'template' => $this->getTemplateRegistry()->getTemplate('button_create'),
2624
            ];
2625
        }
2626
2627
        if (\in_array($action, ['show', 'delete', 'acl', 'history'], true)
2628
            && $this->canAccessObject('edit', $object)
2629
            && $this->hasRoute('edit')
2630
        ) {
2631
            $list['edit'] = [
2632
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2633
                '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 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...
2634
                //'template' => $this->getTemplateRegistry()->getTemplate('button_edit'),
2635
            ];
2636
        }
2637
2638
        if (\in_array($action, ['show', 'edit', 'acl'], true)
2639
            && $this->canAccessObject('history', $object)
2640
            && $this->hasRoute('history')
2641
        ) {
2642
            $list['history'] = [
2643
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2644
                '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 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...
2645
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_history'),
2646
            ];
2647
        }
2648
2649
        if (\in_array($action, ['edit', 'history'], true)
2650
            && $this->isAclEnabled()
2651
            && $this->canAccessObject('acl', $object)
2652
            && $this->hasRoute('acl')
2653
        ) {
2654
            $list['acl'] = [
2655
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2656
                '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 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...
2657
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_acl'),
2658
            ];
2659
        }
2660
2661
        if (\in_array($action, ['edit', 'history', 'acl'], true)
2662
            && $this->canAccessObject('show', $object)
2663
            && \count($this->getShow()) > 0
2664
            && $this->hasRoute('show')
2665
        ) {
2666
            $list['show'] = [
2667
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2668
                '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 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...
2669
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_show'),
2670
            ];
2671
        }
2672
2673
        if (\in_array($action, ['show', 'edit', 'delete', 'acl', 'batch'], true)
2674
            && $this->hasAccess('list')
2675
            && $this->hasRoute('list')
2676
        ) {
2677
            $list['list'] = [
2678
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2679
                '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 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...
2680
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_list'),
2681
            ];
2682
        }
2683
2684
        return $list;
2685
    }
2686
2687
    /**
2688
     * @param string $action
2689
     * @param mixed  $object
2690
     *
2691
     * @return array
2692
     */
2693
    public function getActionButtons($action, $object = null)
2694
    {
2695
        $list = $this->configureActionButtons($action, $object);
2696
2697
        foreach ($this->getExtensions() as $extension) {
2698
            // TODO: remove method check in next major release
2699
            if (method_exists($extension, 'configureActionButtons')) {
2700
                $list = $extension->configureActionButtons($this, $list, $action, $object);
2701
            }
2702
        }
2703
2704
        return $list;
2705
    }
2706
2707
    /**
2708
     * Get the list of actions that can be accessed directly from the dashboard.
2709
     *
2710
     * @return array
2711
     */
2712
    public function getDashboardActions()
2713
    {
2714
        $actions = [];
2715
2716
        if ($this->hasRoute('create') && $this->hasAccess('create')) {
2717
            $actions['create'] = [
2718
                'label' => 'link_add',
2719
                'translation_domain' => 'SonataAdminBundle',
2720
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2721
                '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 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...
2722
                // 'template' => $this->getTemplateRegistry()->getTemplate('action_create'),
2723
                'url' => $this->generateUrl('create'),
2724
                'icon' => 'plus-circle',
2725
            ];
2726
        }
2727
2728
        if ($this->hasRoute('list') && $this->hasAccess('list')) {
2729
            $actions['list'] = [
2730
                'label' => 'link_list',
2731
                'translation_domain' => 'SonataAdminBundle',
2732
                'url' => $this->generateUrl('list'),
2733
                'icon' => 'list',
2734
            ];
2735
        }
2736
2737
        return $actions;
2738
    }
2739
2740
    /**
2741
     * Setting to true will enable mosaic button for the admin screen.
2742
     * Setting to false will hide mosaic button for the admin screen.
2743
     *
2744
     * @param bool $isShown
2745
     */
2746
    final public function showMosaicButton($isShown)
2747
    {
2748
        if ($isShown) {
2749
            $this->listModes['mosaic'] = ['class' => static::MOSAIC_ICON_CLASS];
2750
        } else {
2751
            unset($this->listModes['mosaic']);
2752
        }
2753
    }
2754
2755
    /**
2756
     * @param object $object
2757
     */
2758
    final public function getSearchResultLink($object)
2759
    {
2760
        foreach ($this->searchResultActions as $action) {
2761
            if ($this->hasRoute($action) && $this->hasAccess($action, $object)) {
2762
                return $this->generateObjectUrl($action, $object);
2763
            }
2764
        }
2765
    }
2766
2767
    /**
2768
     * Checks if a filter type is set to a default value.
2769
     *
2770
     * @param string $name
2771
     *
2772
     * @return bool
2773
     */
2774
    final public function isDefaultFilter($name)
2775
    {
2776
        $filter = $this->getFilterParameters();
2777
        $default = $this->getDefaultFilterValues();
2778
2779
        if (!\array_key_exists($name, $filter) || !\array_key_exists($name, $default)) {
2780
            return false;
2781
        }
2782
2783
        return $filter[$name] === $default[$name];
2784
    }
2785
2786
    /**
2787
     * Check object existence and access, without throw Exception.
2788
     *
2789
     * @param string $action
2790
     * @param object $object
2791
     *
2792
     * @return bool
2793
     */
2794
    public function canAccessObject($action, $object)
2795
    {
2796
        return $object && $this->id($object) && $this->hasAccess($action, $object);
2797
    }
2798
2799
    /**
2800
     * @return MutableTemplateRegistryInterface
2801
     */
2802
    final protected function getTemplateRegistry()
2803
    {
2804
        return $this->templateRegistry;
2805
    }
2806
2807
    /**
2808
     * Returns a list of default filters.
2809
     *
2810
     * @return array
2811
     */
2812
    final protected function getDefaultFilterValues()
2813
    {
2814
        $defaultFilterValues = [];
2815
2816
        $this->configureDefaultFilterValues($defaultFilterValues);
2817
2818
        foreach ($this->getExtensions() as $extension) {
2819
            // NEXT_MAJOR: remove method check in next major release
2820
            if (method_exists($extension, 'configureDefaultFilterValues')) {
2821
                $extension->configureDefaultFilterValues($this, $defaultFilterValues);
2822
            }
2823
        }
2824
2825
        return $defaultFilterValues;
2826
    }
2827
2828
    protected function configureFormFields(FormMapper $form)
2829
    {
2830
    }
2831
2832
    protected function configureListFields(ListMapper $list)
2833
    {
2834
    }
2835
2836
    protected function configureDatagridFilters(DatagridMapper $filter)
2837
    {
2838
    }
2839
2840
    protected function configureShowFields(ShowMapper $show)
2841
    {
2842
    }
2843
2844
    protected function configureRoutes(RouteCollection $collection)
2845
    {
2846
    }
2847
2848
    /**
2849
     * Allows you to customize batch actions.
2850
     *
2851
     * @param array $actions List of actions
2852
     *
2853
     * @return array
2854
     */
2855
    protected function configureBatchActions($actions)
2856
    {
2857
        return $actions;
2858
    }
2859
2860
    /**
2861
     * NEXT_MAJOR: remove this method.
2862
     *
2863
     * @return mixed
2864
     *
2865
     * @deprecated Use configureTabMenu instead
2866
     */
2867
    protected function configureSideMenu(MenuItemInterface $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...
2868
    {
2869
    }
2870
2871
    /**
2872
     * Configures the tab menu in your admin.
2873
     *
2874
     * @param string $action
2875
     *
2876
     * @return mixed
2877
     */
2878
    protected function configureTabMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null)
2879
    {
2880
        // Use configureSideMenu not to mess with previous overrides
2881
        // TODO remove once deprecation period is over
2882
        $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...
2883
    }
2884
2885
    /**
2886
     * build the view FieldDescription array.
2887
     */
2888
    protected function buildShow()
2889
    {
2890
        if ($this->show) {
2891
            return;
2892
        }
2893
2894
        $this->show = new FieldDescriptionCollection();
2895
        $mapper = new ShowMapper($this->showBuilder, $this->show, $this);
2896
2897
        $this->configureShowFields($mapper);
2898
2899
        foreach ($this->getExtensions() as $extension) {
2900
            $extension->configureShowFields($mapper);
2901
        }
2902
    }
2903
2904
    /**
2905
     * build the list FieldDescription array.
2906
     */
2907
    protected function buildList()
2908
    {
2909
        if ($this->list) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->list of type array 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...
2910
            return;
2911
        }
2912
2913
        $this->list = $this->getListBuilder()->getBaseList();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getListBuilder()->getBaseList() of type object<Sonata\AdminBundl...dDescriptionCollection> is incompatible with the declared type array of property $list.

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...
2914
2915
        $mapper = new ListMapper($this->getListBuilder(), $this->list, $this);
2916
2917
        if (\count($this->getBatchActions()) > 0) {
2918
            $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance(
2919
                $this->getClass(),
2920
                'batch',
2921
                [
2922
                    'label' => 'batch',
2923
                    'code' => '_batch',
2924
                    'sortable' => false,
2925
                    'virtual_field' => true,
2926
                ]
2927
            );
2928
2929
            $fieldDescription->setAdmin($this);
2930
            // NEXT_MAJOR: Remove this line and use commented line below it instead
2931
            $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 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...
2932
            // $fieldDescription->setTemplate($this->getTemplateRegistry()->getTemplate('batch'));
2933
2934
            $mapper->add($fieldDescription, 'batch');
0 ignored issues
show
Documentation introduced by
$fieldDescription is of type object<Sonata\AdminBundl...ldDescriptionInterface>, 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...
2935
        }
2936
2937
        $this->configureListFields($mapper);
2938
2939
        foreach ($this->getExtensions() as $extension) {
2940
            $extension->configureListFields($mapper);
2941
        }
2942
2943
        if ($this->hasRequest() && $this->getRequest()->isXmlHttpRequest()) {
2944
            $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance(
2945
                $this->getClass(),
2946
                'select',
2947
                [
2948
                    'label' => false,
2949
                    'code' => '_select',
2950
                    'sortable' => false,
2951
                    'virtual_field' => false,
2952
                ]
2953
            );
2954
2955
            $fieldDescription->setAdmin($this);
2956
            // NEXT_MAJOR: Remove this line and use commented line below it instead
2957
            $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 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...
2958
            // $fieldDescription->setTemplate($this->getTemplateRegistry()->getTemplate('select'));
2959
2960
            $mapper->add($fieldDescription, 'select');
0 ignored issues
show
Documentation introduced by
$fieldDescription is of type object<Sonata\AdminBundl...ldDescriptionInterface>, 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...
2961
        }
2962
    }
2963
2964
    /**
2965
     * Build the form FieldDescription collection.
2966
     */
2967
    protected function buildForm()
2968
    {
2969
        if ($this->form) {
2970
            return;
2971
        }
2972
2973
        // append parent object if any
2974
        // todo : clean the way the Admin class can retrieve set the object
2975
        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...
2976
            $parent = $this->getParent()->getObject($this->request->get($this->getParent()->getIdParameter()));
2977
2978
            $propertyAccessor = $this->getConfigurationPool()->getPropertyAccessor();
2979
            $propertyPath = new PropertyPath($this->getParentAssociationMapping());
2980
2981
            $object = $this->getSubject();
2982
2983
            $value = $propertyAccessor->getValue($object, $propertyPath);
0 ignored issues
show
Bug introduced by
It seems like $object defined by $this->getSubject() on line 2981 can also be of type null; however, Symfony\Component\Proper...orInterface::getValue() does only seem to accept object|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...
2984
2985
            if (\is_array($value) || ($value instanceof \Traversable && $value instanceof \ArrayAccess)) {
2986
                $value[] = $parent;
2987
                $propertyAccessor->setValue($object, $propertyPath, $value);
0 ignored issues
show
Bug introduced by
It seems like $object defined by $this->getSubject() on line 2981 can also be of type null; however, Symfony\Component\Proper...orInterface::setValue() does only seem to accept object|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...
2988
            } else {
2989
                $propertyAccessor->setValue($object, $propertyPath, $parent);
0 ignored issues
show
Bug introduced by
It seems like $object defined by $this->getSubject() on line 2981 can also be of type null; however, Symfony\Component\Proper...orInterface::setValue() does only seem to accept object|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...
2990
            }
2991
        }
2992
2993
        $formBuilder = $this->getFormBuilder();
2994
        $formBuilder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
2995
            $this->preValidate($event->getData());
2996
        }, 100);
2997
2998
        $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...
2999
    }
3000
3001
    /**
3002
     * Gets the subclass corresponding to the given name.
3003
     *
3004
     * @param string $name The name of the sub class
3005
     *
3006
     * @return string the subclass
3007
     */
3008
    protected function getSubClass($name)
3009
    {
3010
        if ($this->hasSubClass($name)) {
3011
            return $this->subClasses[$name];
3012
        }
3013
3014
        throw new \RuntimeException(sprintf(
3015
            'Unable to find the subclass `%s` for admin `%s`',
3016
            $name,
3017
            \get_class($this)
3018
        ));
3019
    }
3020
3021
    /**
3022
     * Attach the inline validator to the model metadata, this must be done once per admin.
3023
     */
3024
    protected function attachInlineValidator()
3025
    {
3026
        $admin = $this;
3027
3028
        // add the custom inline validation option
3029
        $metadata = $this->validator->getMetadataFor($this->getClass());
3030
3031
        $metadata->addConstraint(new InlineConstraint([
3032
            'service' => $this,
3033
            'method' => function (ErrorElement $errorElement, $object) use ($admin) {
3034
                /* @var \Sonata\AdminBundle\Admin\AdminInterface $admin */
3035
3036
                // This avoid the main validation to be cascaded to children
3037
                // The problem occurs when a model Page has a collection of Page as property
3038
                if ($admin->hasSubject() && spl_object_hash($object) !== spl_object_hash($admin->getSubject())) {
3039
                    return;
3040
                }
3041
3042
                $admin->validate($errorElement, $object);
3043
3044
                foreach ($admin->getExtensions() as $extension) {
3045
                    $extension->validate($admin, $errorElement, $object);
3046
                }
3047
            },
3048
            'serializingWarning' => true,
3049
        ]));
3050
    }
3051
3052
    /**
3053
     * Predefine per page options.
3054
     */
3055
    protected function predefinePerPageOptions()
3056
    {
3057
        array_unshift($this->perPageOptions, $this->maxPerPage);
3058
        $this->perPageOptions = array_unique($this->perPageOptions);
3059
        sort($this->perPageOptions);
3060
    }
3061
3062
    /**
3063
     * Return list routes with permissions name.
3064
     *
3065
     * @return array
3066
     */
3067
    protected function getAccess()
3068
    {
3069
        $access = array_merge([
3070
            'acl' => 'MASTER',
3071
            'export' => 'EXPORT',
3072
            'historyCompareRevisions' => 'EDIT',
3073
            'historyViewRevision' => 'EDIT',
3074
            'history' => 'EDIT',
3075
            'edit' => 'EDIT',
3076
            'show' => 'VIEW',
3077
            'create' => 'CREATE',
3078
            'delete' => 'DELETE',
3079
            'batchDelete' => 'DELETE',
3080
            'list' => 'LIST',
3081
        ], $this->getAccessMapping());
3082
3083
        foreach ($this->extensions as $extension) {
3084
            // TODO: remove method check in next major release
3085
            if (method_exists($extension, 'getAccessMapping')) {
3086
                $access = array_merge($access, $extension->getAccessMapping($this));
3087
            }
3088
        }
3089
3090
        return $access;
3091
    }
3092
3093
    /**
3094
     * Returns a list of default filters.
3095
     */
3096
    protected function configureDefaultFilterValues(array &$filterValues)
3097
    {
3098
    }
3099
3100
    /**
3101
     * Build all the related urls to the current admin.
3102
     */
3103
    private function buildRoutes(): void
3104
    {
3105
        if ($this->loaded['routes']) {
3106
            return;
3107
        }
3108
3109
        $this->loaded['routes'] = true;
3110
3111
        $this->routes = new RouteCollection(
3112
            $this->getBaseCodeRoute(),
3113
            $this->getBaseRouteName(),
3114
            $this->getBaseRoutePattern(),
3115
            $this->getBaseControllerName()
3116
        );
3117
3118
        $this->routeBuilder->build($this, $this->routes);
3119
3120
        $this->configureRoutes($this->routes);
3121
3122
        foreach ($this->getExtensions() as $extension) {
3123
            $extension->configureRoutes($this, $this->routes);
3124
        }
3125
    }
3126
}
3127
3128
class_exists(\Sonata\Form\Validator\ErrorElement::class);
3129