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

AbstractAdmin::getCurrentChild()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

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

Loading history...
625
    }
626
627
    /**
628
     * {@inheritdoc}
629
     */
630
    public function getExportFormats()
631
    {
632
        return [
633
            'json', 'xml', 'csv', 'xls',
634
        ];
635
    }
636
637
    /**
638
     * @return array
639
     */
640
    public function getExportFields()
641
    {
642
        $fields = $this->getModelManager()->getExportFields($this->getClass());
643
644
        foreach ($this->getExtensions() as $extension) {
645
            if (method_exists($extension, 'configureExportFields')) {
646
                $fields = $extension->configureExportFields($this, $fields);
647
            }
648
        }
649
650
        return $fields;
651
    }
652
653
    public function getDataSourceIterator()
654
    {
655
        $datagrid = $this->getDatagrid();
656
        $datagrid->buildPager();
657
658
        $fields = [];
659
660
        foreach ($this->getExportFields() as $key => $field) {
661
            $label = $this->getTranslationLabel($field, 'export', 'label');
662
            $transLabel = $this->trans($label);
663
664
            // NEXT_MAJOR: Remove this hack, because all field labels will be translated with the major release
665
            // No translation key exists
666
            if ($transLabel === $label) {
667
                $fields[$key] = $field;
668
            } else {
669
                $fields[$transLabel] = $field;
670
            }
671
        }
672
673
        return $this->getModelManager()->getDataSourceIterator($datagrid, $fields);
0 ignored issues
show
Bug introduced by
It seems like $datagrid defined by $this->getDatagrid() on line 655 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...
674
    }
675
676
    public function validate(ErrorElement $errorElement, $object)
677
    {
678
    }
679
680
    /**
681
     * define custom variable.
682
     */
683
    public function initialize()
684
    {
685
        if (!$this->classnameLabel) {
686
            /* NEXT_MAJOR: remove cast to string, null is not supposed to be
687
            supported but was documented as such */
688
            $this->classnameLabel = substr(
689
                (string) $this->getClass(),
690
                strrpos((string) $this->getClass(), '\\') + 1
691
            );
692
        }
693
694
        // NEXT_MAJOR: Remove this line.
695
        $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...
696
697
        $this->configure();
698
    }
699
700
    public function configure()
701
    {
702
    }
703
704
    public function update($object)
705
    {
706
        $this->preUpdate($object);
707
        foreach ($this->extensions as $extension) {
708
            $extension->preUpdate($this, $object);
709
        }
710
711
        $result = $this->getModelManager()->update($object);
712
        // BC compatibility
713
        if (null !== $result) {
714
            $object = $result;
715
        }
716
717
        $this->postUpdate($object);
718
        foreach ($this->extensions as $extension) {
719
            $extension->postUpdate($this, $object);
720
        }
721
722
        return $object;
723
    }
724
725
    public function create($object)
726
    {
727
        $this->prePersist($object);
728
        foreach ($this->extensions as $extension) {
729
            $extension->prePersist($this, $object);
730
        }
731
732
        $result = $this->getModelManager()->create($object);
733
        // BC compatibility
734
        if (null !== $result) {
735
            $object = $result;
736
        }
737
738
        $this->postPersist($object);
739
        foreach ($this->extensions as $extension) {
740
            $extension->postPersist($this, $object);
741
        }
742
743
        $this->createObjectSecurity($object);
744
745
        return $object;
746
    }
747
748
    public function delete($object)
749
    {
750
        $this->preRemove($object);
751
        foreach ($this->extensions as $extension) {
752
            $extension->preRemove($this, $object);
753
        }
754
755
        $this->getSecurityHandler()->deleteObjectSecurity($this, $object);
756
        $this->getModelManager()->delete($object);
757
758
        $this->postRemove($object);
759
        foreach ($this->extensions as $extension) {
760
            $extension->postRemove($this, $object);
761
        }
762
    }
763
764
    /**
765
     * @param object $object
766
     */
767
    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...
768
    {
769
    }
770
771
    public function preUpdate($object)
772
    {
773
    }
774
775
    public function postUpdate($object)
776
    {
777
    }
778
779
    public function prePersist($object)
780
    {
781
    }
782
783
    public function postPersist($object)
784
    {
785
    }
786
787
    public function preRemove($object)
788
    {
789
    }
790
791
    public function postRemove($object)
792
    {
793
    }
794
795
    public function preBatchAction($actionName, ProxyQueryInterface $query, array &$idx, $allElements)
796
    {
797
    }
798
799
    public function getFilterParameters()
800
    {
801
        $parameters = [];
802
803
        // build the values array
804
        if ($this->hasRequest()) {
805
            $filters = $this->request->query->get('filter', []);
806
            if (isset($filters['_page'])) {
807
                $filters['_page'] = (int) $filters['_page'];
808
            }
809
            if (isset($filters['_per_page'])) {
810
                $filters['_per_page'] = (int) $filters['_per_page'];
811
            }
812
813
            // if filter persistence is configured
814
            // NEXT_MAJOR: remove `$this->persistFilters !== false` from the condition
815
            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...
816
                // if reset filters is asked, remove from storage
817
                if ('reset' === $this->request->query->get('filters')) {
818
                    $this->filterPersister->reset($this->getCode());
819
                }
820
821
                // if no filters, fetch from storage
822
                // otherwise save to storage
823
                if (empty($filters)) {
824
                    $filters = $this->filterPersister->get($this->getCode());
825
                } else {
826
                    $this->filterPersister->set($this->getCode(), $filters);
827
                }
828
            }
829
830
            $parameters = array_merge(
831
                $this->getModelManager()->getDefaultSortValues($this->getClass()),
832
                $this->datagridValues, // NEXT_MAJOR: Remove this line.
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$datagridValues has been deprecated with message: since sonata-project/admin-bundle 3.67, use configureDefaultSortValues() instead.

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

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

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

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

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

Loading history...
967
                    throw new \RuntimeException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`', static::class));
968
                }
969
                $baseRoutePattern = $this->urlize($matches[5], '-');
970
            }
971
972
            $this->cachedBaseRoutePattern = sprintf(
973
                '%s/%s/%s',
974
                $this->getParent()->getBaseRoutePattern(),
975
                $this->getParent()->getRouterIdParameter(),
976
                $baseRoutePattern
977
            );
978
        } elseif ($this->baseRoutePattern) {
979
            $this->cachedBaseRoutePattern = $this->baseRoutePattern;
980
        } else {
981
            preg_match(self::CLASS_REGEX, $this->class, $matches);
982
983
            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...
984
                throw new \RuntimeException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`', static::class));
985
            }
986
987
            $this->cachedBaseRoutePattern = sprintf(
988
                '/%s%s/%s',
989
                empty($matches[1]) ? '' : $this->urlize($matches[1], '-').'/',
990
                $this->urlize($matches[3], '-'),
991
                $this->urlize($matches[5], '-')
992
            );
993
        }
994
995
        return $this->cachedBaseRoutePattern;
996
    }
997
998
    /**
999
     * Returns the baseRouteName used to generate the routing information.
1000
     *
1001
     * @throws \RuntimeException
1002
     *
1003
     * @return string the baseRouteName used to generate the routing information
1004
     */
1005
    public function getBaseRouteName()
1006
    {
1007
        if (null !== $this->cachedBaseRouteName) {
1008
            return $this->cachedBaseRouteName;
1009
        }
1010
1011
        if ($this->isChild()) { // the admin class is a child, prefix it with the parent route name
1012
            $baseRouteName = $this->baseRouteName;
1013
            if (!$this->baseRouteName) {
1014
                preg_match(self::CLASS_REGEX, $this->class, $matches);
1015
1016
                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...
1017
                    throw new \RuntimeException(sprintf('Cannot automatically determine base route name, please define a default `baseRouteName` value for the admin class `%s`', static::class));
1018
                }
1019
                $baseRouteName = $this->urlize($matches[5]);
1020
            }
1021
1022
            $this->cachedBaseRouteName = sprintf(
1023
                '%s_%s',
1024
                $this->getParent()->getBaseRouteName(),
1025
                $baseRouteName
1026
            );
1027
        } elseif ($this->baseRouteName) {
1028
            $this->cachedBaseRouteName = $this->baseRouteName;
1029
        } else {
1030
            preg_match(self::CLASS_REGEX, $this->class, $matches);
1031
1032
            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...
1033
                throw new \RuntimeException(sprintf('Cannot automatically determine base route name, please define a default `baseRouteName` value for the admin class `%s`', static::class));
1034
            }
1035
1036
            $this->cachedBaseRouteName = sprintf(
1037
                'admin_%s%s_%s',
1038
                empty($matches[1]) ? '' : $this->urlize($matches[1]).'_',
1039
                $this->urlize($matches[3]),
1040
                $this->urlize($matches[5])
1041
            );
1042
        }
1043
1044
        return $this->cachedBaseRouteName;
1045
    }
1046
1047
    /**
1048
     * urlize the given word.
1049
     *
1050
     * @param string $word
1051
     * @param string $sep  the separator
1052
     *
1053
     * @return string
1054
     */
1055
    public function urlize($word, $sep = '_')
1056
    {
1057
        return strtolower(preg_replace('/[^a-z0-9_]/i', $sep.'$1', $word));
1058
    }
1059
1060
    public function getClass()
1061
    {
1062
        if ($this->hasActiveSubClass()) {
1063
            if ($this->hasParentFieldDescription()) {
1064
                throw new \RuntimeException('Feature not implemented: an embedded admin cannot have subclass');
1065
            }
1066
1067
            $subClass = $this->getRequest()->query->get('subclass');
1068
1069
            if (!$this->hasSubClass($subClass)) {
1070
                throw new \RuntimeException(sprintf('Subclass "%s" is not defined.', $subClass));
1071
            }
1072
1073
            return $this->getSubClass($subClass);
1074
        }
1075
1076
        // see https://github.com/sonata-project/SonataCoreBundle/commit/247eeb0a7ca7211142e101754769d70bc402a5b4
1077
        if ($this->subject && \is_object($this->subject)) {
1078
            return ClassUtils::getClass($this->subject);
1079
        }
1080
1081
        return $this->class;
1082
    }
1083
1084
    public function getSubClasses()
1085
    {
1086
        return $this->subClasses;
1087
    }
1088
1089
    /**
1090
     * NEXT_MAJOR: remove this method.
1091
     */
1092
    public function addSubClass($subClass)
1093
    {
1094
        @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...
1095
            'Method "%s" is deprecated since sonata-project/admin-bundle 3.30 and will be removed in 4.0.',
1096
            __METHOD__
1097
        ), E_USER_DEPRECATED);
1098
1099
        if (!\in_array($subClass, $this->subClasses, true)) {
1100
            $this->subClasses[] = $subClass;
1101
        }
1102
    }
1103
1104
    public function setSubClasses(array $subClasses)
1105
    {
1106
        $this->subClasses = $subClasses;
1107
    }
1108
1109
    public function hasSubClass($name)
1110
    {
1111
        return isset($this->subClasses[$name]);
1112
    }
1113
1114
    public function hasActiveSubClass()
1115
    {
1116
        if (\count($this->subClasses) > 0 && $this->request) {
1117
            return null !== $this->getRequest()->query->get('subclass');
1118
        }
1119
1120
        return false;
1121
    }
1122
1123
    public function getActiveSubClass()
1124
    {
1125
        if (!$this->hasActiveSubClass()) {
1126
            @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...
1127
                '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. '.
1128
                'Use %s::hasActiveSubClass() to know if there is an active subclass.',
1129
                __METHOD__,
1130
                __CLASS__
1131
            ), E_USER_DEPRECATED);
1132
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare string as return type
1133
            // throw new \LogicException(sprintf(
1134
            //    'Admin "%s" has no active subclass.',
1135
            //    static::class
1136
            // ));
1137
1138
            return null;
1139
        }
1140
1141
        return $this->getSubClass($this->getActiveSubclassCode());
1142
    }
1143
1144
    public function getActiveSubclassCode()
1145
    {
1146
        if (!$this->hasActiveSubClass()) {
1147
            @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...
1148
                '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. '.
1149
                'Use %s::hasActiveSubClass() to know if there is an active subclass.',
1150
                __METHOD__,
1151
                __CLASS__
1152
            ), E_USER_DEPRECATED);
1153
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare string as return type
1154
            // throw new \LogicException(sprintf(
1155
            //    'Admin "%s" has no active subclass.',
1156
            //    static::class
1157
            // ));
1158
1159
            return null;
1160
        }
1161
1162
        $subClass = $this->getRequest()->query->get('subclass');
1163
1164
        if (!$this->hasSubClass($subClass)) {
1165
            @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...
1166
                '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. '.
1167
                'Use %s::hasActiveSubClass() to know if there is an active subclass.',
1168
                __METHOD__,
1169
                __CLASS__
1170
            ), E_USER_DEPRECATED);
1171
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare string as return type
1172
            // throw new \LogicException(sprintf(
1173
            //    'Admin "%s" has no active subclass.',
1174
            //    static::class
1175
            // ));
1176
1177
            return null;
1178
        }
1179
1180
        return $subClass;
1181
    }
1182
1183
    public function getBatchActions()
1184
    {
1185
        $actions = [];
1186
1187
        if ($this->hasRoute('delete') && $this->hasAccess('delete')) {
1188
            $actions['delete'] = [
1189
                'label' => 'action_delete',
1190
                'translation_domain' => 'SonataAdminBundle',
1191
                'ask_confirmation' => true, // by default always true
1192
            ];
1193
        }
1194
1195
        $actions = $this->configureBatchActions($actions);
1196
1197
        foreach ($this->getExtensions() as $extension) {
1198
            // NEXT_MAJOR: remove method check
1199
            if (method_exists($extension, 'configureBatchActions')) {
1200
                $actions = $extension->configureBatchActions($this, $actions);
1201
            }
1202
        }
1203
1204
        foreach ($actions  as $name => &$action) {
1205
            if (!\array_key_exists('label', $action)) {
1206
                $action['label'] = $this->getTranslationLabel($name, 'batch', 'label');
1207
            }
1208
1209
            if (!\array_key_exists('translation_domain', $action)) {
1210
                $action['translation_domain'] = $this->getTranslationDomain();
1211
            }
1212
        }
1213
1214
        return $actions;
1215
    }
1216
1217
    public function getRoutes()
1218
    {
1219
        $this->buildRoutes();
1220
1221
        return $this->routes;
1222
    }
1223
1224
    public function getRouterIdParameter()
1225
    {
1226
        return '{'.$this->getIdParameter().'}';
1227
    }
1228
1229
    public function getIdParameter()
1230
    {
1231
        $parameter = 'id';
1232
1233
        for ($i = 0; $i < $this->getChildDepth(); ++$i) {
1234
            $parameter = 'child'.ucfirst($parameter);
1235
        }
1236
1237
        return $parameter;
1238
    }
1239
1240
    public function hasRoute($name)
1241
    {
1242
        if (!$this->routeGenerator) {
1243
            throw new \RuntimeException('RouteGenerator cannot be null');
1244
        }
1245
1246
        return $this->routeGenerator->hasAdminRoute($this, $name);
1247
    }
1248
1249
    /**
1250
     * @param string      $name
1251
     * @param string|null $adminCode
1252
     *
1253
     * @return bool
1254
     */
1255
    public function isCurrentRoute($name, $adminCode = null)
1256
    {
1257
        if (!$this->hasRequest()) {
1258
            return false;
1259
        }
1260
1261
        $request = $this->getRequest();
1262
        $route = $request->get('_route');
1263
1264
        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...
1265
            $admin = $this->getConfigurationPool()->getAdminByAdminCode($adminCode);
1266
        } else {
1267
            $admin = $this;
1268
        }
1269
1270
        if (!$admin) {
1271
            return false;
1272
        }
1273
1274
        return ($admin->getBaseRouteName().'_'.$name) === $route;
1275
    }
1276
1277
    public function generateObjectUrl($name, $object, array $parameters = [], $referenceType = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
1278
    {
1279
        $parameters['id'] = $this->getUrlSafeIdentifier($object);
1280
1281
        return $this->generateUrl($name, $parameters, $referenceType);
1282
    }
1283
1284
    public function generateUrl($name, array $parameters = [], $referenceType = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
1285
    {
1286
        return $this->routeGenerator->generateUrl($this, $name, $parameters, $referenceType);
1287
    }
1288
1289
    public function generateMenuUrl($name, array $parameters = [], $referenceType = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
1290
    {
1291
        return $this->routeGenerator->generateMenuUrl($this, $name, $parameters, $referenceType);
1292
    }
1293
1294
    final public function setTemplateRegistry(MutableTemplateRegistryInterface $templateRegistry)
1295
    {
1296
        $this->templateRegistry = $templateRegistry;
1297
    }
1298
1299
    /**
1300
     * @param array<string, string> $templates
1301
     */
1302
    public function setTemplates(array $templates)
1303
    {
1304
        // NEXT_MAJOR: Remove this line
1305
        $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...
1306
1307
        $this->getTemplateRegistry()->setTemplates($templates);
1308
    }
1309
1310
    /**
1311
     * @param string $name
1312
     * @param string $template
1313
     */
1314
    public function setTemplate($name, $template)
1315
    {
1316
        // NEXT_MAJOR: Remove this line
1317
        $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...
1318
1319
        $this->getTemplateRegistry()->setTemplate($name, $template);
1320
    }
1321
1322
    /**
1323
     * @deprecated since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead
1324
     *
1325
     * @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...
1326
     */
1327
    public function getTemplates()
1328
    {
1329
        return $this->getTemplateRegistry()->getTemplates();
1330
    }
1331
1332
    /**
1333
     * @deprecated since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead
1334
     *
1335
     * @param string $name
1336
     *
1337
     * @return string|null
1338
     */
1339
    public function getTemplate($name)
1340
    {
1341
        return $this->getTemplateRegistry()->getTemplate($name);
1342
    }
1343
1344
    public function getNewInstance()
1345
    {
1346
        $object = $this->getModelManager()->getModelInstance($this->getClass());
1347
1348
        // Append parent object if any
1349
        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...
1350
            $parentAdmin = $this->getParent();
1351
            $parent = $parentAdmin->getObject($this->request->get($parentAdmin->getIdParameter()));
1352
1353
            if (null !== $parent) {
1354
                $propertyAccessor = $this->getConfigurationPool()->getPropertyAccessor();
1355
                $propertyPath = new PropertyPath($this->getParentAssociationMapping());
1356
1357
                $value = $propertyAccessor->getValue($object, $propertyPath);
1358
1359
                if (\is_array($value) || $value instanceof \ArrayAccess) {
1360
                    $value[] = $parent;
1361
                    $propertyAccessor->setValue($object, $propertyPath, $value);
1362
                } else {
1363
                    $propertyAccessor->setValue($object, $propertyPath, $parent);
1364
                }
1365
            }
1366
        } elseif ($this->hasParentFieldDescription()) {
1367
            $parentAdmin = $this->getParentFieldDescription()->getAdmin();
1368
            $parent = $parentAdmin->getObject($this->request->get($parentAdmin->getIdParameter()));
1369
1370
            if (null !== $parent) {
1371
                AdminHelper::addInstance($parent, $this->getParentFieldDescription(), $object);
0 ignored issues
show
Bug introduced by
It seems like $this->getParentFieldDescription() can be null; however, addInstance() does not accept null, maybe add an additional type check?

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1372
            }
1373
        }
1374
1375
        foreach ($this->getExtensions() as $extension) {
1376
            $extension->alterNewInstance($this, $object);
0 ignored issues
show
Bug introduced by
It seems like $object can also be of type array; however, Sonata\AdminBundle\Admin...ace::alterNewInstance() 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...
1377
        }
1378
1379
        return $object;
0 ignored issues
show
Bug Compatibility introduced by
The expression return $object; of type object|array is incompatible with the return type declared by the interface Sonata\AdminBundle\Admin...terface::getNewInstance of type object as it can also be of type array which is not included in this return type.
Loading history...
1380
    }
1381
1382
    public function getFormBuilder()
1383
    {
1384
        $this->formOptions['data_class'] = $this->getClass();
1385
1386
        $formBuilder = $this->getFormContractor()->getFormBuilder(
1387
            $this->getUniqid(),
1388
            $this->formOptions
1389
        );
1390
1391
        $this->defineFormBuilder($formBuilder);
1392
1393
        return $formBuilder;
1394
    }
1395
1396
    /**
1397
     * This method is being called by the main admin class and the child class,
1398
     * the getFormBuilder is only call by the main admin class.
1399
     */
1400
    public function defineFormBuilder(FormBuilderInterface $formBuilder)
1401
    {
1402
        if (!$this->hasSubject()) {
1403
            @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...
1404
                'Calling %s() when there is no subject is deprecated since sonata-project/admin-bundle 3.65 and will throw an exception in 4.0. '.
1405
                'Use %s::setSubject() to set the subject.',
1406
                __METHOD__,
1407
                __CLASS__
1408
            ), E_USER_DEPRECATED);
1409
            // NEXT_MAJOR : remove the previous `trigger_error()` call and uncomment the following exception
1410
            // throw new \LogicException(sprintf(
1411
            //    'Admin "%s" has no subject.',
1412
            //    static::class
1413
            // ));
1414
        }
1415
1416
        $mapper = new FormMapper($this->getFormContractor(), $formBuilder, $this);
1417
1418
        $this->configureFormFields($mapper);
1419
1420
        foreach ($this->getExtensions() as $extension) {
1421
            $extension->configureFormFields($mapper);
1422
        }
1423
1424
        $this->attachInlineValidator();
1425
    }
1426
1427
    public function attachAdminClass(FieldDescriptionInterface $fieldDescription)
1428
    {
1429
        $pool = $this->getConfigurationPool();
1430
1431
        $adminCode = $fieldDescription->getOption('admin_code');
1432
1433
        if (null !== $adminCode) {
1434
            if (!$pool->hasAdminByAdminCode($adminCode)) {
1435
                return;
1436
            }
1437
1438
            $admin = $pool->getAdminByAdminCode($adminCode);
1439
        } else {
1440
            if (!$pool->hasAdminByClass($fieldDescription->getTargetEntity())) {
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...face::getTargetEntity() has been deprecated with message: since sonata-project/admin-bundle 3.69. Use `getTargetModel()` instead.

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

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

Loading history...
1441
                return;
1442
            }
1443
1444
            $admin = $pool->getAdminByClass($fieldDescription->getTargetEntity());
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...face::getTargetEntity() has been deprecated with message: since sonata-project/admin-bundle 3.69. Use `getTargetModel()` instead.

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

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

Loading history...
1445
        }
1446
1447
        if ($this->hasRequest()) {
1448
            $admin->setRequest($this->getRequest());
1449
        }
1450
1451
        $fieldDescription->setAssociationAdmin($admin);
0 ignored issues
show
Bug introduced by
It seems like $admin can also be of type false or null; however, Sonata\AdminBundle\Admin...::setAssociationAdmin() does only seem to accept object<Sonata\AdminBundle\Admin\AdminInterface>, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
1452
    }
1453
1454
    public function getObject($id)
1455
    {
1456
        $object = $this->getModelManager()->find($this->getClass(), $id);
1457
        foreach ($this->getExtensions() as $extension) {
1458
            $extension->alterObject($this, $object);
0 ignored issues
show
Bug introduced by
It seems like $object defined by $this->getModelManager()...$this->getClass(), $id) on line 1456 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...
1459
        }
1460
1461
        return $object;
1462
    }
1463
1464
    public function getForm()
1465
    {
1466
        $this->buildForm();
1467
1468
        return $this->form;
1469
    }
1470
1471
    public function getList()
1472
    {
1473
        $this->buildList();
1474
1475
        return $this->list;
1476
    }
1477
1478
    /**
1479
     * @final since sonata-project/admin-bundle 3.63.0
1480
     */
1481
    public function createQuery($context = 'list')
1482
    {
1483
        if (\func_num_args() > 0) {
1484
            @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...
1485
                'The $context argument of '.__METHOD__.' is deprecated since 3.3, to be removed in 4.0.',
1486
                E_USER_DEPRECATED
1487
            );
1488
        }
1489
1490
        $query = $this->getModelManager()->createQuery($this->getClass());
1491
1492
        $query = $this->configureQuery($query);
1493
        foreach ($this->extensions as $extension) {
1494
            $extension->configureQuery($this, $query, $context);
1495
        }
1496
1497
        return $query;
1498
    }
1499
1500
    public function getDatagrid()
1501
    {
1502
        $this->buildDatagrid();
1503
1504
        return $this->datagrid;
1505
    }
1506
1507
    public function buildTabMenu($action, ?AdminInterface $childAdmin = null)
1508
    {
1509
        if ($this->loaded['tab_menu']) {
1510
            return $this->menu;
1511
        }
1512
1513
        $this->loaded['tab_menu'] = true;
1514
1515
        $menu = $this->menuFactory->createItem('root');
1516
        $menu->setChildrenAttribute('class', 'nav navbar-nav');
1517
        $menu->setExtra('translation_domain', $this->translationDomain);
1518
1519
        // Prevents BC break with KnpMenuBundle v1.x
1520
        if (method_exists($menu, 'setCurrentUri')) {
1521
            $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...
1522
        }
1523
1524
        $this->configureTabMenu($menu, $action, $childAdmin);
1525
1526
        foreach ($this->getExtensions() as $extension) {
1527
            $extension->configureTabMenu($this, $menu, $action, $childAdmin);
1528
        }
1529
1530
        $this->menu = $menu;
1531
1532
        return $this->menu;
1533
    }
1534
1535
    public function buildSideMenu($action, ?AdminInterface $childAdmin = null)
1536
    {
1537
        return $this->buildTabMenu($action, $childAdmin);
1538
    }
1539
1540
    /**
1541
     * @param string $action
1542
     *
1543
     * @return ItemInterface
1544
     */
1545
    public function getSideMenu($action, ?AdminInterface $childAdmin = null)
1546
    {
1547
        if ($this->isChild()) {
1548
            return $this->getParent()->getSideMenu($action, $this);
1549
        }
1550
1551
        $this->buildSideMenu($action, $childAdmin);
1552
1553
        return $this->menu;
1554
    }
1555
1556
    /**
1557
     * Returns the root code.
1558
     *
1559
     * @return string the root code
1560
     */
1561
    public function getRootCode()
1562
    {
1563
        return $this->getRoot()->getCode();
1564
    }
1565
1566
    /**
1567
     * Returns the master admin.
1568
     *
1569
     * @return AbstractAdmin the root admin class
1570
     */
1571
    public function getRoot()
1572
    {
1573
        if (!$this->hasParentFieldDescription()) {
1574
            return $this;
1575
        }
1576
1577
        return $this->getParentFieldDescription()->getAdmin()->getRoot();
1578
    }
1579
1580
    public function setBaseControllerName($baseControllerName)
1581
    {
1582
        $this->baseControllerName = $baseControllerName;
1583
    }
1584
1585
    public function getBaseControllerName()
1586
    {
1587
        return $this->baseControllerName;
1588
    }
1589
1590
    /**
1591
     * @param string $label
1592
     */
1593
    public function setLabel($label)
1594
    {
1595
        $this->label = $label;
1596
    }
1597
1598
    public function getLabel()
1599
    {
1600
        return $this->label;
1601
    }
1602
1603
    /**
1604
     * @param bool $persist
1605
     *
1606
     * NEXT_MAJOR: remove this method
1607
     *
1608
     * @deprecated since sonata-project/admin-bundle 3.34, to be removed in 4.0.
1609
     */
1610
    public function setPersistFilters($persist)
1611
    {
1612
        @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...
1613
            'The '.__METHOD__.' method is deprecated since version 3.34 and will be removed in 4.0.',
1614
            E_USER_DEPRECATED
1615
        );
1616
1617
        $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...
1618
    }
1619
1620
    public function setFilterPersister(?FilterPersisterInterface $filterPersister = null)
1621
    {
1622
        $this->filterPersister = $filterPersister;
1623
        // NEXT_MAJOR remove the deprecated property will be removed. Needed for persisted filter condition.
1624
        $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...
1625
    }
1626
1627
    /**
1628
     * NEXT_MAJOR: Remove this method.
1629
     *
1630
     * @deprecated since sonata-project/admin-bundle 3.67, to be removed in 4.0.
1631
     *
1632
     * @param int $maxPerPage
1633
     */
1634
    public function setMaxPerPage($maxPerPage)
1635
    {
1636
        @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...
1637
            'The method %s is deprecated since sonata-project/admin-bundle 3.67 and will be removed in 4.0.',
1638
            __METHOD__
1639
        ), E_USER_DEPRECATED);
1640
1641
        $this->maxPerPage = $maxPerPage;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$maxPerPage has been deprecated with message: since sonata-project/admin-bundle 3.67.

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

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

Loading history...
1642
    }
1643
1644
    /**
1645
     * @return int
1646
     */
1647
    public function getMaxPerPage()
1648
    {
1649
        // NEXT_MAJOR: Remove this line and uncomment the following.
1650
        return $this->maxPerPage;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tractAdmin::$maxPerPage has been deprecated with message: since sonata-project/admin-bundle 3.67.

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

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

Loading history...
1651
        // $sortValues = $this->getModelManager()->getDefaultSortValues($this->class);
1652
1653
        // return $sortValues['_per_page'] ?? 25;
1654
    }
1655
1656
    /**
1657
     * @param int $maxPageLinks
1658
     */
1659
    public function setMaxPageLinks($maxPageLinks)
1660
    {
1661
        $this->maxPageLinks = $maxPageLinks;
1662
    }
1663
1664
    /**
1665
     * @return int
1666
     */
1667
    public function getMaxPageLinks()
1668
    {
1669
        return $this->maxPageLinks;
1670
    }
1671
1672
    public function getFormGroups()
1673
    {
1674
        if (!\is_array($this->formGroups) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
1675
            @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...
1676
                'Returning other type than array in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only array in version 4.0.',
1677
                __METHOD__
1678
            ), E_USER_DEPRECATED);
1679
        }
1680
1681
        return $this->formGroups;
1682
    }
1683
1684
    public function setFormGroups(array $formGroups)
1685
    {
1686
        $this->formGroups = $formGroups;
1687
    }
1688
1689
    public function removeFieldFromFormGroup($key)
1690
    {
1691
        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...
1692
            unset($this->formGroups[$name]['fields'][$key]);
1693
1694
            if (empty($this->formGroups[$name]['fields'])) {
1695
                unset($this->formGroups[$name]);
1696
            }
1697
        }
1698
    }
1699
1700
    /**
1701
     * @param string $group
1702
     */
1703
    public function reorderFormGroup($group, array $keys)
1704
    {
1705
        // NEXT_MAJOR: Remove the argument "sonata_deprecation_mute" in the following call.
1706
        $formGroups = $this->getFormGroups('sonata_deprecation_mute');
1707
        $formGroups[$group]['fields'] = array_merge(array_flip($keys), $formGroups[$group]['fields']);
1708
        $this->setFormGroups($formGroups);
0 ignored issues
show
Bug introduced by
It seems like $formGroups defined by $this->getFormGroups('sonata_deprecation_mute') on line 1706 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...
1709
    }
1710
1711
    public function getFormTabs()
1712
    {
1713
        if (!\is_array($this->formTabs) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
1714
            @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...
1715
                'Returning other type than array in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only array in version 4.0.',
1716
                __METHOD__
1717
            ), E_USER_DEPRECATED);
1718
        }
1719
1720
        return $this->formTabs;
1721
    }
1722
1723
    public function setFormTabs(array $formTabs)
1724
    {
1725
        $this->formTabs = $formTabs;
1726
    }
1727
1728
    public function getShowTabs()
1729
    {
1730
        if (!\is_array($this->showTabs) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
1731
            @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...
1732
                'Returning other type than array in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only array in version 4.0.',
1733
                __METHOD__
1734
            ), E_USER_DEPRECATED);
1735
        }
1736
1737
        return $this->showTabs;
1738
    }
1739
1740
    public function setShowTabs(array $showTabs)
1741
    {
1742
        $this->showTabs = $showTabs;
1743
    }
1744
1745
    public function getShowGroups()
1746
    {
1747
        if (!\is_array($this->showGroups) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
1748
            @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...
1749
                'Returning other type than array in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only array in version 4.0.',
1750
                __METHOD__
1751
            ), E_USER_DEPRECATED);
1752
        }
1753
1754
        return $this->showGroups;
1755
    }
1756
1757
    public function setShowGroups(array $showGroups)
1758
    {
1759
        $this->showGroups = $showGroups;
1760
    }
1761
1762
    public function reorderShowGroup($group, array $keys)
1763
    {
1764
        // NEXT_MAJOR: Remove the argument "sonata_deprecation_mute" in the following call.
1765
        $showGroups = $this->getShowGroups('sonata_deprecation_mute');
1766
        $showGroups[$group]['fields'] = array_merge(array_flip($keys), $showGroups[$group]['fields']);
1767
        $this->setShowGroups($showGroups);
0 ignored issues
show
Bug introduced by
It seems like $showGroups defined by $this->getShowGroups('sonata_deprecation_mute') on line 1765 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...
1768
    }
1769
1770
    public function setParentFieldDescription(FieldDescriptionInterface $parentFieldDescription)
1771
    {
1772
        $this->parentFieldDescription = $parentFieldDescription;
1773
    }
1774
1775
    public function getParentFieldDescription()
1776
    {
1777
        if (!$this->hasParentFieldDescription()) {
1778
            @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...
1779
                'Calling %s() when there is no parent field description is deprecated since sonata-project/admin-bundle 3.66 and will throw an exception in 4.0. '.
1780
                'Use %s::hasParentFieldDescription() to know if there is a parent field description.',
1781
                __METHOD__,
1782
                __CLASS__
1783
            ), E_USER_DEPRECATED);
1784
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare FieldDescriptionInterface as return type
1785
            // throw new \LogicException(sprintf(
1786
            //    'Admin "%s" has no parent field description.',
1787
            //    static::class
1788
            // ));
1789
1790
            return null;
1791
        }
1792
1793
        return $this->parentFieldDescription;
1794
    }
1795
1796
    public function hasParentFieldDescription()
1797
    {
1798
        return $this->parentFieldDescription instanceof FieldDescriptionInterface;
1799
    }
1800
1801
    public function setSubject($subject)
1802
    {
1803
        if (\is_object($subject) && !is_a($subject, $this->getClass(), true)) {
1804
            $message = <<<'EOT'
1805
You are trying to set entity an instance of "%s",
1806
which is not the one registered with this admin class ("%s").
1807
This is deprecated since 3.5 and will no longer be supported in 4.0.
1808
EOT;
1809
1810
            @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...
1811
                sprintf($message, \get_class($subject), $this->getClass()),
1812
                E_USER_DEPRECATED
1813
            ); // NEXT_MAJOR : throw an exception instead
1814
        }
1815
1816
        $this->subject = $subject;
1817
    }
1818
1819
    public function getSubject()
1820
    {
1821
        if (!$this->hasSubject()) {
1822
            @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...
1823
                'Calling %s() when there is no subject is deprecated since sonata-project/admin-bundle 3.66 and will throw an exception in 4.0. '.
1824
                'Use %s::hasSubject() to know if there is a subject.',
1825
                __METHOD__,
1826
                __CLASS__
1827
            ), E_USER_DEPRECATED);
1828
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and update the return type
1829
            // throw new \LogicException(sprintf(
1830
            //    'Admin "%s" has no subject.',
1831
            //    static::class
1832
            // ));
1833
1834
            return null;
1835
        }
1836
1837
        return $this->subject;
1838
    }
1839
1840
    public function hasSubject()
1841
    {
1842
        if (null === $this->subject && $this->hasRequest() && !$this->hasParentFieldDescription()) {
1843
            $id = $this->request->get($this->getIdParameter());
1844
1845
            if (null !== $id) {
1846
                $this->subject = $this->getObject($id);
1847
            }
1848
        }
1849
1850
        return null !== $this->subject;
1851
    }
1852
1853
    public function getFormFieldDescriptions()
1854
    {
1855
        $this->buildForm();
1856
1857
        return $this->formFieldDescriptions;
1858
    }
1859
1860
    public function getFormFieldDescription($name)
1861
    {
1862
        $this->buildForm();
1863
1864
        if (!$this->hasFormFieldDescription($name)) {
1865
            @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...
1866
                'Calling %s() when there is no form field description is deprecated since sonata-project/admin-bundle 3.69 and will throw an exception in 4.0. '.
1867
                'Use %s::hasFormFieldDescription() to know if there is a form field description.',
1868
                __METHOD__,
1869
                __CLASS__
1870
            ), E_USER_DEPRECATED);
1871
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare FieldDescriptionInterface as return type
1872
            // throw new \LogicException(sprintf(
1873
            //    'Admin "%s" has no form field description for the field %s.',
1874
            //    static::class,
1875
            //    $name
1876
            // ));
1877
1878
            return null;
1879
        }
1880
1881
        return $this->formFieldDescriptions[$name];
1882
    }
1883
1884
    /**
1885
     * Returns true if the admin has a FieldDescription with the given $name.
1886
     *
1887
     * @param string $name
1888
     *
1889
     * @return bool
1890
     */
1891
    public function hasFormFieldDescription($name)
1892
    {
1893
        $this->buildForm();
1894
1895
        return \array_key_exists($name, $this->formFieldDescriptions) ? true : false;
1896
    }
1897
1898
    public function addFormFieldDescription($name, FieldDescriptionInterface $fieldDescription)
1899
    {
1900
        $this->formFieldDescriptions[$name] = $fieldDescription;
1901
    }
1902
1903
    /**
1904
     * remove a FieldDescription.
1905
     *
1906
     * @param string $name
1907
     */
1908
    public function removeFormFieldDescription($name)
1909
    {
1910
        unset($this->formFieldDescriptions[$name]);
1911
    }
1912
1913
    /**
1914
     * build and return the collection of form FieldDescription.
1915
     *
1916
     * @return FieldDescriptionInterface[] collection of form FieldDescription
1917
     */
1918
    public function getShowFieldDescriptions()
1919
    {
1920
        $this->buildShow();
1921
1922
        return $this->showFieldDescriptions;
1923
    }
1924
1925
    /**
1926
     * Returns the form FieldDescription with the given $name.
1927
     *
1928
     * @param string $name
1929
     *
1930
     * @return FieldDescriptionInterface
1931
     */
1932
    public function getShowFieldDescription($name)
1933
    {
1934
        $this->buildShow();
1935
1936
        if (!$this->hasShowFieldDescription($name)) {
1937
            @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...
1938
                'Calling %s() when there is no show field description is deprecated since sonata-project/admin-bundle 3.69 and will throw an exception in 4.0. '.
1939
                'Use %s::hasFormFieldDescription() to know if there is a show field description.',
1940
                __METHOD__,
1941
                __CLASS__
1942
            ), E_USER_DEPRECATED);
1943
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare FieldDescriptionInterface as return type
1944
            // throw new \LogicException(sprintf(
1945
            //    'Admin "%s" has no show field description for the field %s.',
1946
            //    static::class,
1947
            //    $name
1948
            // ));
1949
1950
            return null;
1951
        }
1952
1953
        return $this->showFieldDescriptions[$name];
1954
    }
1955
1956
    public function hasShowFieldDescription($name)
1957
    {
1958
        $this->buildShow();
1959
1960
        return \array_key_exists($name, $this->showFieldDescriptions);
1961
    }
1962
1963
    public function addShowFieldDescription($name, FieldDescriptionInterface $fieldDescription)
1964
    {
1965
        $this->showFieldDescriptions[$name] = $fieldDescription;
1966
    }
1967
1968
    public function removeShowFieldDescription($name)
1969
    {
1970
        unset($this->showFieldDescriptions[$name]);
1971
    }
1972
1973
    public function getListFieldDescriptions()
1974
    {
1975
        $this->buildList();
1976
1977
        return $this->listFieldDescriptions;
1978
    }
1979
1980
    public function getListFieldDescription($name)
1981
    {
1982
        $this->buildList();
1983
1984
        if (!$this->hasListFieldDescription($name)) {
1985
            @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...
1986
                'Calling %s() when there is no list field description is deprecated since sonata-project/admin-bundle 3.66 and will throw an exception in 4.0. '.
1987
                'Use %s::hasListFieldDescription(\'%s\') to know if there is a list field description.',
1988
                __METHOD__,
1989
                __CLASS__,
1990
                $name
1991
            ), E_USER_DEPRECATED);
1992
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare FieldDescriptionInterface as return type
1993
            // throw new \LogicException(sprintf(
1994
            //    'Admin "%s" has no list field description for %s.',
1995
            //    static::class,
1996
            //    $name
1997
            // ));
1998
1999
            return null;
2000
        }
2001
2002
        return $this->listFieldDescriptions[$name];
2003
    }
2004
2005
    public function hasListFieldDescription($name)
2006
    {
2007
        $this->buildList();
2008
2009
        return \array_key_exists($name, $this->listFieldDescriptions) ? true : false;
2010
    }
2011
2012
    public function addListFieldDescription($name, FieldDescriptionInterface $fieldDescription)
2013
    {
2014
        $this->listFieldDescriptions[$name] = $fieldDescription;
2015
    }
2016
2017
    public function removeListFieldDescription($name)
2018
    {
2019
        unset($this->listFieldDescriptions[$name]);
2020
    }
2021
2022
    public function getFilterFieldDescription($name)
2023
    {
2024
        $this->buildDatagrid();
2025
2026
        if (!$this->hasFilterFieldDescription($name)) {
2027
            @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...
2028
                'Calling %s() when there is no filter field description is deprecated since sonata-project/admin-bundle 3.69 and will throw an exception in 4.0. '.
2029
                'Use %s::hasFilterFieldDescription() to know if there is a filter field description.',
2030
                __METHOD__,
2031
                __CLASS__
2032
            ), E_USER_DEPRECATED);
2033
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare FieldDescriptionInterface as return type
2034
            // throw new \LogicException(sprintf(
2035
            //    'Admin "%s" has no filter field description for the field %s.',
2036
            //    static::class,
2037
            //    $name
2038
            // ));
2039
2040
            return null;
2041
        }
2042
2043
        return $this->filterFieldDescriptions[$name];
2044
    }
2045
2046
    public function hasFilterFieldDescription($name)
2047
    {
2048
        $this->buildDatagrid();
2049
2050
        return \array_key_exists($name, $this->filterFieldDescriptions) ? true : false;
2051
    }
2052
2053
    public function addFilterFieldDescription($name, FieldDescriptionInterface $fieldDescription)
2054
    {
2055
        $this->filterFieldDescriptions[$name] = $fieldDescription;
2056
    }
2057
2058
    public function removeFilterFieldDescription($name)
2059
    {
2060
        unset($this->filterFieldDescriptions[$name]);
2061
    }
2062
2063
    public function getFilterFieldDescriptions()
2064
    {
2065
        $this->buildDatagrid();
2066
2067
        return $this->filterFieldDescriptions;
2068
    }
2069
2070
    public function addChild(AdminInterface $child)
2071
    {
2072
        $parentAdmin = $this;
2073
        while ($parentAdmin->isChild() && $parentAdmin->getCode() !== $child->getCode()) {
2074
            $parentAdmin = $parentAdmin->getParent();
2075
        }
2076
2077
        if ($parentAdmin->getCode() === $child->getCode()) {
2078
            throw new \RuntimeException(sprintf(
2079
                'Circular reference detected! The child admin `%s` is already in the parent tree of the `%s` admin.',
2080
                $child->getCode(),
2081
                $this->getCode()
2082
            ));
2083
        }
2084
2085
        $this->children[$child->getCode()] = $child;
2086
2087
        $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...
2088
2089
        // NEXT_MAJOR: remove $args and add $field parameter to this function on next Major
2090
2091
        $args = \func_get_args();
2092
2093
        if (isset($args[1])) {
2094
            $child->addParentAssociationMapping($this->getCode(), $args[1]);
2095
        } else {
2096
            @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...
2097
                'Calling "addChild" without second argument is deprecated since'
2098
                .' sonata-project/admin-bundle 3.35 and will not be allowed in 4.0.',
2099
                E_USER_DEPRECATED
2100
            );
2101
        }
2102
    }
2103
2104
    public function hasChild($code)
2105
    {
2106
        return isset($this->children[$code]);
2107
    }
2108
2109
    public function getChildren()
2110
    {
2111
        return $this->children;
2112
    }
2113
2114
    public function getChild($code)
2115
    {
2116
        if (!$this->hasChild($code)) {
2117
            @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...
2118
                'Calling %s() when there is no child is deprecated since sonata-project/admin-bundle 3.69'
2119
                .' and will throw an exception in 4.0. Use %s::hasChild() to know if the child exists.',
2120
                __METHOD__,
2121
                __CLASS__
2122
            ), E_USER_DEPRECATED);
2123
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare AdminInterface as return type
2124
            // throw new \LogicException(sprintf(
2125
            //    'Admin "%s" has no child for the code %s.',
2126
            //    static::class,
2127
            //    $code
2128
            // ));
2129
2130
            return null;
2131
        }
2132
2133
        return $this->children[$code];
2134
    }
2135
2136
    public function setParent(AdminInterface $parent)
2137
    {
2138
        $this->parent = $parent;
2139
    }
2140
2141
    public function getParent()
2142
    {
2143
        if (!$this->isChild()) {
2144
            @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...
2145
                'Calling %s() when there is no parent is deprecated since sonata-project/admin-bundle 3.66 and will throw an exception in 4.0. '.
2146
                'Use %s::isChild() to know if there is a parent.',
2147
                __METHOD__,
2148
                __CLASS__
2149
            ), E_USER_DEPRECATED);
2150
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare AdminInterface as return type
2151
            // throw new \LogicException(sprintf(
2152
            //    'Admin "%s" has no parent.',
2153
            //    static::class
2154
            // ));
2155
2156
            return null;
2157
        }
2158
2159
        return $this->parent;
2160
    }
2161
2162
    final public function getRootAncestor()
2163
    {
2164
        $parent = $this;
2165
2166
        while ($parent->isChild()) {
2167
            $parent = $parent->getParent();
2168
        }
2169
2170
        return $parent;
2171
    }
2172
2173
    final public function getChildDepth()
2174
    {
2175
        $parent = $this;
2176
        $depth = 0;
2177
2178
        while ($parent->isChild()) {
2179
            $parent = $parent->getParent();
2180
            ++$depth;
2181
        }
2182
2183
        return $depth;
2184
    }
2185
2186
    final public function getCurrentLeafChildAdmin()
2187
    {
2188
        $child = $this->getCurrentChildAdmin();
2189
2190
        if (null === $child) {
2191
            return null;
2192
        }
2193
2194
        for ($c = $child; null !== $c; $c = $child->getCurrentChildAdmin()) {
2195
            $child = $c;
2196
        }
2197
2198
        return $child;
2199
    }
2200
2201
    public function isChild()
2202
    {
2203
        return $this->parent instanceof AdminInterface;
2204
    }
2205
2206
    /**
2207
     * Returns true if the admin has children, false otherwise.
2208
     *
2209
     * @return bool if the admin has children
2210
     */
2211
    public function hasChildren()
2212
    {
2213
        return \count($this->children) > 0;
2214
    }
2215
2216
    public function setUniqid($uniqid)
2217
    {
2218
        $this->uniqid = $uniqid;
2219
    }
2220
2221
    public function getUniqid()
2222
    {
2223
        if (!$this->uniqid) {
2224
            $this->uniqid = 's'.uniqid();
2225
        }
2226
2227
        return $this->uniqid;
2228
    }
2229
2230
    /**
2231
     * Returns the classname label.
2232
     *
2233
     * @return string the classname label
2234
     */
2235
    public function getClassnameLabel()
2236
    {
2237
        return $this->classnameLabel;
2238
    }
2239
2240
    public function getPersistentParameters()
2241
    {
2242
        $parameters = [];
2243
2244
        foreach ($this->getExtensions() as $extension) {
2245
            $params = $extension->getPersistentParameters($this);
2246
2247
            if (!\is_array($params)) {
2248
                throw new \RuntimeException(sprintf('The %s::getPersistentParameters must return an array', \get_class($extension)));
2249
            }
2250
2251
            $parameters = array_merge($parameters, $params);
2252
        }
2253
2254
        return $parameters;
2255
    }
2256
2257
    /**
2258
     * @param string $name
2259
     *
2260
     * @return mixed|null
2261
     */
2262
    public function getPersistentParameter($name)
2263
    {
2264
        $parameters = $this->getPersistentParameters();
2265
2266
        return $parameters[$name] ?? null;
2267
    }
2268
2269
    public function getBreadcrumbs($action)
2270
    {
2271
        @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...
2272
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
2273
            ' Use Sonata\AdminBundle\Admin\BreadcrumbsBuilder::getBreadcrumbs instead.',
2274
            E_USER_DEPRECATED
2275
        );
2276
2277
        return $this->getBreadcrumbsBuilder()->getBreadcrumbs($this, $action);
2278
    }
2279
2280
    /**
2281
     * Generates the breadcrumbs array.
2282
     *
2283
     * Note: the method will be called by the top admin instance (parent => child)
2284
     *
2285
     * @param string $action
2286
     *
2287
     * @return array
2288
     */
2289
    public function buildBreadcrumbs($action, ?ItemInterface $menu = null)
2290
    {
2291
        @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...
2292
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.',
2293
            E_USER_DEPRECATED
2294
        );
2295
2296
        if (isset($this->breadcrumbs[$action])) {
2297
            return $this->breadcrumbs[$action];
2298
        }
2299
2300
        return $this->breadcrumbs[$action] = $this->getBreadcrumbsBuilder()
2301
            ->buildBreadcrumbs($this, $action, $menu);
2302
    }
2303
2304
    /**
2305
     * NEXT_MAJOR : remove this method.
2306
     *
2307
     * @return BreadcrumbsBuilderInterface
2308
     */
2309
    final public function getBreadcrumbsBuilder()
2310
    {
2311
        @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...
2312
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
2313
            ' Use the sonata.admin.breadcrumbs_builder service instead.',
2314
            E_USER_DEPRECATED
2315
        );
2316
        if (null === $this->breadcrumbsBuilder) {
2317
            $this->breadcrumbsBuilder = new BreadcrumbsBuilder(
2318
                $this->getConfigurationPool()->getContainer()->getParameter('sonata.admin.configuration.breadcrumbs')
2319
            );
2320
        }
2321
2322
        return $this->breadcrumbsBuilder;
2323
    }
2324
2325
    /**
2326
     * NEXT_MAJOR : remove this method.
2327
     *
2328
     * @return AbstractAdmin
2329
     */
2330
    final public function setBreadcrumbsBuilder(BreadcrumbsBuilderInterface $value)
2331
    {
2332
        @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...
2333
            'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
2334
            ' Use the sonata.admin.breadcrumbs_builder service instead.',
2335
            E_USER_DEPRECATED
2336
        );
2337
        $this->breadcrumbsBuilder = $value;
2338
2339
        return $this;
2340
    }
2341
2342
    public function setCurrentChild($currentChild)
2343
    {
2344
        $this->currentChild = $currentChild;
2345
    }
2346
2347
    /**
2348
     * NEXT_MAJOR: Remove this method.
2349
     *
2350
     * @deprecated since sonata-project/admin-bundle 3.65, to be removed in 4.0
2351
     */
2352
    public function getCurrentChild()
2353
    {
2354
        @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...
2355
            sprintf(
2356
                'The %s() method is deprecated since version 3.65 and will be removed in 4.0. Use %s::isCurrentChild() instead.',
2357
                __METHOD__,
2358
                __CLASS__
2359
            ),
2360
            E_USER_DEPRECATED
2361
        );
2362
2363
        return $this->currentChild;
2364
    }
2365
2366
    public function isCurrentChild(): bool
2367
    {
2368
        return $this->currentChild;
2369
    }
2370
2371
    /**
2372
     * Returns the current child admin instance.
2373
     *
2374
     * @return AdminInterface|null the current child admin instance
2375
     */
2376
    public function getCurrentChildAdmin()
2377
    {
2378
        foreach ($this->children as $children) {
2379
            if ($children->isCurrentChild()) {
2380
                return $children;
2381
            }
2382
        }
2383
2384
        return null;
2385
    }
2386
2387
    public function trans($id, array $parameters = [], $domain = null, $locale = null)
2388
    {
2389
        @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...
2390
            'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2391
            E_USER_DEPRECATED
2392
        );
2393
2394
        $domain = $domain ?: $this->getTranslationDomain();
2395
2396
        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...
2397
    }
2398
2399
    /**
2400
     * Translate a message id.
2401
     *
2402
     * NEXT_MAJOR: remove this method
2403
     *
2404
     * @param string      $id
2405
     * @param int         $count
2406
     * @param string|null $domain
2407
     * @param string|null $locale
2408
     *
2409
     * @return string the translated string
2410
     *
2411
     * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
2412
     */
2413
    public function transChoice($id, $count, array $parameters = [], $domain = null, $locale = null)
2414
    {
2415
        @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...
2416
            'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2417
            E_USER_DEPRECATED
2418
        );
2419
2420
        $domain = $domain ?: $this->getTranslationDomain();
2421
2422
        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...
2423
    }
2424
2425
    public function setTranslationDomain($translationDomain)
2426
    {
2427
        $this->translationDomain = $translationDomain;
2428
    }
2429
2430
    public function getTranslationDomain()
2431
    {
2432
        return $this->translationDomain;
2433
    }
2434
2435
    /**
2436
     * {@inheritdoc}
2437
     *
2438
     * NEXT_MAJOR: remove this method
2439
     *
2440
     * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
2441
     */
2442
    public function setTranslator(TranslatorInterface $translator)
2443
    {
2444
        $args = \func_get_args();
2445
        if (isset($args[1]) && $args[1]) {
2446
            @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...
2447
                'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2448
                E_USER_DEPRECATED
2449
            );
2450
        }
2451
2452
        $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...
2453
    }
2454
2455
    /**
2456
     * {@inheritdoc}
2457
     *
2458
     * NEXT_MAJOR: remove this method
2459
     *
2460
     * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
2461
     */
2462
    public function getTranslator()
2463
    {
2464
        @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...
2465
            'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
2466
            E_USER_DEPRECATED
2467
        );
2468
2469
        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...
2470
    }
2471
2472
    public function getTranslationLabel($label, $context = '', $type = '')
2473
    {
2474
        return $this->getLabelTranslatorStrategy()->getLabel($label, $context, $type);
2475
    }
2476
2477
    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...
2478
    {
2479
        $this->request = $request;
2480
2481
        foreach ($this->getChildren() as $children) {
2482
            $children->setRequest($request);
2483
        }
2484
    }
2485
2486
    public function getRequest()
2487
    {
2488
        if (!$this->request) {
2489
            // NEXT_MAJOR: Throw \LogicException instead.
2490
            throw new \RuntimeException('The Request object has not been set');
2491
        }
2492
2493
        return $this->request;
2494
    }
2495
2496
    public function hasRequest()
2497
    {
2498
        return null !== $this->request;
2499
    }
2500
2501
    public function setFormContractor(FormContractorInterface $formBuilder)
2502
    {
2503
        $this->formContractor = $formBuilder;
2504
    }
2505
2506
    /**
2507
     * @return FormContractorInterface
2508
     */
2509
    public function getFormContractor()
2510
    {
2511
        return $this->formContractor;
2512
    }
2513
2514
    public function setDatagridBuilder(DatagridBuilderInterface $datagridBuilder)
2515
    {
2516
        $this->datagridBuilder = $datagridBuilder;
2517
    }
2518
2519
    public function getDatagridBuilder()
2520
    {
2521
        return $this->datagridBuilder;
2522
    }
2523
2524
    public function setListBuilder(ListBuilderInterface $listBuilder)
2525
    {
2526
        $this->listBuilder = $listBuilder;
2527
    }
2528
2529
    public function getListBuilder()
2530
    {
2531
        return $this->listBuilder;
2532
    }
2533
2534
    public function setShowBuilder(ShowBuilderInterface $showBuilder)
2535
    {
2536
        $this->showBuilder = $showBuilder;
2537
    }
2538
2539
    /**
2540
     * @return ShowBuilderInterface
2541
     */
2542
    public function getShowBuilder()
2543
    {
2544
        return $this->showBuilder;
2545
    }
2546
2547
    public function setConfigurationPool(Pool $configurationPool)
2548
    {
2549
        $this->configurationPool = $configurationPool;
2550
    }
2551
2552
    /**
2553
     * @return Pool
2554
     */
2555
    public function getConfigurationPool()
2556
    {
2557
        return $this->configurationPool;
2558
    }
2559
2560
    public function setRouteGenerator(RouteGeneratorInterface $routeGenerator)
2561
    {
2562
        $this->routeGenerator = $routeGenerator;
2563
    }
2564
2565
    /**
2566
     * @return RouteGeneratorInterface
2567
     */
2568
    public function getRouteGenerator()
2569
    {
2570
        return $this->routeGenerator;
2571
    }
2572
2573
    public function getCode()
2574
    {
2575
        return $this->code;
2576
    }
2577
2578
    /**
2579
     * NEXT_MAJOR: Remove this function.
2580
     *
2581
     * @deprecated This method is deprecated since sonata-project/admin-bundle 3.24 and will be removed in 4.0
2582
     *
2583
     * @param string $baseCodeRoute
2584
     */
2585
    public function setBaseCodeRoute($baseCodeRoute)
2586
    {
2587
        @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...
2588
            'The '.__METHOD__.' is deprecated since 3.24 and will be removed in 4.0.',
2589
            E_USER_DEPRECATED
2590
        );
2591
2592
        $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...
2593
    }
2594
2595
    public function getBaseCodeRoute()
2596
    {
2597
        // NEXT_MAJOR: Uncomment the following lines.
2598
        // if ($this->isChild()) {
2599
        //     return $this->getParent()->getBaseCodeRoute().'|'.$this->getCode();
2600
        // }
2601
        //
2602
        // return $this->getCode();
2603
2604
        // NEXT_MAJOR: Remove all the code below.
2605
        if ($this->isChild()) {
2606
            $parentCode = $this->getParent()->getCode();
2607
2608
            if ($this->getParent()->isChild()) {
2609
                $parentCode = $this->getParent()->getBaseCodeRoute();
2610
            }
2611
2612
            return $parentCode.'|'.$this->getCode();
2613
        }
2614
2615
        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...
2616
    }
2617
2618
    public function getModelManager()
2619
    {
2620
        return $this->modelManager;
2621
    }
2622
2623
    public function setModelManager(ModelManagerInterface $modelManager)
2624
    {
2625
        $this->modelManager = $modelManager;
2626
    }
2627
2628
    public function getManagerType()
2629
    {
2630
        return $this->managerType;
2631
    }
2632
2633
    /**
2634
     * @param string $type
2635
     */
2636
    public function setManagerType($type)
2637
    {
2638
        $this->managerType = $type;
2639
    }
2640
2641
    public function getObjectIdentifier()
2642
    {
2643
        return $this->getCode();
2644
    }
2645
2646
    /**
2647
     * Set the roles and permissions per role.
2648
     */
2649
    public function setSecurityInformation(array $information)
2650
    {
2651
        $this->securityInformation = $information;
2652
    }
2653
2654
    public function getSecurityInformation()
2655
    {
2656
        return $this->securityInformation;
2657
    }
2658
2659
    /**
2660
     * Return the list of permissions the user should have in order to display the admin.
2661
     *
2662
     * @param string $context
2663
     *
2664
     * @return array
2665
     */
2666
    public function getPermissionsShow($context)
2667
    {
2668
        switch ($context) {
2669
            case self::CONTEXT_DASHBOARD:
2670
            case self::CONTEXT_MENU:
2671
            default:
2672
                return ['LIST'];
2673
        }
2674
    }
2675
2676
    public function showIn($context)
2677
    {
2678
        switch ($context) {
2679
            case self::CONTEXT_DASHBOARD:
2680
            case self::CONTEXT_MENU:
2681
            default:
2682
                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...
2683
        }
2684
    }
2685
2686
    public function createObjectSecurity($object)
2687
    {
2688
        $this->getSecurityHandler()->createObjectSecurity($this, $object);
2689
    }
2690
2691
    public function setSecurityHandler(SecurityHandlerInterface $securityHandler)
2692
    {
2693
        $this->securityHandler = $securityHandler;
2694
    }
2695
2696
    public function getSecurityHandler()
2697
    {
2698
        return $this->securityHandler;
2699
    }
2700
2701
    public function isGranted($name, $object = null)
2702
    {
2703
        $objectRef = $object ? '/'.spl_object_hash($object).'#'.$this->id($object) : '';
2704
        $key = md5(json_encode($name).$objectRef);
2705
2706
        if (!\array_key_exists($key, $this->cacheIsGranted)) {
2707
            $this->cacheIsGranted[$key] = $this->securityHandler->isGranted($this, $name, $object ?: $this);
2708
        }
2709
2710
        return $this->cacheIsGranted[$key];
2711
    }
2712
2713
    public function getUrlSafeIdentifier($model)
2714
    {
2715
        return $this->getModelManager()->getUrlSafeIdentifier($model);
2716
    }
2717
2718
    public function getNormalizedIdentifier($model)
2719
    {
2720
        return $this->getModelManager()->getNormalizedIdentifier($model);
2721
    }
2722
2723
    public function id($model)
2724
    {
2725
        return $this->getNormalizedIdentifier($model);
2726
    }
2727
2728
    public function setValidator($validator)
2729
    {
2730
        // NEXT_MAJOR: Move ValidatorInterface check to method signature
2731
        if (!$validator instanceof ValidatorInterface) {
2732
            throw new \InvalidArgumentException(
2733
                'Argument 1 must be an instance of Symfony\Component\Validator\Validator\ValidatorInterface'
2734
            );
2735
        }
2736
2737
        $this->validator = $validator;
2738
    }
2739
2740
    public function getValidator()
2741
    {
2742
        return $this->validator;
2743
    }
2744
2745
    public function getShow()
2746
    {
2747
        $this->buildShow();
2748
2749
        return $this->show;
2750
    }
2751
2752
    public function setFormTheme(array $formTheme)
2753
    {
2754
        $this->formTheme = $formTheme;
2755
    }
2756
2757
    public function getFormTheme()
2758
    {
2759
        return $this->formTheme;
2760
    }
2761
2762
    public function setFilterTheme(array $filterTheme)
2763
    {
2764
        $this->filterTheme = $filterTheme;
2765
    }
2766
2767
    public function getFilterTheme()
2768
    {
2769
        return $this->filterTheme;
2770
    }
2771
2772
    public function addExtension(AdminExtensionInterface $extension)
2773
    {
2774
        $this->extensions[] = $extension;
2775
    }
2776
2777
    public function getExtensions()
2778
    {
2779
        return $this->extensions;
2780
    }
2781
2782
    public function setMenuFactory(FactoryInterface $menuFactory)
2783
    {
2784
        $this->menuFactory = $menuFactory;
2785
    }
2786
2787
    public function getMenuFactory()
2788
    {
2789
        return $this->menuFactory;
2790
    }
2791
2792
    public function setRouteBuilder(RouteBuilderInterface $routeBuilder)
2793
    {
2794
        $this->routeBuilder = $routeBuilder;
2795
    }
2796
2797
    public function getRouteBuilder()
2798
    {
2799
        return $this->routeBuilder;
2800
    }
2801
2802
    public function toString($object)
2803
    {
2804
        if (!\is_object($object)) {
2805
            return '';
2806
        }
2807
2808
        if (method_exists($object, '__toString') && null !== $object->__toString()) {
2809
            return (string) $object;
2810
        }
2811
2812
        return sprintf('%s:%s', ClassUtils::getClass($object), spl_object_hash($object));
2813
    }
2814
2815
    public function setLabelTranslatorStrategy(LabelTranslatorStrategyInterface $labelTranslatorStrategy)
2816
    {
2817
        $this->labelTranslatorStrategy = $labelTranslatorStrategy;
2818
    }
2819
2820
    public function getLabelTranslatorStrategy()
2821
    {
2822
        return $this->labelTranslatorStrategy;
2823
    }
2824
2825
    public function supportsPreviewMode()
2826
    {
2827
        return $this->supportsPreviewMode;
2828
    }
2829
2830
    /**
2831
     * NEXT_MAJOR: Remove this.
2832
     *
2833
     * @deprecated since sonata-project/admin-bundle 3.67, to be removed in 4.0.
2834
     *
2835
     * Set custom per page options.
2836
     */
2837
    public function setPerPageOptions(array $options)
2838
    {
2839
        @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...
2840
            'The method %s is deprecated since sonata-project/admin-bundle 3.67 and will be removed in 4.0.',
2841
            __METHOD__
2842
        ), E_USER_DEPRECATED);
2843
2844
        $this->perPageOptions = $options;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$perPageOptions has been deprecated with message: since sonata-project/admin-bundle 3.67.

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

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

Loading history...
2845
    }
2846
2847
    /**
2848
     * Returns predefined per page options.
2849
     *
2850
     * @return array
2851
     */
2852
    public function getPerPageOptions()
2853
    {
2854
        // NEXT_MAJOR: Remove this line and uncomment the following
2855
        return $this->perPageOptions;
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$perPageOptions has been deprecated with message: since sonata-project/admin-bundle 3.67.

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

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

Loading history...
2856
//        $perPageOptions = $this->getModelManager()->getDefaultPerPageOptions($this->class);
2857
//        $perPageOptions[] = $this->getMaxPerPage();
2858
//
2859
//        $perPageOptions = array_unique($perPageOptions);
2860
//        sort($perPageOptions);
2861
//
2862
//        return $perPageOptions;
2863
    }
2864
2865
    /**
2866
     * Set pager type.
2867
     *
2868
     * @param string $pagerType
2869
     */
2870
    public function setPagerType($pagerType)
2871
    {
2872
        $this->pagerType = $pagerType;
2873
    }
2874
2875
    /**
2876
     * Get pager type.
2877
     *
2878
     * @return string
2879
     */
2880
    public function getPagerType()
2881
    {
2882
        return $this->pagerType;
2883
    }
2884
2885
    /**
2886
     * Returns true if the per page value is allowed, false otherwise.
2887
     *
2888
     * @param int $perPage
2889
     *
2890
     * @return bool
2891
     */
2892
    public function determinedPerPageValue($perPage)
2893
    {
2894
        return \in_array($perPage, $this->getPerPageOptions(), true);
2895
    }
2896
2897
    public function isAclEnabled()
2898
    {
2899
        return $this->getSecurityHandler() instanceof AclSecurityHandlerInterface;
2900
    }
2901
2902
    public function getObjectMetadata($object)
2903
    {
2904
        return new Metadata($this->toString($object));
2905
    }
2906
2907
    public function getListModes()
2908
    {
2909
        return $this->listModes;
2910
    }
2911
2912
    public function setListMode($mode)
2913
    {
2914
        if (!$this->hasRequest()) {
2915
            throw new \RuntimeException(sprintf('No request attached to the current admin: %s', $this->getCode()));
2916
        }
2917
2918
        $this->getRequest()->getSession()->set(sprintf('%s.list_mode', $this->getCode()), $mode);
2919
    }
2920
2921
    public function getListMode()
2922
    {
2923
        if (!$this->hasRequest()) {
2924
            return 'list';
2925
        }
2926
2927
        return $this->getRequest()->getSession()->get(sprintf('%s.list_mode', $this->getCode()), 'list');
2928
    }
2929
2930
    public function getAccessMapping()
2931
    {
2932
        return $this->accessMapping;
2933
    }
2934
2935
    public function checkAccess($action, $object = null)
2936
    {
2937
        $access = $this->getAccess();
2938
2939
        if (!\array_key_exists($action, $access)) {
2940
            throw new \InvalidArgumentException(sprintf(
2941
                'Action "%s" could not be found in access mapping.'
2942
                .' Please make sure your action is defined into your admin class accessMapping property.',
2943
                $action
2944
            ));
2945
        }
2946
2947
        if (!\is_array($access[$action])) {
2948
            $access[$action] = [$access[$action]];
2949
        }
2950
2951
        foreach ($access[$action] as $role) {
2952
            if (false === $this->isGranted($role, $object)) {
2953
                throw new AccessDeniedException(sprintf('Access Denied to the action %s and role %s', $action, $role));
2954
            }
2955
        }
2956
    }
2957
2958
    /**
2959
     * Hook to handle access authorization, without throw Exception.
2960
     *
2961
     * @param string $action
2962
     * @param object $object
2963
     *
2964
     * @return bool
2965
     */
2966
    public function hasAccess($action, $object = null)
2967
    {
2968
        $access = $this->getAccess();
2969
2970
        if (!\array_key_exists($action, $access)) {
2971
            return false;
2972
        }
2973
2974
        if (!\is_array($access[$action])) {
2975
            $access[$action] = [$access[$action]];
2976
        }
2977
2978
        foreach ($access[$action] as $role) {
2979
            if (false === $this->isGranted($role, $object)) {
2980
                return false;
2981
            }
2982
        }
2983
2984
        return true;
2985
    }
2986
2987
    /**
2988
     * @param string      $action
2989
     * @param object|null $object
2990
     *
2991
     * @return array
2992
     */
2993
    public function configureActionButtons($action, $object = null)
2994
    {
2995
        $list = [];
2996
2997
        if (\in_array($action, ['tree', 'show', 'edit', 'delete', 'list', 'batch'], true)
2998
            && $this->hasAccess('create')
2999
            && $this->hasRoute('create')
3000
        ) {
3001
            $list['create'] = [
3002
                // NEXT_MAJOR: Remove this line and use commented line below it instead
3003
                '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...
3004
//                'template' => $this->getTemplateRegistry()->getTemplate('button_create'),
3005
            ];
3006
        }
3007
3008
        if (\in_array($action, ['show', 'delete', 'acl', 'history'], true)
3009
            && $this->canAccessObject('edit', $object)
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 2993 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...
3010
            && $this->hasRoute('edit')
3011
        ) {
3012
            $list['edit'] = [
3013
                // NEXT_MAJOR: Remove this line and use commented line below it instead
3014
                '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...
3015
                //'template' => $this->getTemplateRegistry()->getTemplate('button_edit'),
3016
            ];
3017
        }
3018
3019
        if (\in_array($action, ['show', 'edit', 'acl'], true)
3020
            && $this->canAccessObject('history', $object)
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 2993 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...
3021
            && $this->hasRoute('history')
3022
        ) {
3023
            $list['history'] = [
3024
                // NEXT_MAJOR: Remove this line and use commented line below it instead
3025
                '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...
3026
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_history'),
3027
            ];
3028
        }
3029
3030
        if (\in_array($action, ['edit', 'history'], true)
3031
            && $this->isAclEnabled()
3032
            && $this->canAccessObject('acl', $object)
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 2993 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...
3033
            && $this->hasRoute('acl')
3034
        ) {
3035
            $list['acl'] = [
3036
                // NEXT_MAJOR: Remove this line and use commented line below it instead
3037
                '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...
3038
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_acl'),
3039
            ];
3040
        }
3041
3042
        if (\in_array($action, ['edit', 'history', 'acl'], true)
3043
            && $this->canAccessObject('show', $object)
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 2993 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...
3044
            && \count($this->getShow()) > 0
3045
            && $this->hasRoute('show')
3046
        ) {
3047
            $list['show'] = [
3048
                // NEXT_MAJOR: Remove this line and use commented line below it instead
3049
                '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...
3050
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_show'),
3051
            ];
3052
        }
3053
3054
        if (\in_array($action, ['show', 'edit', 'delete', 'acl', 'batch'], true)
3055
            && $this->hasAccess('list')
3056
            && $this->hasRoute('list')
3057
        ) {
3058
            $list['list'] = [
3059
                // NEXT_MAJOR: Remove this line and use commented line below it instead
3060
                '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...
3061
                // 'template' => $this->getTemplateRegistry()->getTemplate('button_list'),
3062
            ];
3063
        }
3064
3065
        return $list;
3066
    }
3067
3068
    /**
3069
     * @param string $action
3070
     * @param object $object
3071
     *
3072
     * @return array
3073
     */
3074
    public function getActionButtons($action, $object = null)
3075
    {
3076
        $list = $this->configureActionButtons($action, $object);
3077
3078
        foreach ($this->getExtensions() as $extension) {
3079
            // NEXT_MAJOR: remove method check
3080
            if (method_exists($extension, 'configureActionButtons')) {
3081
                $list = $extension->configureActionButtons($this, $list, $action, $object);
3082
            }
3083
        }
3084
3085
        return $list;
3086
    }
3087
3088
    /**
3089
     * Get the list of actions that can be accessed directly from the dashboard.
3090
     *
3091
     * @return array
3092
     */
3093
    public function getDashboardActions()
3094
    {
3095
        $actions = [];
3096
3097
        if ($this->hasRoute('create') && $this->hasAccess('create')) {
3098
            $actions['create'] = [
3099
                'label' => 'link_add',
3100
                'translation_domain' => 'SonataAdminBundle',
3101
                // NEXT_MAJOR: Remove this line and use commented line below it instead
3102
                '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...
3103
                // 'template' => $this->getTemplateRegistry()->getTemplate('action_create'),
3104
                'url' => $this->generateUrl('create'),
3105
                'icon' => 'plus-circle',
3106
            ];
3107
        }
3108
3109
        if ($this->hasRoute('list') && $this->hasAccess('list')) {
3110
            $actions['list'] = [
3111
                'label' => 'link_list',
3112
                'translation_domain' => 'SonataAdminBundle',
3113
                'url' => $this->generateUrl('list'),
3114
                'icon' => 'list',
3115
            ];
3116
        }
3117
3118
        return $actions;
3119
    }
3120
3121
    /**
3122
     * Setting to true will enable mosaic button for the admin screen.
3123
     * Setting to false will hide mosaic button for the admin screen.
3124
     *
3125
     * @param bool $isShown
3126
     */
3127
    final public function showMosaicButton($isShown)
3128
    {
3129
        if ($isShown) {
3130
            $this->listModes['mosaic'] = ['class' => static::MOSAIC_ICON_CLASS];
3131
        } else {
3132
            unset($this->listModes['mosaic']);
3133
        }
3134
    }
3135
3136
    /**
3137
     * @param object $object
3138
     */
3139
    final public function getSearchResultLink($object)
3140
    {
3141
        foreach ($this->searchResultActions as $action) {
3142
            if ($this->hasRoute($action) && $this->hasAccess($action, $object)) {
3143
                return $this->generateObjectUrl($action, $object);
3144
            }
3145
        }
3146
3147
        return null;
3148
    }
3149
3150
    /**
3151
     * Checks if a filter type is set to a default value.
3152
     *
3153
     * @param string $name
3154
     *
3155
     * @return bool
3156
     */
3157
    final public function isDefaultFilter($name)
3158
    {
3159
        $filter = $this->getFilterParameters();
3160
        $default = $this->getDefaultFilterValues();
3161
3162
        if (!\array_key_exists($name, $filter) || !\array_key_exists($name, $default)) {
3163
            return false;
3164
        }
3165
3166
        return $filter[$name] === $default[$name];
3167
    }
3168
3169
    /**
3170
     * Check object existence and access, without throw Exception.
3171
     *
3172
     * @param string $action
3173
     * @param object $object
3174
     *
3175
     * @return bool
3176
     */
3177
    public function canAccessObject($action, $object)
3178
    {
3179
        return $object && $this->id($object) && $this->hasAccess($action, $object);
3180
    }
3181
3182
    protected function configureQuery(ProxyQueryInterface $query): ProxyQueryInterface
3183
    {
3184
        return $query;
3185
    }
3186
3187
    /**
3188
     * @return MutableTemplateRegistryInterface
3189
     */
3190
    final protected function getTemplateRegistry()
3191
    {
3192
        return $this->templateRegistry;
3193
    }
3194
3195
    /**
3196
     * Returns a list of default sort values.
3197
     *
3198
     * @return array{_page?: int, _per_page?: int, _sort_by?: string, _sort_order?: string}
0 ignored issues
show
Documentation introduced by
The doc-type array{_page?: could not be parsed: Unknown type name "array{_page?:" at position 0. (view supported doc-types)

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

Loading history...
3199
     */
3200
    final protected function getDefaultSortValues(): array
3201
    {
3202
        $defaultSortValues = [];
3203
3204
        $this->configureDefaultSortValues($defaultSortValues);
3205
3206
        foreach ($this->getExtensions() as $extension) {
3207
            // NEXT_MAJOR: remove method check
3208
            if (method_exists($extension, 'configureDefaultSortValues')) {
3209
                $extension->configureDefaultSortValues($this, $defaultSortValues);
3210
            }
3211
        }
3212
3213
        return $defaultSortValues;
3214
    }
3215
3216
    /**
3217
     * Returns a list of default filters.
3218
     *
3219
     * @return array
3220
     */
3221
    final protected function getDefaultFilterValues()
3222
    {
3223
        $defaultFilterValues = [];
3224
3225
        $this->configureDefaultFilterValues($defaultFilterValues);
3226
3227
        foreach ($this->getExtensions() as $extension) {
3228
            // NEXT_MAJOR: remove method check
3229
            if (method_exists($extension, 'configureDefaultFilterValues')) {
3230
                $extension->configureDefaultFilterValues($this, $defaultFilterValues);
3231
            }
3232
        }
3233
3234
        return $defaultFilterValues;
3235
    }
3236
3237
    protected function configureFormFields(FormMapper $form)
3238
    {
3239
    }
3240
3241
    protected function configureListFields(ListMapper $list)
3242
    {
3243
    }
3244
3245
    protected function configureDatagridFilters(DatagridMapper $filter)
3246
    {
3247
    }
3248
3249
    protected function configureShowFields(ShowMapper $show)
3250
    {
3251
    }
3252
3253
    protected function configureRoutes(RouteCollection $collection)
3254
    {
3255
    }
3256
3257
    /**
3258
     * Allows you to customize batch actions.
3259
     *
3260
     * @param array $actions List of actions
3261
     *
3262
     * @return array
3263
     */
3264
    protected function configureBatchActions($actions)
3265
    {
3266
        return $actions;
3267
    }
3268
3269
    /**
3270
     * NEXT_MAJOR: remove this method.
3271
     *
3272
     * @deprecated Use configureTabMenu instead
3273
     */
3274
    protected function configureSideMenu(ItemInterface $menu, $action, ?AdminInterface $childAdmin = null)
0 ignored issues
show
Unused Code introduced by
The parameter $menu is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $action is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $childAdmin is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
3275
    {
3276
    }
3277
3278
    /**
3279
     * Configures the tab menu in your admin.
3280
     *
3281
     * @param string $action
3282
     */
3283
    protected function configureTabMenu(ItemInterface $menu, $action, ?AdminInterface $childAdmin = null)
3284
    {
3285
        // Use configureSideMenu not to mess with previous overrides
3286
        // NEXT_MAJOR: remove this line
3287
        $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...
3288
    }
3289
3290
    /**
3291
     * build the view FieldDescription array.
3292
     */
3293
    protected function buildShow()
3294
    {
3295
        if ($this->loaded['show']) {
3296
            return;
3297
        }
3298
3299
        $this->loaded['show'] = true;
3300
3301
        $this->show = $this->getShowBuilder()->getBaseList();
3302
        $mapper = new ShowMapper($this->getShowBuilder(), $this->show, $this);
3303
3304
        $this->configureShowFields($mapper);
3305
3306
        foreach ($this->getExtensions() as $extension) {
3307
            $extension->configureShowFields($mapper);
3308
        }
3309
    }
3310
3311
    /**
3312
     * build the list FieldDescription array.
3313
     */
3314
    protected function buildList()
3315
    {
3316
        if ($this->loaded['list']) {
3317
            return;
3318
        }
3319
3320
        $this->loaded['list'] = true;
3321
3322
        $this->list = $this->getListBuilder()->getBaseList();
3323
        $mapper = new ListMapper($this->getListBuilder(), $this->list, $this);
3324
3325
        if (\count($this->getBatchActions()) > 0 && $this->hasRequest() && !$this->getRequest()->isXmlHttpRequest()) {
3326
            $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance(
3327
                $this->getClass(),
3328
                'batch',
3329
                [
3330
                    'label' => 'batch',
3331
                    'code' => '_batch',
3332
                    'sortable' => false,
3333
                    'virtual_field' => true,
3334
                ]
3335
            );
3336
3337
            $fieldDescription->setAdmin($this);
3338
            // NEXT_MAJOR: Remove this line and use commented line below it instead
3339
            $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...
3340
            // $fieldDescription->setTemplate($this->getTemplateRegistry()->getTemplate('batch'));
3341
3342
            $mapper->add($fieldDescription, ListMapper::TYPE_BATCH);
3343
        }
3344
3345
        $this->configureListFields($mapper);
3346
3347
        foreach ($this->getExtensions() as $extension) {
3348
            $extension->configureListFields($mapper);
3349
        }
3350
3351
        if ($this->hasRequest() && $this->getRequest()->isXmlHttpRequest()) {
3352
            $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance(
3353
                $this->getClass(),
3354
                'select',
3355
                [
3356
                    'label' => false,
3357
                    'code' => '_select',
3358
                    'sortable' => false,
3359
                    'virtual_field' => false,
3360
                ]
3361
            );
3362
3363
            $fieldDescription->setAdmin($this);
3364
            // NEXT_MAJOR: Remove this line and use commented line below it instead
3365
            $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...
3366
            // $fieldDescription->setTemplate($this->getTemplateRegistry()->getTemplate('select'));
3367
3368
            $mapper->add($fieldDescription, ListMapper::TYPE_SELECT);
3369
        }
3370
    }
3371
3372
    /**
3373
     * Build the form FieldDescription collection.
3374
     */
3375
    protected function buildForm()
3376
    {
3377
        if ($this->loaded['form']) {
3378
            return;
3379
        }
3380
3381
        $this->loaded['form'] = true;
3382
3383
        $formBuilder = $this->getFormBuilder();
3384
        $formBuilder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
3385
            $this->preValidate($event->getData());
3386
        }, 100);
3387
3388
        $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...
3389
    }
3390
3391
    /**
3392
     * Gets the subclass corresponding to the given name.
3393
     *
3394
     * @param string $name The name of the sub class
3395
     *
3396
     * @return string the subclass
3397
     */
3398
    protected function getSubClass($name)
3399
    {
3400
        if ($this->hasSubClass($name)) {
3401
            return $this->subClasses[$name];
3402
        }
3403
3404
        // NEXT_MAJOR: Throw \LogicException instead.
3405
        throw new \RuntimeException(sprintf(
3406
            'Unable to find the subclass `%s` for admin `%s`',
3407
            $name,
3408
            static::class
3409
        ));
3410
    }
3411
3412
    /**
3413
     * Attach the inline validator to the model metadata, this must be done once per admin.
3414
     */
3415
    protected function attachInlineValidator()
3416
    {
3417
        $admin = $this;
3418
3419
        // add the custom inline validation option
3420
        $metadata = $this->validator->getMetadataFor($this->getClass());
3421
3422
        $metadata->addConstraint(new InlineConstraint([
3423
            'service' => $this,
3424
            'method' => static function (ErrorElement $errorElement, $object) use ($admin) {
3425
                /* @var \Sonata\AdminBundle\Admin\AdminInterface $admin */
3426
3427
                // This avoid the main validation to be cascaded to children
3428
                // The problem occurs when a model Page has a collection of Page as property
3429
                if ($admin->hasSubject() && spl_object_hash($object) !== spl_object_hash($admin->getSubject())) {
3430
                    return;
3431
                }
3432
3433
                $admin->validate($errorElement, $object);
3434
3435
                foreach ($admin->getExtensions() as $extension) {
3436
                    $extension->validate($admin, $errorElement, $object);
3437
                }
3438
            },
3439
            'serializingWarning' => true,
3440
        ]));
3441
    }
3442
3443
    /**
3444
     * NEXT_MAJOR: Remove this function.
3445
     *
3446
     * @deprecated since sonata-project/admin-bundle 3.67, to be removed in 4.0.
3447
     *
3448
     * Predefine per page options.
3449
     */
3450
    protected function predefinePerPageOptions()
3451
    {
3452
        array_unshift($this->perPageOptions, $this->maxPerPage);
0 ignored issues
show
Deprecated Code introduced by
The property Sonata\AdminBundle\Admin...tAdmin::$perPageOptions has been deprecated with message: since sonata-project/admin-bundle 3.67.

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

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

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

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

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

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

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

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

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

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

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

Loading history...
3455
    }
3456
3457
    /**
3458
     * Return list routes with permissions name.
3459
     *
3460
     * @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...
3461
     */
3462
    protected function getAccess()
3463
    {
3464
        $access = array_merge([
3465
            'acl' => 'MASTER',
3466
            'export' => 'EXPORT',
3467
            'historyCompareRevisions' => 'EDIT',
3468
            'historyViewRevision' => 'EDIT',
3469
            'history' => 'EDIT',
3470
            'edit' => 'EDIT',
3471
            'show' => 'VIEW',
3472
            'create' => 'CREATE',
3473
            'delete' => 'DELETE',
3474
            'batchDelete' => 'DELETE',
3475
            'list' => 'LIST',
3476
        ], $this->getAccessMapping());
3477
3478
        foreach ($this->extensions as $extension) {
3479
            // NEXT_MAJOR: remove method check
3480
            if (method_exists($extension, 'getAccessMapping')) {
3481
                $access = array_merge($access, $extension->getAccessMapping($this));
3482
            }
3483
        }
3484
3485
        return $access;
3486
    }
3487
3488
    /**
3489
     * Configures a list of default filters.
3490
     */
3491
    protected function configureDefaultFilterValues(array &$filterValues)
3492
    {
3493
    }
3494
3495
    /**
3496
     * Configures a list of default sort values.
3497
     *
3498
     * Example:
3499
     *   $sortValues['_sort_by'] = 'foo'
3500
     *   $sortValues['_sort_order'] = 'DESC'
3501
     */
3502
    protected function configureDefaultSortValues(array &$sortValues)
0 ignored issues
show
Unused Code introduced by
The parameter $sortValues is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
3503
    {
3504
    }
3505
3506
    /**
3507
     * Build all the related urls to the current admin.
3508
     */
3509
    private function buildRoutes(): void
3510
    {
3511
        if ($this->loaded['routes']) {
3512
            return;
3513
        }
3514
3515
        $this->loaded['routes'] = true;
3516
3517
        $this->routes = new RouteCollection(
3518
            $this->getBaseCodeRoute(),
3519
            $this->getBaseRouteName(),
3520
            $this->getBaseRoutePattern(),
3521
            $this->getBaseControllerName()
3522
        );
3523
3524
        $this->routeBuilder->build($this, $this->routes);
3525
3526
        $this->configureRoutes($this->routes);
3527
3528
        foreach ($this->getExtensions() as $extension) {
3529
            $extension->configureRoutes($this, $this->routes);
3530
        }
3531
    }
3532
}
3533
3534
class_exists(\Sonata\Form\Validator\ErrorElement::class);
3535