Completed
Push — 3.x ( 13fffb...881418 )
by Vincent
02:54
created

AbstractAdmin::supportsPreviewMode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Admin;
15
16
use Doctrine\Common\Util\ClassUtils;
17
use Knp\Menu\FactoryInterface 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 sonata-project/admin-bundle 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 sonata-project/admin-bundle 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 sonata-project/admin-bundle 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<string, bool>
389
     */
390
    protected $loaded = [
391
        'view_fields' => false,
392
        'view_groups' => false,
393
        'routes' => false,
394
        'tab_menu' => false,
395
    ];
396
397
    /**
398
     * @var string[]
399
     */
400
    protected $formTheme = [];
401
402
    /**
403
     * @var string[]
404
     */
405
    protected $filterTheme = [];
406
407
    /**
408
     * @var array<string, string>
409
     *
410
     * @deprecated since sonata-project/admin-bundle 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<string, string>
481
     */
482
    private $subClasses = [];
483
484
    /**
485
     * The list collection.
486
     *
487
     * @var FieldDescriptionCollection
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 sonata-project/admin-bundle 3.24 and will be removed in 4.0

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

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

Loading history...
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
    /**
724
     * @param object $object
725
     */
726
    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...
727
    {
728
    }
729
730
    public function preUpdate($object)
731
    {
732
    }
733
734
    public function postUpdate($object)
735
    {
736
    }
737
738
    public function prePersist($object)
739
    {
740
    }
741
742
    public function postPersist($object)
743
    {
744
    }
745
746
    public function preRemove($object)
747
    {
748
    }
749
750
    public function postRemove($object)
751
    {
752
    }
753
754
    public function preBatchAction($actionName, ProxyQueryInterface $query, array &$idx, $allElements)
755
    {
756
    }
757
758
    public function getFilterParameters()
759
    {
760
        $parameters = [];
761
762
        // build the values array
763
        if ($this->hasRequest()) {
764
            $filters = $this->request->query->get('filter', []);
765
            if (isset($filters['_page'])) {
766
                $filters['_page'] = (int) $filters['_page'];
767
            }
768
            if (isset($filters['_per_page'])) {
769
                $filters['_per_page'] = (int) $filters['_per_page'];
770
            }
771
772
            // if filter persistence is configured
773
            // NEXT_MAJOR: remove `$this->persistFilters !== false` from the condition
774
            if (false !== $this->persistFilters && null !== $this->filterPersister) {
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$persistFilters has been deprecated with message: since sonata-project/admin-bundle 3.34, to be removed in 4.0.

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

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

Loading history...
775
                // if reset filters is asked, remove from storage
776
                if ('reset' === $this->request->query->get('filters')) {
777
                    $this->filterPersister->reset($this->getCode());
778
                }
779
780
                // if no filters, fetch from storage
781
                // otherwise save to storage
782
                if (empty($filters)) {
783
                    $filters = $this->filterPersister->get($this->getCode());
784
                } else {
785
                    $this->filterPersister->set($this->getCode(), $filters);
786
                }
787
            }
788
789
            $parameters = array_merge(
790
                $this->getModelManager()->getDefaultSortValues($this->getClass()),
791
                $this->datagridValues,
792
                $this->getDefaultFilterValues(),
793
                $filters
794
            );
795
796
            if (!$this->determinedPerPageValue($parameters['_per_page'])) {
797
                $parameters['_per_page'] = $this->maxPerPage;
798
            }
799
800
            // always force the parent value
801
            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...
802
                $name = str_replace('.', '__', $this->getParentAssociationMapping());
803
                $parameters[$name] = ['value' => $this->request->get($this->getParent()->getIdParameter())];
804
            }
805
        }
806
807
        return $parameters;
808
    }
809
810
    public function buildDatagrid()
811
    {
812
        if ($this->datagrid) {
813
            return;
814
        }
815
816
        $filterParameters = $this->getFilterParameters();
817
818
        // transform _sort_by from a string to a FieldDescriptionInterface for the datagrid.
819
        if (isset($filterParameters['_sort_by']) && \is_string($filterParameters['_sort_by'])) {
820
            if ($this->hasListFieldDescription($filterParameters['_sort_by'])) {
821
                $filterParameters['_sort_by'] = $this->getListFieldDescription($filterParameters['_sort_by']);
822
            } else {
823
                $filterParameters['_sort_by'] = $this->getModelManager()->getNewFieldDescriptionInstance(
824
                    $this->getClass(),
825
                    $filterParameters['_sort_by'],
826
                    []
827
                );
828
829
                $this->getListBuilder()->buildField(null, $filterParameters['_sort_by'], $this);
830
            }
831
        }
832
833
        // initialize the datagrid
834
        $this->datagrid = $this->getDatagridBuilder()->getBaseDatagrid($this, $filterParameters);
835
836
        $this->datagrid->getPager()->setMaxPageLinks($this->maxPageLinks);
837
838
        $mapper = new DatagridMapper($this->getDatagridBuilder(), $this->datagrid, $this);
839
840
        // build the datagrid filter
841
        $this->configureDatagridFilters($mapper);
842
843
        // ok, try to limit to add parent filter
844
        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...
845
            $mapper->add($this->getParentAssociationMapping(), null, [
846
                'show_filter' => false,
847
                'label' => false,
848
                'field_type' => ModelHiddenType::class,
849
                'field_options' => [
850
                    'model_manager' => $this->getModelManager(),
851
                ],
852
                'operator_type' => HiddenType::class,
853
            ], null, null, [
854
                'admin_code' => $this->getParent()->getCode(),
855
            ]);
856
        }
857
858
        foreach ($this->getExtensions() as $extension) {
859
            $extension->configureDatagridFilters($mapper);
860
        }
861
    }
862
863
    /**
864
     * Returns the name of the parent related field, so the field can be use to set the default
865
     * value (ie the parent object) or to filter the object.
866
     *
867
     * @throws \InvalidArgumentException
868
     *
869
     * @return string|null
870
     */
871
    public function getParentAssociationMapping()
872
    {
873
        // NEXT_MAJOR: remove array check
874
        if (\is_array($this->parentAssociationMapping) && $this->getParent()) {
875
            $parent = $this->getParent()->getCode();
876
877
            if (\array_key_exists($parent, $this->parentAssociationMapping)) {
878
                return $this->parentAssociationMapping[$parent];
879
            }
880
881
            throw new \InvalidArgumentException(sprintf(
882
                "There's no association between %s and %s.",
883
                $this->getCode(),
884
                $this->getParent()->getCode()
885
            ));
886
        }
887
888
        // NEXT_MAJOR: remove this line
889
        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 889 which is incompatible with the return type documented by Sonata\AdminBundle\Admin...arentAssociationMapping of type string|null.
Loading history...
890
    }
891
892
    /**
893
     * @param string $code
894
     * @param string $value
895
     */
896
    final public function addParentAssociationMapping($code, $value)
897
    {
898
        $this->parentAssociationMapping[$code] = $value;
899
    }
900
901
    /**
902
     * Returns the baseRoutePattern used to generate the routing information.
903
     *
904
     * @throws \RuntimeException
905
     *
906
     * @return string the baseRoutePattern used to generate the routing information
907
     */
908
    public function getBaseRoutePattern()
909
    {
910
        if (null !== $this->cachedBaseRoutePattern) {
911
            return $this->cachedBaseRoutePattern;
912
        }
913
914
        if ($this->isChild()) { // the admin class is a child, prefix it with the parent route pattern
915
            $baseRoutePattern = $this->baseRoutePattern;
916
            if (!$this->baseRoutePattern) {
917
                preg_match(self::CLASS_REGEX, $this->class, $matches);
918
919
                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...
920
                    throw new \RuntimeException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`', static::class));
921
                }
922
                $baseRoutePattern = $this->urlize($matches[5], '-');
923
            }
924
925
            $this->cachedBaseRoutePattern = sprintf(
926
                '%s/%s/%s',
927
                $this->getParent()->getBaseRoutePattern(),
928
                $this->getParent()->getRouterIdParameter(),
929
                $baseRoutePattern
930
            );
931
        } elseif ($this->baseRoutePattern) {
932
            $this->cachedBaseRoutePattern = $this->baseRoutePattern;
933
        } else {
934
            preg_match(self::CLASS_REGEX, $this->class, $matches);
935
936
            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...
937
                throw new \RuntimeException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`', static::class));
938
            }
939
940
            $this->cachedBaseRoutePattern = sprintf(
941
                '/%s%s/%s',
942
                empty($matches[1]) ? '' : $this->urlize($matches[1], '-').'/',
943
                $this->urlize($matches[3], '-'),
944
                $this->urlize($matches[5], '-')
945
            );
946
        }
947
948
        return $this->cachedBaseRoutePattern;
949
    }
950
951
    /**
952
     * Returns the baseRouteName used to generate the routing information.
953
     *
954
     * @throws \RuntimeException
955
     *
956
     * @return string the baseRouteName used to generate the routing information
957
     */
958
    public function getBaseRouteName()
959
    {
960
        if (null !== $this->cachedBaseRouteName) {
961
            return $this->cachedBaseRouteName;
962
        }
963
964
        if ($this->isChild()) { // the admin class is a child, prefix it with the parent route name
965
            $baseRouteName = $this->baseRouteName;
966
            if (!$this->baseRouteName) {
967
                preg_match(self::CLASS_REGEX, $this->class, $matches);
968
969
                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...
970
                    throw new \RuntimeException(sprintf('Cannot automatically determine base route name, please define a default `baseRouteName` value for the admin class `%s`', static::class));
971
                }
972
                $baseRouteName = $this->urlize($matches[5]);
973
            }
974
975
            $this->cachedBaseRouteName = sprintf(
976
                '%s_%s',
977
                $this->getParent()->getBaseRouteName(),
978
                $baseRouteName
979
            );
980
        } elseif ($this->baseRouteName) {
981
            $this->cachedBaseRouteName = $this->baseRouteName;
982
        } else {
983
            preg_match(self::CLASS_REGEX, $this->class, $matches);
984
985
            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...
986
                throw new \RuntimeException(sprintf('Cannot automatically determine base route name, please define a default `baseRouteName` value for the admin class `%s`', static::class));
987
            }
988
989
            $this->cachedBaseRouteName = sprintf(
990
                'admin_%s%s_%s',
991
                empty($matches[1]) ? '' : $this->urlize($matches[1]).'_',
992
                $this->urlize($matches[3]),
993
                $this->urlize($matches[5])
994
            );
995
        }
996
997
        return $this->cachedBaseRouteName;
998
    }
999
1000
    /**
1001
     * urlize the given word.
1002
     *
1003
     * @param string $word
1004
     * @param string $sep  the separator
1005
     *
1006
     * @return string
1007
     */
1008
    public function urlize($word, $sep = '_')
1009
    {
1010
        return strtolower(preg_replace('/[^a-z0-9_]/i', $sep.'$1', $word));
1011
    }
1012
1013
    public function getClass()
1014
    {
1015
        if ($this->hasActiveSubClass()) {
1016
            if ($this->getParentFieldDescription()) {
1017
                throw new \RuntimeException('Feature not implemented: an embedded admin cannot have subclass');
1018
            }
1019
1020
            $subClass = $this->getRequest()->query->get('subclass');
1021
1022
            if (!$this->hasSubClass($subClass)) {
1023
                throw new \RuntimeException(sprintf('Subclass "%s" is not defined.', $subClass));
1024
            }
1025
1026
            return $this->getSubClass($subClass);
1027
        }
1028
1029
        // see https://github.com/sonata-project/SonataCoreBundle/commit/247eeb0a7ca7211142e101754769d70bc402a5b4
1030
        if ($this->subject && \is_object($this->subject)) {
1031
            return ClassUtils::getClass($this->subject);
1032
        }
1033
1034
        return $this->class;
1035
    }
1036
1037
    public function getSubClasses()
1038
    {
1039
        return $this->subClasses;
1040
    }
1041
1042
    /**
1043
     * NEXT_MAJOR: remove this method.
1044
     */
1045
    public function addSubClass($subClass)
1046
    {
1047
        @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...
1048
            'Method "%s" is deprecated since sonata-project/admin-bundle 3.30 and will be removed in 4.0.',
1049
            __METHOD__
1050
        ), E_USER_DEPRECATED);
1051
1052
        if (!\in_array($subClass, $this->subClasses, true)) {
1053
            $this->subClasses[] = $subClass;
1054
        }
1055
    }
1056
1057
    public function setSubClasses(array $subClasses)
1058
    {
1059
        $this->subClasses = $subClasses;
1060
    }
1061
1062
    public function hasSubClass($name)
1063
    {
1064
        return isset($this->subClasses[$name]);
1065
    }
1066
1067
    public function hasActiveSubClass()
1068
    {
1069
        if (\count($this->subClasses) > 0 && $this->request) {
1070
            return null !== $this->getRequest()->query->get('subclass');
1071
        }
1072
1073
        return false;
1074
    }
1075
1076
    public function getActiveSubClass()
1077
    {
1078
        if (!$this->hasActiveSubClass()) {
1079
            @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...
1080
                'Calling %s() when there is no active subclass is deprecated since sonata-project/admin-bundle 3.52 and will throw an exception in 4.0. '.
1081
                'Use %s::hasActiveSubClass() to know if there is an active subclass.',
1082
                __METHOD__,
1083
                __CLASS__
1084
            ), E_USER_DEPRECATED);
1085
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare string as return type
1086
            // throw new \LogicException(sprintf(
1087
            //    'Admin "%s" has no active subclass.',
1088
            //    static::class
1089
            // ));
1090
1091
            return null;
1092
        }
1093
1094
        return $this->getSubClass($this->getActiveSubclassCode());
1095
    }
1096
1097
    public function getActiveSubclassCode()
1098
    {
1099
        if (!$this->hasActiveSubClass()) {
1100
            @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...
1101
                'Calling %s() when there is no active subclass is deprecated since sonata-project/admin-bundle 3.52 and will throw an exception in 4.0. '.
1102
                'Use %s::hasActiveSubClass() to know if there is an active subclass.',
1103
                __METHOD__,
1104
                __CLASS__
1105
            ), E_USER_DEPRECATED);
1106
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare string as return type
1107
            // throw new \LogicException(sprintf(
1108
            //    'Admin "%s" has no active subclass.',
1109
            //    static::class
1110
            // ));
1111
1112
            return null;
1113
        }
1114
1115
        $subClass = $this->getRequest()->query->get('subclass');
1116
1117
        if (!$this->hasSubClass($subClass)) {
1118
            @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...
1119
                'Calling %s() when there is no active subclass is deprecated since sonata-project/admin-bundle 3.52 and will throw an exception in 4.0. '.
1120
                'Use %s::hasActiveSubClass() to know if there is an active subclass.',
1121
                __METHOD__,
1122
                __CLASS__
1123
            ), E_USER_DEPRECATED);
1124
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare string as return type
1125
            // throw new \LogicException(sprintf(
1126
            //    'Admin "%s" has no active subclass.',
1127
            //    static::class
1128
            // ));
1129
1130
            return null;
1131
        }
1132
1133
        return $subClass;
1134
    }
1135
1136
    public function getBatchActions()
1137
    {
1138
        $actions = [];
1139
1140
        if ($this->hasRoute('delete') && $this->hasAccess('delete')) {
1141
            $actions['delete'] = [
1142
                'label' => 'action_delete',
1143
                'translation_domain' => 'SonataAdminBundle',
1144
                'ask_confirmation' => true, // by default always true
1145
            ];
1146
        }
1147
1148
        $actions = $this->configureBatchActions($actions);
1149
1150
        foreach ($this->getExtensions() as $extension) {
1151
            // NEXT_MAJOR: remove method check
1152
            if (method_exists($extension, 'configureBatchActions')) {
1153
                $actions = $extension->configureBatchActions($this, $actions);
1154
            }
1155
        }
1156
1157
        foreach ($actions  as $name => &$action) {
1158
            if (!\array_key_exists('label', $action)) {
1159
                $action['label'] = $this->getTranslationLabel($name, 'batch', 'label');
1160
            }
1161
1162
            if (!\array_key_exists('translation_domain', $action)) {
1163
                $action['translation_domain'] = $this->getTranslationDomain();
1164
            }
1165
        }
1166
1167
        return $actions;
1168
    }
1169
1170
    public function getRoutes()
1171
    {
1172
        $this->buildRoutes();
1173
1174
        return $this->routes;
1175
    }
1176
1177
    public function getRouterIdParameter()
1178
    {
1179
        return '{'.$this->getIdParameter().'}';
1180
    }
1181
1182
    public function getIdParameter()
1183
    {
1184
        $parameter = 'id';
1185
1186
        for ($i = 0; $i < $this->getChildDepth(); ++$i) {
1187
            $parameter = 'child'.ucfirst($parameter);
1188
        }
1189
1190
        return $parameter;
1191
    }
1192
1193
    public function hasRoute($name)
1194
    {
1195
        if (!$this->routeGenerator) {
1196
            throw new \RuntimeException('RouteGenerator cannot be null');
1197
        }
1198
1199
        return $this->routeGenerator->hasAdminRoute($this, $name);
1200
    }
1201
1202
    /**
1203
     * @param string      $name
1204
     * @param string|null $adminCode
1205
     *
1206
     * @return bool
1207
     */
1208
    public function isCurrentRoute($name, $adminCode = null)
1209
    {
1210
        if (!$this->hasRequest()) {
1211
            return false;
1212
        }
1213
1214
        $request = $this->getRequest();
1215
        $route = $request->get('_route');
1216
1217
        if ($adminCode) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $adminCode of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1218
            $admin = $this->getConfigurationPool()->getAdminByAdminCode($adminCode);
1219
        } else {
1220
            $admin = $this;
1221
        }
1222
1223
        if (!$admin) {
1224
            return false;
1225
        }
1226
1227
        return ($admin->getBaseRouteName().'_'.$name) === $route;
1228
    }
1229
1230
    public function generateObjectUrl($name, $object, array $parameters = [], $absolute = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
1231
    {
1232
        $parameters['id'] = $this->getUrlsafeIdentifier($object);
1233
1234
        return $this->generateUrl($name, $parameters, $absolute);
1235
    }
1236
1237
    public function generateUrl($name, array $parameters = [], $absolute = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
1238
    {
1239
        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...
1240
    }
1241
1242
    public function generateMenuUrl($name, array $parameters = [], $absolute = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
1243
    {
1244
        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...
1245
    }
1246
1247
    final public function setTemplateRegistry(MutableTemplateRegistryInterface $templateRegistry)
1248
    {
1249
        $this->templateRegistry = $templateRegistry;
1250
    }
1251
1252
    /**
1253
     * @param array<string, string> $templates
1254
     */
1255
    public function setTemplates(array $templates)
1256
    {
1257
        // NEXT_MAJOR: Remove this line
1258
        $this->templates = $templates;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin\AbstractAdmin::$templates has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead

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

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

Loading history...
1259
1260
        $this->getTemplateRegistry()->setTemplates($templates);
1261
    }
1262
1263
    /**
1264
     * @param string $name
1265
     * @param string $template
1266
     */
1267
    public function setTemplate($name, $template)
1268
    {
1269
        // NEXT_MAJOR: Remove this line
1270
        $this->templates[$name] = $template;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin\AbstractAdmin::$templates has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead

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

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

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

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

Loading history...
1279
     */
1280
    public function getTemplates()
1281
    {
1282
        return $this->getTemplateRegistry()->getTemplates();
1283
    }
1284
1285
    /**
1286
     * @deprecated since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead
1287
     *
1288
     * @param string $name
1289
     *
1290
     * @return string|null
1291
     */
1292
    public function getTemplate($name)
1293
    {
1294
        return $this->getTemplateRegistry()->getTemplate($name);
1295
    }
1296
1297
    public function getNewInstance()
1298
    {
1299
        $object = $this->getModelManager()->getModelInstance($this->getClass());
1300
        foreach ($this->getExtensions() as $extension) {
1301
            $extension->alterNewInstance($this, $object);
1302
        }
1303
1304
        return $object;
1305
    }
1306
1307
    public function getFormBuilder()
1308
    {
1309
        $this->formOptions['data_class'] = $this->getClass();
1310
1311
        $formBuilder = $this->getFormContractor()->getFormBuilder(
1312
            $this->getUniqid(),
1313
            $this->formOptions
1314
        );
1315
1316
        $this->defineFormBuilder($formBuilder);
1317
1318
        return $formBuilder;
1319
    }
1320
1321
    /**
1322
     * This method is being called by the main admin class and the child class,
1323
     * the getFormBuilder is only call by the main admin class.
1324
     */
1325
    public function defineFormBuilder(FormBuilderInterface $formBuilder)
1326
    {
1327
        $mapper = new FormMapper($this->getFormContractor(), $formBuilder, $this);
1328
1329
        $this->configureFormFields($mapper);
1330
1331
        foreach ($this->getExtensions() as $extension) {
1332
            $extension->configureFormFields($mapper);
1333
        }
1334
1335
        $this->attachInlineValidator();
1336
    }
1337
1338
    public function attachAdminClass(FieldDescriptionInterface $fieldDescription)
1339
    {
1340
        $pool = $this->getConfigurationPool();
1341
1342
        $adminCode = $fieldDescription->getOption('admin_code');
1343
1344
        if (null !== $adminCode) {
1345
            $admin = $pool->getAdminByAdminCode($adminCode);
1346
        } else {
1347
            $admin = $pool->getAdminByClass($fieldDescription->getTargetEntity());
1348
        }
1349
1350
        if (!$admin) {
1351
            return;
1352
        }
1353
1354
        if ($this->hasRequest()) {
1355
            $admin->setRequest($this->getRequest());
1356
        }
1357
1358
        $fieldDescription->setAssociationAdmin($admin);
1359
    }
1360
1361
    public function getObject($id)
1362
    {
1363
        $object = $this->getModelManager()->find($this->getClass(), $id);
1364
        foreach ($this->getExtensions() as $extension) {
1365
            $extension->alterObject($this, $object);
0 ignored issues
show
Bug introduced by
It seems like $object defined by $this->getModelManager()...$this->getClass(), $id) on line 1363 can also be of type null; however, Sonata\AdminBundle\Admin...nterface::alterObject() does only seem to accept object, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
1366
        }
1367
1368
        return $object;
1369
    }
1370
1371
    public function getForm()
1372
    {
1373
        $this->buildForm();
1374
1375
        return $this->form;
1376
    }
1377
1378
    public function getList()
1379
    {
1380
        $this->buildList();
1381
1382
        return $this->list;
1383
    }
1384
1385
    /**
1386
     * @final since sonata-project/admin-bundle 3.63.0
1387
     */
1388
    public function createQuery($context = 'list')
1389
    {
1390
        if (\func_num_args() > 0) {
1391
            @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...
1392
                'The $context argument of '.__METHOD__.' is deprecated since 3.3, to be removed in 4.0.',
1393
                E_USER_DEPRECATED
1394
            );
1395
        }
1396
1397
        $query = $this->getModelManager()->createQuery($this->getClass());
1398
1399
        $query = $this->configureQuery($query);
1400
        foreach ($this->extensions as $extension) {
1401
            $extension->configureQuery($this, $query, $context);
1402
        }
1403
1404
        return $query;
1405
    }
1406
1407
    public function getDatagrid()
1408
    {
1409
        $this->buildDatagrid();
1410
1411
        return $this->datagrid;
1412
    }
1413
1414
    public function buildTabMenu($action, AdminInterface $childAdmin = null)
1415
    {
1416
        if ($this->loaded['tab_menu']) {
1417
            return $this->menu;
1418
        }
1419
1420
        $this->loaded['tab_menu'] = true;
1421
1422
        $menu = $this->menuFactory->createItem('root');
1423
        $menu->setChildrenAttribute('class', 'nav navbar-nav');
1424
        $menu->setExtra('translation_domain', $this->translationDomain);
1425
1426
        // Prevents BC break with KnpMenuBundle v1.x
1427
        if (method_exists($menu, 'setCurrentUri')) {
1428
            $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...
1429
        }
1430
1431
        $this->configureTabMenu($menu, $action, $childAdmin);
1432
1433
        foreach ($this->getExtensions() as $extension) {
1434
            $extension->configureTabMenu($this, $menu, $action, $childAdmin);
1435
        }
1436
1437
        $this->menu = $menu;
1438
1439
        return $this->menu;
1440
    }
1441
1442
    public function buildSideMenu($action, AdminInterface $childAdmin = null)
1443
    {
1444
        return $this->buildTabMenu($action, $childAdmin);
1445
    }
1446
1447
    /**
1448
     * @param string $action
1449
     *
1450
     * @return ItemInterface
1451
     */
1452
    public function getSideMenu($action, AdminInterface $childAdmin = null)
1453
    {
1454
        if ($this->isChild()) {
1455
            return $this->getParent()->getSideMenu($action, $this);
1456
        }
1457
1458
        $this->buildSideMenu($action, $childAdmin);
1459
1460
        return $this->menu;
1461
    }
1462
1463
    /**
1464
     * Returns the root code.
1465
     *
1466
     * @return string the root code
1467
     */
1468
    public function getRootCode()
1469
    {
1470
        return $this->getRoot()->getCode();
1471
    }
1472
1473
    /**
1474
     * Returns the master admin.
1475
     *
1476
     * @return AbstractAdmin the root admin class
1477
     */
1478
    public function getRoot()
1479
    {
1480
        $parentFieldDescription = $this->getParentFieldDescription();
1481
1482
        if (!$parentFieldDescription) {
1483
            return $this;
1484
        }
1485
1486
        return $parentFieldDescription->getAdmin()->getRoot();
1487
    }
1488
1489
    public function setBaseControllerName($baseControllerName)
1490
    {
1491
        $this->baseControllerName = $baseControllerName;
1492
    }
1493
1494
    public function getBaseControllerName()
1495
    {
1496
        return $this->baseControllerName;
1497
    }
1498
1499
    /**
1500
     * @param string $label
1501
     */
1502
    public function setLabel($label)
1503
    {
1504
        $this->label = $label;
1505
    }
1506
1507
    public function getLabel()
1508
    {
1509
        return $this->label;
1510
    }
1511
1512
    /**
1513
     * @param bool $persist
1514
     *
1515
     * NEXT_MAJOR: remove this method
1516
     *
1517
     * @deprecated since sonata-project/admin-bundle 3.34, to be removed in 4.0.
1518
     */
1519
    public function setPersistFilters($persist)
1520
    {
1521
        @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...
1522
            'The '.__METHOD__.' method is deprecated since version 3.34 and will be removed in 4.0.',
1523
            E_USER_DEPRECATED
1524
        );
1525
1526
        $this->persistFilters = $persist;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$persistFilters has been deprecated with message: since sonata-project/admin-bundle 3.34, to be removed in 4.0.

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

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

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

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

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

Loading history...
1534
    }
1535
1536
    /**
1537
     * @param int $maxPerPage
1538
     */
1539
    public function setMaxPerPage($maxPerPage)
1540
    {
1541
        $this->maxPerPage = $maxPerPage;
1542
    }
1543
1544
    /**
1545
     * @return int
1546
     */
1547
    public function getMaxPerPage()
1548
    {
1549
        return $this->maxPerPage;
1550
    }
1551
1552
    /**
1553
     * @param int $maxPageLinks
1554
     */
1555
    public function setMaxPageLinks($maxPageLinks)
1556
    {
1557
        $this->maxPageLinks = $maxPageLinks;
1558
    }
1559
1560
    /**
1561
     * @return int
1562
     */
1563
    public function getMaxPageLinks()
1564
    {
1565
        return $this->maxPageLinks;
1566
    }
1567
1568
    public function getFormGroups()
1569
    {
1570
        return $this->formGroups;
1571
    }
1572
1573
    public function setFormGroups(array $formGroups)
1574
    {
1575
        $this->formGroups = $formGroups;
1576
    }
1577
1578
    public function removeFieldFromFormGroup($key)
1579
    {
1580
        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...
1581
            unset($this->formGroups[$name]['fields'][$key]);
1582
1583
            if (empty($this->formGroups[$name]['fields'])) {
1584
                unset($this->formGroups[$name]);
1585
            }
1586
        }
1587
    }
1588
1589
    /**
1590
     * @param array $group
1591
     */
1592
    public function reorderFormGroup($group, array $keys)
1593
    {
1594
        $formGroups = $this->getFormGroups();
1595
        $formGroups[$group]['fields'] = array_merge(array_flip($keys), $formGroups[$group]['fields']);
1596
        $this->setFormGroups($formGroups);
0 ignored issues
show
Bug introduced by
It seems like $formGroups defined by $this->getFormGroups() on line 1594 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...
1597
    }
1598
1599
    public function getFormTabs()
1600
    {
1601
        return $this->formTabs;
1602
    }
1603
1604
    public function setFormTabs(array $formTabs)
1605
    {
1606
        $this->formTabs = $formTabs;
1607
    }
1608
1609
    public function getShowTabs()
1610
    {
1611
        return $this->showTabs;
1612
    }
1613
1614
    public function setShowTabs(array $showTabs)
1615
    {
1616
        $this->showTabs = $showTabs;
1617
    }
1618
1619
    public function getShowGroups()
1620
    {
1621
        return $this->showGroups;
1622
    }
1623
1624
    public function setShowGroups(array $showGroups)
1625
    {
1626
        $this->showGroups = $showGroups;
1627
    }
1628
1629
    public function reorderShowGroup($group, array $keys)
1630
    {
1631
        $showGroups = $this->getShowGroups();
1632
        $showGroups[$group]['fields'] = array_merge(array_flip($keys), $showGroups[$group]['fields']);
1633
        $this->setShowGroups($showGroups);
0 ignored issues
show
Bug introduced by
It seems like $showGroups defined by $this->getShowGroups() on line 1631 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...
1634
    }
1635
1636
    public function setParentFieldDescription(FieldDescriptionInterface $parentFieldDescription)
1637
    {
1638
        $this->parentFieldDescription = $parentFieldDescription;
1639
    }
1640
1641
    public function getParentFieldDescription()
1642
    {
1643
        return $this->parentFieldDescription;
1644
    }
1645
1646
    public function hasParentFieldDescription()
1647
    {
1648
        return $this->parentFieldDescription instanceof FieldDescriptionInterface;
1649
    }
1650
1651
    public function setSubject($subject)
1652
    {
1653
        if (\is_object($subject) && !is_a($subject, $this->getClass(), true)) {
1654
            $message = <<<'EOT'
1655
You are trying to set entity an instance of "%s",
1656
which is not the one registered with this admin class ("%s").
1657
This is deprecated since 3.5 and will no longer be supported in 4.0.
1658
EOT;
1659
1660
            @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...
1661
                sprintf($message, \get_class($subject), $this->getClass()),
1662
                E_USER_DEPRECATED
1663
            ); // NEXT_MAJOR : throw an exception instead
1664
        }
1665
1666
        $this->subject = $subject;
1667
    }
1668
1669
    public function getSubject()
1670
    {
1671
        if (null === $this->subject && $this->request && !$this->hasParentFieldDescription()) {
1672
            $id = $this->request->get($this->getIdParameter());
1673
1674
            if (null !== $id) {
1675
                $this->subject = $this->getObject($id);
1676
            }
1677
        }
1678
1679
        return $this->subject;
1680
    }
1681
1682
    public function hasSubject()
1683
    {
1684
        return (bool) $this->getSubject();
1685
    }
1686
1687
    public function getFormFieldDescriptions()
1688
    {
1689
        $this->buildForm();
1690
1691
        return $this->formFieldDescriptions;
1692
    }
1693
1694
    public function getFormFieldDescription($name)
1695
    {
1696
        return $this->hasFormFieldDescription($name) ? $this->formFieldDescriptions[$name] : null;
1697
    }
1698
1699
    /**
1700
     * Returns true if the admin has a FieldDescription with the given $name.
1701
     *
1702
     * @param string $name
1703
     *
1704
     * @return bool
1705
     */
1706
    public function hasFormFieldDescription($name)
1707
    {
1708
        return \array_key_exists($name, $this->formFieldDescriptions) ? true : false;
1709
    }
1710
1711
    public function addFormFieldDescription($name, FieldDescriptionInterface $fieldDescription)
1712
    {
1713
        $this->formFieldDescriptions[$name] = $fieldDescription;
1714
    }
1715
1716
    /**
1717
     * remove a FieldDescription.
1718
     *
1719
     * @param string $name
1720
     */
1721
    public function removeFormFieldDescription($name)
1722
    {
1723
        unset($this->formFieldDescriptions[$name]);
1724
    }
1725
1726
    /**
1727
     * build and return the collection of form FieldDescription.
1728
     *
1729
     * @return array collection of form FieldDescription
1730
     */
1731
    public function getShowFieldDescriptions()
1732
    {
1733
        $this->buildShow();
1734
1735
        return $this->showFieldDescriptions;
1736
    }
1737
1738
    /**
1739
     * Returns the form FieldDescription with the given $name.
1740
     *
1741
     * @param string $name
1742
     *
1743
     * @return FieldDescriptionInterface
1744
     */
1745
    public function getShowFieldDescription($name)
1746
    {
1747
        $this->buildShow();
1748
1749
        return $this->hasShowFieldDescription($name) ? $this->showFieldDescriptions[$name] : null;
1750
    }
1751
1752
    public function hasShowFieldDescription($name)
1753
    {
1754
        return \array_key_exists($name, $this->showFieldDescriptions);
1755
    }
1756
1757
    public function addShowFieldDescription($name, FieldDescriptionInterface $fieldDescription)
1758
    {
1759
        $this->showFieldDescriptions[$name] = $fieldDescription;
1760
    }
1761
1762
    public function removeShowFieldDescription($name)
1763
    {
1764
        unset($this->showFieldDescriptions[$name]);
1765
    }
1766
1767
    public function getListFieldDescriptions()
1768
    {
1769
        $this->buildList();
1770
1771
        return $this->listFieldDescriptions;
1772
    }
1773
1774
    public function getListFieldDescription($name)
1775
    {
1776
        return $this->hasListFieldDescription($name) ? $this->listFieldDescriptions[$name] : null;
1777
    }
1778
1779
    public function hasListFieldDescription($name)
1780
    {
1781
        $this->buildList();
1782
1783
        return \array_key_exists($name, $this->listFieldDescriptions) ? true : false;
1784
    }
1785
1786
    public function addListFieldDescription($name, FieldDescriptionInterface $fieldDescription)
1787
    {
1788
        $this->listFieldDescriptions[$name] = $fieldDescription;
1789
    }
1790
1791
    public function removeListFieldDescription($name)
1792
    {
1793
        unset($this->listFieldDescriptions[$name]);
1794
    }
1795
1796
    public function getFilterFieldDescription($name)
1797
    {
1798
        return $this->hasFilterFieldDescription($name) ? $this->filterFieldDescriptions[$name] : null;
1799
    }
1800
1801
    public function hasFilterFieldDescription($name)
1802
    {
1803
        return \array_key_exists($name, $this->filterFieldDescriptions) ? true : false;
1804
    }
1805
1806
    public function addFilterFieldDescription($name, FieldDescriptionInterface $fieldDescription)
1807
    {
1808
        $this->filterFieldDescriptions[$name] = $fieldDescription;
1809
    }
1810
1811
    public function removeFilterFieldDescription($name)
1812
    {
1813
        unset($this->filterFieldDescriptions[$name]);
1814
    }
1815
1816
    public function getFilterFieldDescriptions()
1817
    {
1818
        $this->buildDatagrid();
1819
1820
        return $this->filterFieldDescriptions;
1821
    }
1822
1823
    public function addChild(AdminInterface $child)
1824
    {
1825
        for ($parentAdmin = $this; null !== $parentAdmin; $parentAdmin = $parentAdmin->getParent()) {
1826
            if ($parentAdmin->getCode() !== $child->getCode()) {
1827
                continue;
1828
            }
1829
1830
            throw new \RuntimeException(sprintf(
1831
                'Circular reference detected! The child admin `%s` is already in the parent tree of the `%s` admin.',
1832
                $child->getCode(),
1833
                $this->getCode()
1834
            ));
1835
        }
1836
1837
        $this->children[$child->getCode()] = $child;
1838
1839
        $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...
1840
1841
        // NEXT_MAJOR: remove $args and add $field parameter to this function on next Major
1842
1843
        $args = \func_get_args();
1844
1845
        if (isset($args[1])) {
1846
            $child->addParentAssociationMapping($this->getCode(), $args[1]);
1847
        } else {
1848
            @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...
1849
                'Calling "addChild" without second argument is deprecated since'
1850
                .' sonata-project/admin-bundle 3.35 and will not be allowed in 4.0.',
1851
                E_USER_DEPRECATED
1852
            );
1853
        }
1854
    }
1855
1856
    public function hasChild($code)
1857
    {
1858
        return isset($this->children[$code]);
1859
    }
1860
1861
    public function getChildren()
1862
    {
1863
        return $this->children;
1864
    }
1865
1866
    public function getChild($code)
1867
    {
1868
        return $this->hasChild($code) ? $this->children[$code] : null;
1869
    }
1870
1871
    public function setParent(AdminInterface $parent)
1872
    {
1873
        $this->parent = $parent;
1874
    }
1875
1876
    public function getParent()
1877
    {
1878
        return $this->parent;
1879
    }
1880
1881
    final public function getRootAncestor()
1882
    {
1883
        $parent = $this;
1884
1885
        while ($parent->isChild()) {
1886
            $parent = $parent->getParent();
1887
        }
1888
1889
        return $parent;
1890
    }
1891
1892
    final public function getChildDepth()
1893
    {
1894
        $parent = $this;
1895
        $depth = 0;
1896
1897
        while ($parent->isChild()) {
1898
            $parent = $parent->getParent();
1899
            ++$depth;
1900
        }
1901
1902
        return $depth;
1903
    }
1904
1905
    final public function getCurrentLeafChildAdmin()
1906
    {
1907
        $child = $this->getCurrentChildAdmin();
1908
1909
        if (null === $child) {
1910
            return null;
1911
        }
1912
1913
        for ($c = $child; null !== $c; $c = $child->getCurrentChildAdmin()) {
1914
            $child = $c;
1915
        }
1916
1917
        return $child;
1918
    }
1919
1920
    public function isChild()
1921
    {
1922
        return $this->parent instanceof AdminInterface;
1923
    }
1924
1925
    /**
1926
     * Returns true if the admin has children, false otherwise.
1927
     *
1928
     * @return bool if the admin has children
1929
     */
1930
    public function hasChildren()
1931
    {
1932
        return \count($this->children) > 0;
1933
    }
1934
1935
    public function setUniqid($uniqid)
1936
    {
1937
        $this->uniqid = $uniqid;
1938
    }
1939
1940
    public function getUniqid()
1941
    {
1942
        if (!$this->uniqid) {
1943
            $this->uniqid = 's'.uniqid();
1944
        }
1945
1946
        return $this->uniqid;
1947
    }
1948
1949
    /**
1950
     * Returns the classname label.
1951
     *
1952
     * @return string the classname label
1953
     */
1954
    public function getClassnameLabel()
1955
    {
1956
        return $this->classnameLabel;
1957
    }
1958
1959
    public function getPersistentParameters()
1960
    {
1961
        $parameters = [];
1962
1963
        foreach ($this->getExtensions() as $extension) {
1964
            $params = $extension->getPersistentParameters($this);
1965
1966
            if (!\is_array($params)) {
1967
                throw new \RuntimeException(sprintf('The %s::getPersistentParameters must return an array', \get_class($extension)));
1968
            }
1969
1970
            $parameters = array_merge($parameters, $params);
1971
        }
1972
1973
        return $parameters;
1974
    }
1975
1976
    /**
1977
     * @param string $name
1978
     *
1979
     * @return mixed|null
1980
     */
1981
    public function getPersistentParameter($name)
1982
    {
1983
        $parameters = $this->getPersistentParameters();
1984
1985
        return $parameters[$name] ?? null;
1986
    }
1987
1988
    public function getBreadcrumbs($action)
1989
    {
1990
        @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...
1991
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
1992
            ' Use Sonata\AdminBundle\Admin\BreadcrumbsBuilder::getBreadcrumbs instead.',
1993
            E_USER_DEPRECATED
1994
        );
1995
1996
        return $this->getBreadcrumbsBuilder()->getBreadcrumbs($this, $action);
1997
    }
1998
1999
    /**
2000
     * Generates the breadcrumbs array.
2001
     *
2002
     * Note: the method will be called by the top admin instance (parent => child)
2003
     *
2004
     * @param string $action
2005
     *
2006
     * @return array
2007
     */
2008
    public function buildBreadcrumbs($action, MenuItemInterface $menu = null)
2009
    {
2010
        @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...
2011
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.',
2012
            E_USER_DEPRECATED
2013
        );
2014
2015
        if (isset($this->breadcrumbs[$action])) {
2016
            return $this->breadcrumbs[$action];
2017
        }
2018
2019
        return $this->breadcrumbs[$action] = $this->getBreadcrumbsBuilder()
2020
            ->buildBreadcrumbs($this, $action, $menu);
2021
    }
2022
2023
    /**
2024
     * NEXT_MAJOR : remove this method.
2025
     *
2026
     * @return BreadcrumbsBuilderInterface
2027
     */
2028
    final public function getBreadcrumbsBuilder()
2029
    {
2030
        @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...
2031
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
2032
            ' Use the sonata.admin.breadcrumbs_builder service instead.',
2033
            E_USER_DEPRECATED
2034
        );
2035
        if (null === $this->breadcrumbsBuilder) {
2036
            $this->breadcrumbsBuilder = new BreadcrumbsBuilder(
2037
                $this->getConfigurationPool()->getContainer()->getParameter('sonata.admin.configuration.breadcrumbs')
2038
            );
2039
        }
2040
2041
        return $this->breadcrumbsBuilder;
2042
    }
2043
2044
    /**
2045
     * NEXT_MAJOR : remove this method.
2046
     *
2047
     * @return AbstractAdmin
2048
     */
2049
    final public function setBreadcrumbsBuilder(BreadcrumbsBuilderInterface $value)
2050
    {
2051
        @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...
2052
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
2053
            ' Use the sonata.admin.breadcrumbs_builder service instead.',
2054
            E_USER_DEPRECATED
2055
        );
2056
        $this->breadcrumbsBuilder = $value;
2057
2058
        return $this;
2059
    }
2060
2061
    public function setCurrentChild($currentChild)
2062
    {
2063
        $this->currentChild = $currentChild;
2064
    }
2065
2066
    /**
2067
     * NEXT_MAJOR: Remove this method.
2068
     *
2069
     * @deprecated since sonata-project/admin-bundle 3.x, to be removed in 4.0
2070
     */
2071
    public function getCurrentChild()
2072
    {
2073
        @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...
2074
            sprintf(
2075
                'The %s() method is deprecated since version 3.x and will be removed in 4.0. Use %s::isCurrentChild() instead.',
2076
                __METHOD__,
2077
                __CLASS__
2078
            ),
2079
            E_USER_DEPRECATED
2080
        );
2081
2082
        return $this->currentChild;
2083
    }
2084
2085
    public function isCurrentChild(): bool
2086
    {
2087
        return $this->currentChild;
2088
    }
2089
2090
    /**
2091
     * Returns the current child admin instance.
2092
     *
2093
     * @return AdminInterface|null the current child admin instance
2094
     */
2095
    public function getCurrentChildAdmin()
2096
    {
2097
        foreach ($this->children as $children) {
2098
            if ($children->isCurrentChild()) {
2099
                return $children;
2100
            }
2101
        }
2102
2103
        return null;
2104
    }
2105
2106
    public function trans($id, array $parameters = [], $domain = null, $locale = null)
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
        $domain = $domain ?: $this->getTranslationDomain();
2114
2115
        return $this->translator->trans($id, $parameters, $domain, $locale);
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$translator has been deprecated with message: since sonata-project/admin-bundle 3.9, to be removed with 4.0

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

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

Loading history...
2116
    }
2117
2118
    /**
2119
     * Translate a message id.
2120
     *
2121
     * NEXT_MAJOR: remove this method
2122
     *
2123
     * @param string      $id
2124
     * @param int         $count
2125
     * @param string|null $domain
2126
     * @param string|null $locale
2127
     *
2128
     * @return string the translated string
2129
     *
2130
     * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
2131
     */
2132
    public function transChoice($id, $count, array $parameters = [], $domain = null, $locale = null)
2133
    {
2134
        @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...
2135
            'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2136
            E_USER_DEPRECATED
2137
        );
2138
2139
        $domain = $domain ?: $this->getTranslationDomain();
2140
2141
        return $this->translator->transChoice($id, $count, $parameters, $domain, $locale);
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$translator has been deprecated with message: since sonata-project/admin-bundle 3.9, to be removed with 4.0

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

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

Loading history...
2142
    }
2143
2144
    public function setTranslationDomain($translationDomain)
2145
    {
2146
        $this->translationDomain = $translationDomain;
2147
    }
2148
2149
    public function getTranslationDomain()
2150
    {
2151
        return $this->translationDomain;
2152
    }
2153
2154
    /**
2155
     * {@inheritdoc}
2156
     *
2157
     * NEXT_MAJOR: remove this method
2158
     *
2159
     * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
2160
     */
2161
    public function setTranslator(TranslatorInterface $translator)
2162
    {
2163
        $args = \func_get_args();
2164
        if (isset($args[1]) && $args[1]) {
2165
            @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...
2166
                'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2167
                E_USER_DEPRECATED
2168
            );
2169
        }
2170
2171
        $this->translator = $translator;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$translator has been deprecated with message: since sonata-project/admin-bundle 3.9, to be removed with 4.0

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

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

Loading history...
2172
    }
2173
2174
    /**
2175
     * {@inheritdoc}
2176
     *
2177
     * NEXT_MAJOR: remove this method
2178
     *
2179
     * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
2180
     */
2181
    public function getTranslator()
2182
    {
2183
        @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...
2184
            'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2185
            E_USER_DEPRECATED
2186
        );
2187
2188
        return $this->translator;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$translator has been deprecated with message: since sonata-project/admin-bundle 3.9, to be removed with 4.0

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

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

Loading history...
2189
    }
2190
2191
    public function getTranslationLabel($label, $context = '', $type = '')
2192
    {
2193
        return $this->getLabelTranslatorStrategy()->getLabel($label, $context, $type);
2194
    }
2195
2196
    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...
2197
    {
2198
        $this->request = $request;
2199
2200
        foreach ($this->getChildren() as $children) {
2201
            $children->setRequest($request);
2202
        }
2203
    }
2204
2205
    public function getRequest()
2206
    {
2207
        if (!$this->request) {
2208
            throw new \RuntimeException('The Request object has not been set');
2209
        }
2210
2211
        return $this->request;
2212
    }
2213
2214
    public function hasRequest()
2215
    {
2216
        return null !== $this->request;
2217
    }
2218
2219
    public function setFormContractor(FormContractorInterface $formBuilder)
2220
    {
2221
        $this->formContractor = $formBuilder;
2222
    }
2223
2224
    /**
2225
     * @return FormContractorInterface
2226
     */
2227
    public function getFormContractor()
2228
    {
2229
        return $this->formContractor;
2230
    }
2231
2232
    public function setDatagridBuilder(DatagridBuilderInterface $datagridBuilder)
2233
    {
2234
        $this->datagridBuilder = $datagridBuilder;
2235
    }
2236
2237
    public function getDatagridBuilder()
2238
    {
2239
        return $this->datagridBuilder;
2240
    }
2241
2242
    public function setListBuilder(ListBuilderInterface $listBuilder)
2243
    {
2244
        $this->listBuilder = $listBuilder;
2245
    }
2246
2247
    public function getListBuilder()
2248
    {
2249
        return $this->listBuilder;
2250
    }
2251
2252
    public function setShowBuilder(ShowBuilderInterface $showBuilder)
2253
    {
2254
        $this->showBuilder = $showBuilder;
2255
    }
2256
2257
    /**
2258
     * @return ShowBuilderInterface
2259
     */
2260
    public function getShowBuilder()
2261
    {
2262
        return $this->showBuilder;
2263
    }
2264
2265
    public function setConfigurationPool(Pool $configurationPool)
2266
    {
2267
        $this->configurationPool = $configurationPool;
2268
    }
2269
2270
    /**
2271
     * @return Pool
2272
     */
2273
    public function getConfigurationPool()
2274
    {
2275
        return $this->configurationPool;
2276
    }
2277
2278
    public function setRouteGenerator(RouteGeneratorInterface $routeGenerator)
2279
    {
2280
        $this->routeGenerator = $routeGenerator;
2281
    }
2282
2283
    /**
2284
     * @return RouteGeneratorInterface
2285
     */
2286
    public function getRouteGenerator()
2287
    {
2288
        return $this->routeGenerator;
2289
    }
2290
2291
    public function getCode()
2292
    {
2293
        return $this->code;
2294
    }
2295
2296
    /**
2297
     * NEXT_MAJOR: Remove this function.
2298
     *
2299
     * @deprecated This method is deprecated since sonata-project/admin-bundle 3.24 and will be removed in 4.0
2300
     *
2301
     * @param string $baseCodeRoute
2302
     */
2303
    public function setBaseCodeRoute($baseCodeRoute)
2304
    {
2305
        @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...
2306
            'The '.__METHOD__.' is deprecated since 3.24 and will be removed in 4.0.',
2307
            E_USER_DEPRECATED
2308
        );
2309
2310
        $this->baseCodeRoute = $baseCodeRoute;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...ctAdmin::$baseCodeRoute has been deprecated with message: This attribute is deprecated since sonata-project/admin-bundle 3.24 and will be removed in 4.0

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

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

Loading history...
2311
    }
2312
2313
    public function getBaseCodeRoute()
2314
    {
2315
        // NEXT_MAJOR: Uncomment the following lines.
2316
        // if ($this->isChild()) {
2317
        //     return $this->getParent()->getBaseCodeRoute().'|'.$this->getCode();
2318
        // }
2319
        //
2320
        // return $this->getCode();
2321
2322
        // NEXT_MAJOR: Remove all the code below.
2323
        if ($this->isChild()) {
2324
            $parentCode = $this->getParent()->getCode();
2325
2326
            if ($this->getParent()->isChild()) {
2327
                $parentCode = $this->getParent()->getBaseCodeRoute();
2328
            }
2329
2330
            return $parentCode.'|'.$this->getCode();
2331
        }
2332
2333
        return $this->baseCodeRoute;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...ctAdmin::$baseCodeRoute has been deprecated with message: This attribute is deprecated since sonata-project/admin-bundle 3.24 and will be removed in 4.0

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

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

Loading history...
2334
    }
2335
2336
    public function getModelManager()
2337
    {
2338
        return $this->modelManager;
2339
    }
2340
2341
    public function setModelManager(ModelManagerInterface $modelManager)
2342
    {
2343
        $this->modelManager = $modelManager;
2344
    }
2345
2346
    public function getManagerType()
2347
    {
2348
        return $this->managerType;
2349
    }
2350
2351
    /**
2352
     * @param string $type
2353
     */
2354
    public function setManagerType($type)
2355
    {
2356
        $this->managerType = $type;
2357
    }
2358
2359
    public function getObjectIdentifier()
2360
    {
2361
        return $this->getCode();
2362
    }
2363
2364
    /**
2365
     * Set the roles and permissions per role.
2366
     */
2367
    public function setSecurityInformation(array $information)
2368
    {
2369
        $this->securityInformation = $information;
2370
    }
2371
2372
    public function getSecurityInformation()
2373
    {
2374
        return $this->securityInformation;
2375
    }
2376
2377
    /**
2378
     * Return the list of permissions the user should have in order to display the admin.
2379
     *
2380
     * @param string $context
2381
     *
2382
     * @return array
2383
     */
2384
    public function getPermissionsShow($context)
2385
    {
2386
        switch ($context) {
2387
            case self::CONTEXT_DASHBOARD:
2388
            case self::CONTEXT_MENU:
2389
            default:
2390
                return ['LIST'];
2391
        }
2392
    }
2393
2394
    public function showIn($context)
2395
    {
2396
        switch ($context) {
2397
            case self::CONTEXT_DASHBOARD:
2398
            case self::CONTEXT_MENU:
2399
            default:
2400
                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...
2401
        }
2402
    }
2403
2404
    public function createObjectSecurity($object)
2405
    {
2406
        $this->getSecurityHandler()->createObjectSecurity($this, $object);
2407
    }
2408
2409
    public function setSecurityHandler(SecurityHandlerInterface $securityHandler)
2410
    {
2411
        $this->securityHandler = $securityHandler;
2412
    }
2413
2414
    public function getSecurityHandler()
2415
    {
2416
        return $this->securityHandler;
2417
    }
2418
2419
    public function isGranted($name, $object = null)
2420
    {
2421
        $key = md5(json_encode($name).($object ? '/'.spl_object_hash($object) : ''));
2422
2423
        if (!\array_key_exists($key, $this->cacheIsGranted)) {
2424
            $this->cacheIsGranted[$key] = $this->securityHandler->isGranted($this, $name, $object ?: $this);
2425
        }
2426
2427
        return $this->cacheIsGranted[$key];
2428
    }
2429
2430
    public function getUrlsafeIdentifier($entity)
2431
    {
2432
        return $this->getModelManager()->getUrlsafeIdentifier($entity);
2433
    }
2434
2435
    public function getNormalizedIdentifier($entity)
2436
    {
2437
        return $this->getModelManager()->getNormalizedIdentifier($entity);
2438
    }
2439
2440
    public function id($entity)
2441
    {
2442
        return $this->getNormalizedIdentifier($entity);
2443
    }
2444
2445
    public function setValidator($validator)
2446
    {
2447
        // NEXT_MAJOR: Move ValidatorInterface check to method signature
2448
        if (!$validator instanceof ValidatorInterface) {
2449
            throw new \InvalidArgumentException(
2450
                'Argument 1 must be an instance of Symfony\Component\Validator\Validator\ValidatorInterface'
2451
            );
2452
        }
2453
2454
        $this->validator = $validator;
2455
    }
2456
2457
    public function getValidator()
2458
    {
2459
        return $this->validator;
2460
    }
2461
2462
    public function getShow()
2463
    {
2464
        $this->buildShow();
2465
2466
        return $this->show;
2467
    }
2468
2469
    public function setFormTheme(array $formTheme)
2470
    {
2471
        $this->formTheme = $formTheme;
2472
    }
2473
2474
    public function getFormTheme()
2475
    {
2476
        return $this->formTheme;
2477
    }
2478
2479
    public function setFilterTheme(array $filterTheme)
2480
    {
2481
        $this->filterTheme = $filterTheme;
2482
    }
2483
2484
    public function getFilterTheme()
2485
    {
2486
        return $this->filterTheme;
2487
    }
2488
2489
    public function addExtension(AdminExtensionInterface $extension)
2490
    {
2491
        $this->extensions[] = $extension;
2492
    }
2493
2494
    public function getExtensions()
2495
    {
2496
        return $this->extensions;
2497
    }
2498
2499
    public function setMenuFactory(MenuFactoryInterface $menuFactory)
2500
    {
2501
        $this->menuFactory = $menuFactory;
2502
    }
2503
2504
    public function getMenuFactory()
2505
    {
2506
        return $this->menuFactory;
2507
    }
2508
2509
    public function setRouteBuilder(RouteBuilderInterface $routeBuilder)
2510
    {
2511
        $this->routeBuilder = $routeBuilder;
2512
    }
2513
2514
    public function getRouteBuilder()
2515
    {
2516
        return $this->routeBuilder;
2517
    }
2518
2519
    public function toString($object)
2520
    {
2521
        if (!\is_object($object)) {
2522
            return '';
2523
        }
2524
2525
        if (method_exists($object, '__toString') && null !== $object->__toString()) {
2526
            return (string) $object;
2527
        }
2528
2529
        return sprintf('%s:%s', ClassUtils::getClass($object), spl_object_hash($object));
2530
    }
2531
2532
    public function setLabelTranslatorStrategy(LabelTranslatorStrategyInterface $labelTranslatorStrategy)
2533
    {
2534
        $this->labelTranslatorStrategy = $labelTranslatorStrategy;
2535
    }
2536
2537
    public function getLabelTranslatorStrategy()
2538
    {
2539
        return $this->labelTranslatorStrategy;
2540
    }
2541
2542
    public function supportsPreviewMode()
2543
    {
2544
        return $this->supportsPreviewMode;
2545
    }
2546
2547
    /**
2548
     * Set custom per page options.
2549
     */
2550
    public function setPerPageOptions(array $options)
2551
    {
2552
        $this->perPageOptions = $options;
2553
    }
2554
2555
    /**
2556
     * Returns predefined per page options.
2557
     *
2558
     * @return array
2559
     */
2560
    public function getPerPageOptions()
2561
    {
2562
        return $this->perPageOptions;
2563
    }
2564
2565
    /**
2566
     * Set pager type.
2567
     *
2568
     * @param string $pagerType
2569
     */
2570
    public function setPagerType($pagerType)
2571
    {
2572
        $this->pagerType = $pagerType;
2573
    }
2574
2575
    /**
2576
     * Get pager type.
2577
     *
2578
     * @return string
2579
     */
2580
    public function getPagerType()
2581
    {
2582
        return $this->pagerType;
2583
    }
2584
2585
    /**
2586
     * Returns true if the per page value is allowed, false otherwise.
2587
     *
2588
     * @param int $perPage
2589
     *
2590
     * @return bool
2591
     */
2592
    public function determinedPerPageValue($perPage)
2593
    {
2594
        return \in_array($perPage, $this->perPageOptions, true);
2595
    }
2596
2597
    public function isAclEnabled()
2598
    {
2599
        return $this->getSecurityHandler() instanceof AclSecurityHandlerInterface;
2600
    }
2601
2602
    public function getObjectMetadata($object)
2603
    {
2604
        return new Metadata($this->toString($object));
2605
    }
2606
2607
    public function getListModes()
2608
    {
2609
        return $this->listModes;
2610
    }
2611
2612
    public function setListMode($mode)
2613
    {
2614
        if (!$this->hasRequest()) {
2615
            throw new \RuntimeException(sprintf('No request attached to the current admin: %s', $this->getCode()));
2616
        }
2617
2618
        $this->getRequest()->getSession()->set(sprintf('%s.list_mode', $this->getCode()), $mode);
2619
    }
2620
2621
    public function getListMode()
2622
    {
2623
        if (!$this->hasRequest()) {
2624
            return 'list';
2625
        }
2626
2627
        return $this->getRequest()->getSession()->get(sprintf('%s.list_mode', $this->getCode()), 'list');
2628
    }
2629
2630
    public function getAccessMapping()
2631
    {
2632
        return $this->accessMapping;
2633
    }
2634
2635
    public function checkAccess($action, $object = null)
2636
    {
2637
        $access = $this->getAccess();
2638
2639
        if (!\array_key_exists($action, $access)) {
2640
            throw new \InvalidArgumentException(sprintf(
2641
                'Action "%s" could not be found in access mapping.'
2642
                .' Please make sure your action is defined into your admin class accessMapping property.',
2643
                $action
2644
            ));
2645
        }
2646
2647
        if (!\is_array($access[$action])) {
2648
            $access[$action] = [$access[$action]];
2649
        }
2650
2651
        foreach ($access[$action] as $role) {
2652
            if (false === $this->isGranted($role, $object)) {
2653
                throw new AccessDeniedException(sprintf('Access Denied to the action %s and role %s', $action, $role));
2654
            }
2655
        }
2656
    }
2657
2658
    /**
2659
     * Hook to handle access authorization, without throw Exception.
2660
     *
2661
     * @param string $action
2662
     * @param object $object
2663
     *
2664
     * @return bool
2665
     */
2666
    public function hasAccess($action, $object = null)
2667
    {
2668
        $access = $this->getAccess();
2669
2670
        if (!\array_key_exists($action, $access)) {
2671
            return false;
2672
        }
2673
2674
        if (!\is_array($access[$action])) {
2675
            $access[$action] = [$access[$action]];
2676
        }
2677
2678
        foreach ($access[$action] as $role) {
2679
            if (false === $this->isGranted($role, $object)) {
2680
                return false;
2681
            }
2682
        }
2683
2684
        return true;
2685
    }
2686
2687
    /**
2688
     * @param string      $action
2689
     * @param object|null $object
2690
     *
2691
     * @return array
2692
     */
2693
    public function configureActionButtons($action, $object = null)
2694
    {
2695
        $list = [];
2696
2697
        if (\in_array($action, ['tree', 'show', 'edit', 'delete', 'list', 'batch'], true)
2698
            && $this->hasAccess('create')
2699
            && $this->hasRoute('create')
2700
        ) {
2701
            $list['create'] = [
2702
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2703
                'template' => $this->getTemplate('button_create'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead

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

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

Loading history...
2704
//                'template' => $this->getTemplateRegistry()->getTemplate('button_create'),
2705
            ];
2706
        }
2707
2708
        if (\in_array($action, ['show', 'delete', 'acl', 'history'], true)
2709
            && $this->canAccessObject('edit', $object)
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 2693 can also be of type null; however, Sonata\AdminBundle\Admin...dmin::canAccessObject() does only seem to accept object, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

Loading history...
2710
            && $this->hasRoute('edit')
2711
        ) {
2712
            $list['edit'] = [
2713
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2714
                'template' => $this->getTemplate('button_edit'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead

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

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

Loading history...
2715
                //'template' => $this->getTemplateRegistry()->getTemplate('button_edit'),
2716
            ];
2717
        }
2718
2719
        if (\in_array($action, ['show', 'edit', 'acl'], true)
2720
            && $this->canAccessObject('history', $object)
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 2693 can also be of type null; however, Sonata\AdminBundle\Admin...dmin::canAccessObject() does only seem to accept object, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

Loading history...
2721
            && $this->hasRoute('history')
2722
        ) {
2723
            $list['history'] = [
2724
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2725
                'template' => $this->getTemplate('button_history'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead

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

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

Loading history...
2726
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_history'),
2727
            ];
2728
        }
2729
2730
        if (\in_array($action, ['edit', 'history'], true)
2731
            && $this->isAclEnabled()
2732
            && $this->canAccessObject('acl', $object)
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 2693 can also be of type null; however, Sonata\AdminBundle\Admin...dmin::canAccessObject() does only seem to accept object, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

Loading history...
2733
            && $this->hasRoute('acl')
2734
        ) {
2735
            $list['acl'] = [
2736
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2737
                'template' => $this->getTemplate('button_acl'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead

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

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

Loading history...
2738
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_acl'),
2739
            ];
2740
        }
2741
2742
        if (\in_array($action, ['edit', 'history', 'acl'], true)
2743
            && $this->canAccessObject('show', $object)
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 2693 can also be of type null; however, Sonata\AdminBundle\Admin...dmin::canAccessObject() does only seem to accept object, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

Loading history...
2744
            && \count($this->getShow()) > 0
2745
            && $this->hasRoute('show')
2746
        ) {
2747
            $list['show'] = [
2748
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2749
                'template' => $this->getTemplate('button_show'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead

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

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

Loading history...
2750
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_show'),
2751
            ];
2752
        }
2753
2754
        if (\in_array($action, ['show', 'edit', 'delete', 'acl', 'batch'], true)
2755
            && $this->hasAccess('list')
2756
            && $this->hasRoute('list')
2757
        ) {
2758
            $list['list'] = [
2759
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2760
                'template' => $this->getTemplate('button_list'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead

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

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

Loading history...
2761
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_list'),
2762
            ];
2763
        }
2764
2765
        return $list;
2766
    }
2767
2768
    /**
2769
     * @param string $action
2770
     * @param object $object
2771
     *
2772
     * @return array
2773
     */
2774
    public function getActionButtons($action, $object = null)
2775
    {
2776
        $list = $this->configureActionButtons($action, $object);
2777
2778
        foreach ($this->getExtensions() as $extension) {
2779
            // NEXT_MAJOR: remove method check
2780
            if (method_exists($extension, 'configureActionButtons')) {
2781
                $list = $extension->configureActionButtons($this, $list, $action, $object);
2782
            }
2783
        }
2784
2785
        return $list;
2786
    }
2787
2788
    /**
2789
     * Get the list of actions that can be accessed directly from the dashboard.
2790
     *
2791
     * @return array
2792
     */
2793
    public function getDashboardActions()
2794
    {
2795
        $actions = [];
2796
2797
        if ($this->hasRoute('create') && $this->hasAccess('create')) {
2798
            $actions['create'] = [
2799
                'label' => 'link_add',
2800
                'translation_domain' => 'SonataAdminBundle',
2801
                // NEXT_MAJOR: Remove this line and use commented line below it instead
2802
                'template' => $this->getTemplate('action_create'),
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead

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

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

Loading history...
2803
                // 'template' => $this->getTemplateRegistry()->getTemplate('action_create'),
2804
                'url' => $this->generateUrl('create'),
2805
                'icon' => 'plus-circle',
2806
            ];
2807
        }
2808
2809
        if ($this->hasRoute('list') && $this->hasAccess('list')) {
2810
            $actions['list'] = [
2811
                'label' => 'link_list',
2812
                'translation_domain' => 'SonataAdminBundle',
2813
                'url' => $this->generateUrl('list'),
2814
                'icon' => 'list',
2815
            ];
2816
        }
2817
2818
        return $actions;
2819
    }
2820
2821
    /**
2822
     * Setting to true will enable mosaic button for the admin screen.
2823
     * Setting to false will hide mosaic button for the admin screen.
2824
     *
2825
     * @param bool $isShown
2826
     */
2827
    final public function showMosaicButton($isShown)
2828
    {
2829
        if ($isShown) {
2830
            $this->listModes['mosaic'] = ['class' => static::MOSAIC_ICON_CLASS];
2831
        } else {
2832
            unset($this->listModes['mosaic']);
2833
        }
2834
    }
2835
2836
    /**
2837
     * @param object $object
2838
     */
2839
    final public function getSearchResultLink($object)
2840
    {
2841
        foreach ($this->searchResultActions as $action) {
2842
            if ($this->hasRoute($action) && $this->hasAccess($action, $object)) {
2843
                return $this->generateObjectUrl($action, $object);
2844
            }
2845
        }
2846
2847
        return null;
2848
    }
2849
2850
    /**
2851
     * Checks if a filter type is set to a default value.
2852
     *
2853
     * @param string $name
2854
     *
2855
     * @return bool
2856
     */
2857
    final public function isDefaultFilter($name)
2858
    {
2859
        $filter = $this->getFilterParameters();
2860
        $default = $this->getDefaultFilterValues();
2861
2862
        if (!\array_key_exists($name, $filter) || !\array_key_exists($name, $default)) {
2863
            return false;
2864
        }
2865
2866
        return $filter[$name] === $default[$name];
2867
    }
2868
2869
    /**
2870
     * Check object existence and access, without throw Exception.
2871
     *
2872
     * @param string $action
2873
     * @param object $object
2874
     *
2875
     * @return bool
2876
     */
2877
    public function canAccessObject($action, $object)
2878
    {
2879
        return $object && $this->id($object) && $this->hasAccess($action, $object);
2880
    }
2881
2882
    protected function configureQuery(ProxyQueryInterface $query): ProxyQueryInterface
2883
    {
2884
        return $query;
2885
    }
2886
2887
    /**
2888
     * @return MutableTemplateRegistryInterface
2889
     */
2890
    final protected function getTemplateRegistry()
2891
    {
2892
        return $this->templateRegistry;
2893
    }
2894
2895
    /**
2896
     * Returns a list of default filters.
2897
     *
2898
     * @return array
2899
     */
2900
    final protected function getDefaultFilterValues()
2901
    {
2902
        $defaultFilterValues = [];
2903
2904
        $this->configureDefaultFilterValues($defaultFilterValues);
2905
2906
        foreach ($this->getExtensions() as $extension) {
2907
            // NEXT_MAJOR: remove method check
2908
            if (method_exists($extension, 'configureDefaultFilterValues')) {
2909
                $extension->configureDefaultFilterValues($this, $defaultFilterValues);
2910
            }
2911
        }
2912
2913
        return $defaultFilterValues;
2914
    }
2915
2916
    protected function configureFormFields(FormMapper $form)
2917
    {
2918
    }
2919
2920
    protected function configureListFields(ListMapper $list)
2921
    {
2922
    }
2923
2924
    protected function configureDatagridFilters(DatagridMapper $filter)
2925
    {
2926
    }
2927
2928
    protected function configureShowFields(ShowMapper $show)
2929
    {
2930
    }
2931
2932
    protected function configureRoutes(RouteCollection $collection)
2933
    {
2934
    }
2935
2936
    /**
2937
     * Allows you to customize batch actions.
2938
     *
2939
     * @param array $actions List of actions
2940
     *
2941
     * @return array
2942
     */
2943
    protected function configureBatchActions($actions)
2944
    {
2945
        return $actions;
2946
    }
2947
2948
    /**
2949
     * NEXT_MAJOR: remove this method.
2950
     *
2951
     * @deprecated Use configureTabMenu instead
2952
     */
2953
    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...
2954
    {
2955
    }
2956
2957
    /**
2958
     * Configures the tab menu in your admin.
2959
     *
2960
     * @param string $action
2961
     */
2962
    protected function configureTabMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null)
2963
    {
2964
        // Use configureSideMenu not to mess with previous overrides
2965
        // NEXT_MAJOR: remove this line
2966
        $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...
2967
    }
2968
2969
    /**
2970
     * build the view FieldDescription array.
2971
     */
2972
    protected function buildShow()
2973
    {
2974
        if ($this->show) {
2975
            return;
2976
        }
2977
2978
        $this->show = new FieldDescriptionCollection();
2979
        $mapper = new ShowMapper($this->showBuilder, $this->show, $this);
2980
2981
        $this->configureShowFields($mapper);
2982
2983
        foreach ($this->getExtensions() as $extension) {
2984
            $extension->configureShowFields($mapper);
2985
        }
2986
    }
2987
2988
    /**
2989
     * build the list FieldDescription array.
2990
     */
2991
    protected function buildList()
2992
    {
2993
        if ($this->list) {
2994
            return;
2995
        }
2996
2997
        $this->list = $this->getListBuilder()->getBaseList();
2998
2999
        $mapper = new ListMapper($this->getListBuilder(), $this->list, $this);
3000
3001
        if (\count($this->getBatchActions()) > 0 && $this->hasRequest() && !$this->getRequest()->isXmlHttpRequest()) {
3002
            $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance(
3003
                $this->getClass(),
3004
                'batch',
3005
                [
3006
                    'label' => 'batch',
3007
                    'code' => '_batch',
3008
                    'sortable' => false,
3009
                    'virtual_field' => true,
3010
                ]
3011
            );
3012
3013
            $fieldDescription->setAdmin($this);
3014
            // NEXT_MAJOR: Remove this line and use commented line below it instead
3015
            $fieldDescription->setTemplate($this->getTemplate('batch'));
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead

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

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

Loading history...
3016
            // $fieldDescription->setTemplate($this->getTemplateRegistry()->getTemplate('batch'));
3017
3018
            $mapper->add($fieldDescription, 'batch');
3019
        }
3020
3021
        $this->configureListFields($mapper);
3022
3023
        foreach ($this->getExtensions() as $extension) {
3024
            $extension->configureListFields($mapper);
3025
        }
3026
3027
        if ($this->hasRequest() && $this->getRequest()->isXmlHttpRequest()) {
3028
            $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance(
3029
                $this->getClass(),
3030
                'select',
3031
                [
3032
                    'label' => false,
3033
                    'code' => '_select',
3034
                    'sortable' => false,
3035
                    'virtual_field' => false,
3036
                ]
3037
            );
3038
3039
            $fieldDescription->setAdmin($this);
3040
            // NEXT_MAJOR: Remove this line and use commented line below it instead
3041
            $fieldDescription->setTemplate($this->getTemplate('select'));
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...actAdmin::getTemplate() has been deprecated with message: since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead

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

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

Loading history...
3042
            // $fieldDescription->setTemplate($this->getTemplateRegistry()->getTemplate('select'));
3043
3044
            $mapper->add($fieldDescription, 'select');
3045
        }
3046
    }
3047
3048
    /**
3049
     * Build the form FieldDescription collection.
3050
     */
3051
    protected function buildForm()
3052
    {
3053
        if ($this->form) {
3054
            return;
3055
        }
3056
3057
        // append parent object if any
3058
        // todo : clean the way the Admin class can retrieve set the object
3059
        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...
3060
            $parent = $this->getParent()->getObject($this->request->get($this->getParent()->getIdParameter()));
3061
3062
            $propertyAccessor = $this->getConfigurationPool()->getPropertyAccessor();
3063
            $propertyPath = new PropertyPath($this->getParentAssociationMapping());
3064
3065
            $object = $this->getSubject();
3066
3067
            $value = $propertyAccessor->getValue($object, $propertyPath);
0 ignored issues
show
Bug introduced by
It seems like $object defined by $this->getSubject() on line 3065 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...
3068
3069
            if (\is_array($value) || $value instanceof \ArrayAccess) {
3070
                $value[] = $parent;
3071
                $propertyAccessor->setValue($object, $propertyPath, $value);
0 ignored issues
show
Bug introduced by
It seems like $object defined by $this->getSubject() on line 3065 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...
3072
            } else {
3073
                $propertyAccessor->setValue($object, $propertyPath, $parent);
0 ignored issues
show
Bug introduced by
It seems like $object defined by $this->getSubject() on line 3065 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...
3074
            }
3075
        }
3076
3077
        $formBuilder = $this->getFormBuilder();
3078
        $formBuilder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
3079
            $this->preValidate($event->getData());
3080
        }, 100);
3081
3082
        $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...
3083
    }
3084
3085
    /**
3086
     * Gets the subclass corresponding to the given name.
3087
     *
3088
     * @param string $name The name of the sub class
3089
     *
3090
     * @return string the subclass
3091
     */
3092
    protected function getSubClass($name)
3093
    {
3094
        if ($this->hasSubClass($name)) {
3095
            return $this->subClasses[$name];
3096
        }
3097
3098
        throw new \RuntimeException(sprintf(
3099
            'Unable to find the subclass `%s` for admin `%s`',
3100
            $name,
3101
            static::class
3102
        ));
3103
    }
3104
3105
    /**
3106
     * Attach the inline validator to the model metadata, this must be done once per admin.
3107
     */
3108
    protected function attachInlineValidator()
3109
    {
3110
        $admin = $this;
3111
3112
        // add the custom inline validation option
3113
        $metadata = $this->validator->getMetadataFor($this->getClass());
3114
3115
        $metadata->addConstraint(new InlineConstraint([
3116
            'service' => $this,
3117
            'method' => static function (ErrorElement $errorElement, $object) use ($admin) {
3118
                /* @var \Sonata\AdminBundle\Admin\AdminInterface $admin */
3119
3120
                // This avoid the main validation to be cascaded to children
3121
                // The problem occurs when a model Page has a collection of Page as property
3122
                if ($admin->hasSubject() && spl_object_hash($object) !== spl_object_hash($admin->getSubject())) {
3123
                    return;
3124
                }
3125
3126
                $admin->validate($errorElement, $object);
3127
3128
                foreach ($admin->getExtensions() as $extension) {
3129
                    $extension->validate($admin, $errorElement, $object);
3130
                }
3131
            },
3132
            'serializingWarning' => true,
3133
        ]));
3134
    }
3135
3136
    /**
3137
     * Predefine per page options.
3138
     */
3139
    protected function predefinePerPageOptions()
3140
    {
3141
        array_unshift($this->perPageOptions, $this->maxPerPage);
3142
        $this->perPageOptions = array_unique($this->perPageOptions);
3143
        sort($this->perPageOptions);
3144
    }
3145
3146
    /**
3147
     * Return list routes with permissions name.
3148
     *
3149
     * @return array<string, string>
0 ignored issues
show
Documentation introduced by
The doc-type array<string, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

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

Loading history...
3150
     */
3151
    protected function getAccess()
3152
    {
3153
        $access = array_merge([
3154
            'acl' => 'MASTER',
3155
            'export' => 'EXPORT',
3156
            'historyCompareRevisions' => 'EDIT',
3157
            'historyViewRevision' => 'EDIT',
3158
            'history' => 'EDIT',
3159
            'edit' => 'EDIT',
3160
            'show' => 'VIEW',
3161
            'create' => 'CREATE',
3162
            'delete' => 'DELETE',
3163
            'batchDelete' => 'DELETE',
3164
            'list' => 'LIST',
3165
        ], $this->getAccessMapping());
3166
3167
        foreach ($this->extensions as $extension) {
3168
            // NEXT_MAJOR: remove method check
3169
            if (method_exists($extension, 'getAccessMapping')) {
3170
                $access = array_merge($access, $extension->getAccessMapping($this));
3171
            }
3172
        }
3173
3174
        return $access;
3175
    }
3176
3177
    /**
3178
     * Configures a list of default filters.
3179
     */
3180
    protected function configureDefaultFilterValues(array &$filterValues)
3181
    {
3182
    }
3183
3184
    /**
3185
     * Build all the related urls to the current admin.
3186
     */
3187
    private function buildRoutes(): void
3188
    {
3189
        if ($this->loaded['routes']) {
3190
            return;
3191
        }
3192
3193
        $this->loaded['routes'] = true;
3194
3195
        $this->routes = new RouteCollection(
3196
            $this->getBaseCodeRoute(),
3197
            $this->getBaseRouteName(),
3198
            $this->getBaseRoutePattern(),
3199
            $this->getBaseControllerName()
3200
        );
3201
3202
        $this->routeBuilder->build($this, $this->routes);
3203
3204
        $this->configureRoutes($this->routes);
3205
3206
        foreach ($this->getExtensions() as $extension) {
3207
            $extension->configureRoutes($this, $this->routes);
3208
        }
3209
    }
3210
}
3211
3212
class_exists(\Sonata\Form\Validator\ErrorElement::class);
3213