Completed
Push — master ( 57b7f2...4f95ff )
by Grégoire
28:13 queued 01:46
created

AbstractAdmin   F

Complexity

Total Complexity 409

Size/Duplication

Total Lines 2726
Duplicated Lines 0 %

Coupling/Cohesion

Components 4
Dependencies 39

Importance

Changes 0
Metric Value
wmc 409
lcom 4
cbo 39
dl 0
loc 2726
rs 0.8
c 0
b 0
f 0

214 Methods

Rating   Name   Duplication   Size   Complexity  
A hasListFieldDescription() 0 6 2
A addListFieldDescription() 0 4 1
A removeListFieldDescription() 0 4 1
A getFormBuilder() 0 13 1
A setTranslationDomain() 0 4 1
A getTranslationDomain() 0 4 1
A setTranslator() 0 12 3
A __construct() 0 6 1
A getExportFormats() 0 6 1
A getExportFields() 0 12 3
A getDataSourceIterator() 0 26 3
A validate() 0 3 1
A initialize() 0 8 2
A update() 0 20 4
A create() 0 22 4
A delete() 0 15 3
A preValidate() 0 3 1
A preUpdate() 0 3 1
A postUpdate() 0 3 1
A prePersist() 0 3 1
A postPersist() 0 3 1
A preRemove() 0 3 1
A postRemove() 0 3 1
A preBatchAction() 0 3 1
B getFilterParameters() 0 50 11
A getParentAssociationMapping() 0 18 3
A addParentAssociationMapping() 0 4 1
B getBaseRoutePattern() 0 48 8
B getBaseRouteName() 0 49 8
B getClass() 0 23 6
A getSubClasses() 0 4 1
A setSubClasses() 0 4 1
A hasSubClass() 0 4 1
A hasActiveSubClass() 0 8 3
A getActiveSubClass() 0 11 2
A getActiveSubclassCode() 0 20 3
B getBatchActions() 0 30 7
A getRoutes() 0 6 1
A getRouterIdParameter() 0 4 1
A getIdParameter() 0 10 2
A hasRoute() 0 8 2
A isCurrentRoute() 0 21 4
A generateObjectUrl() 0 6 1
A generateUrl() 0 4 1
A generateMenuUrl() 0 4 1
A setTemplateRegistry() 0 4 1
A setTemplates() 0 4 1
A setTemplate() 0 4 1
A getNewInstance() 0 12 2
A defineFormBuilder() 0 19 3
A attachAdminClass() 0 28 5
A getObject() 0 9 2
A getForm() 0 6 1
A getList() 0 6 1
A createQuery() 0 11 2
A getDatagrid() 0 6 1
A buildTabMenu() 0 27 4
A getSideMenu() 0 10 2
A getRootCode() 0 4 1
A getRoot() 0 8 2
A setBaseControllerName() 0 4 1
A getBaseControllerName() 0 4 1
A setLabel() 0 4 1
A getLabel() 0 4 1
A setFilterPersister() 0 4 1
A getMaxPerPage() 0 6 1
A setMaxPageLinks() 0 4 1
A getMaxPageLinks() 0 4 1
A getFormGroups() 0 4 1
A setFormGroups() 0 4 1
A removeFieldFromFormGroup() 0 10 3
A reorderFormGroup() 0 6 1
A getFormTabs() 0 4 1
A setFormTabs() 0 4 1
A getShowTabs() 0 4 1
A setShowTabs() 0 4 1
A getShowGroups() 0 4 1
A setShowGroups() 0 4 1
A reorderShowGroup() 0 6 1
A setParentFieldDescription() 0 4 1
A getParentFieldDescription() 0 11 2
A hasParentFieldDescription() 0 4 1
A setSubject() 0 13 3
A getSubject() 0 11 2
A hasSubject() 0 12 5
A getFormFieldDescriptions() 0 6 1
A getFormFieldDescription() 0 14 2
A hasFormFieldDescription() 0 6 2
A addFormFieldDescription() 0 4 1
A removeFormFieldDescription() 0 4 1
A getShowFieldDescriptions() 0 6 1
A getShowFieldDescription() 0 14 2
A hasShowFieldDescription() 0 6 1
A addShowFieldDescription() 0 4 1
A removeShowFieldDescription() 0 4 1
A getListFieldDescriptions() 0 6 1
A getListFieldDescription() 0 14 2
A getFilterFieldDescription() 0 14 2
A hasFilterFieldDescription() 0 6 2
A addFilterFieldDescription() 0 4 1
A removeFilterFieldDescription() 0 4 1
A getFilterFieldDescriptions() 0 6 1
A addChild() 0 20 4
A hasChild() 0 4 1
A getChildren() 0 4 1
A getChild() 0 12 2
A setParent() 0 4 1
A getParent() 0 11 2
A getRootAncestor() 0 10 2
A getChildDepth() 0 12 2
A getCurrentLeafChildAdmin() 0 14 3
A isChild() 0 4 1
A hasChildren() 0 4 1
A setUniqid() 0 4 1
A getUniqid() 0 8 2
A getClassnameLabel() 0 4 1
A getPersistentParameters() 0 12 2
A getPersistentParameter() 0 6 1
A setCurrentChild() 0 4 1
A isCurrentChild() 0 4 1
A getCurrentChildAdmin() 0 10 3
A getTranslationLabel() 0 4 1
A setRequest() 0 8 2
A getRequest() 0 8 2
A hasRequest() 0 4 1
A setFormContractor() 0 4 1
A getFormContractor() 0 4 1
A setDatagridBuilder() 0 4 1
A getDatagridBuilder() 0 4 1
A setListBuilder() 0 4 1
A getListBuilder() 0 4 1
A setShowBuilder() 0 4 1
A getShowBuilder() 0 4 1
A setConfigurationPool() 0 4 1
A getConfigurationPool() 0 4 1
A setRouteGenerator() 0 4 1
A getRouteGenerator() 0 4 1
A getCode() 0 4 1
A getBaseCodeRoute() 0 8 2
A getModelManager() 0 4 1
A setModelManager() 0 4 1
A getManagerType() 0 4 1
A setManagerType() 0 4 1
A getObjectIdentifier() 0 4 1
A setSecurityInformation() 0 4 1
A getSecurityInformation() 0 4 1
A getPermissionsShow() 0 9 3
A showIn() 0 9 3
A createObjectSecurity() 0 4 1
A setSecurityHandler() 0 4 1
A getSecurityHandler() 0 4 1
A isGranted() 0 11 4
A getUrlSafeIdentifier() 0 4 1
A getNormalizedIdentifier() 0 4 1
A id() 0 4 1
A setValidator() 0 4 1
A getValidator() 0 4 1
A getShow() 0 6 1
A setFormTheme() 0 4 1
A getFormTheme() 0 4 1
A setFilterTheme() 0 4 1
A getFilterTheme() 0 4 1
A addExtension() 0 4 1
A getExtensions() 0 4 1
A setMenuFactory() 0 4 1
A getMenuFactory() 0 4 1
A setRouteBuilder() 0 4 1
A getRouteBuilder() 0 4 1
A toString() 0 12 4
A setLabelTranslatorStrategy() 0 4 1
A getLabelTranslatorStrategy() 0 4 1
A supportsPreviewMode() 0 4 1
A getPerPageOptions() 0 10 1
A setPagerType() 0 4 1
A getPagerType() 0 4 1
A determinedPerPageValue() 0 4 1
A isAclEnabled() 0 4 1
A getObjectMetadata() 0 4 1
A getListModes() 0 4 1
A setListMode() 0 8 2
A getListMode() 0 8 2
A getAccessMapping() 0 4 1
A checkAccess() 0 22 5
A hasAccess() 0 20 5
F getActionButtons() 0 68 22
A getDashboardActions() 0 25 5
A showMosaicButton() 0 8 2
A getSearchResultLink() 0 10 4
A canAccessObject() 0 4 3
A configureActionButtons() 0 4 1
A configure() 0 3 1
A configureQuery() 0 4 1
A urlize() 0 4 1
A getTemplateRegistry() 0 4 1
A getDefaultSortValues() 0 12 2
A getDefaultFilterValues() 0 12 2
A configureFormFields() 0 3 1
A configureListFields() 0 3 1
A configureDatagridFilters() 0 3 1
A configureShowFields() 0 3 1
A configureRoutes() 0 3 1
A configureBatchActions() 0 4 1
A configureTabMenu() 0 3 1
A buildShow() 0 17 3
B buildList() 0 53 8
A buildForm() 0 15 2
A getSubClass() 0 8 2
A attachInlineValidator() 0 37 5
A getAccess() 0 22 2
A configureDefaultFilterValues() 0 3 1
A configureDefaultSortValues() 0 3 1
B appendParentObject() 0 28 8
B buildDatagrid() 0 54 9
A buildRoutes() 0 23 3

How to fix   Complexity   

Complex Class

Complex classes like AbstractAdmin often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractAdmin, and based on these observations, apply Extract Interface, too.

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1151
        }
1152
1153
        return $object;
1154
    }
1155
1156
    public function getForm(): ?FormInterface
1157
    {
1158
        $this->buildForm();
1159
1160
        return $this->form;
1161
    }
1162
1163
    public function getList(): ?FieldDescriptionCollection
1164
    {
1165
        $this->buildList();
1166
1167
        return $this->list;
1168
    }
1169
1170
    final public function createQuery(): ProxyQueryInterface
1171
    {
1172
        $query = $this->getModelManager()->createQuery($this->getClass());
1173
1174
        $query = $this->configureQuery($query);
1175
        foreach ($this->extensions as $extension) {
1176
            $extension->configureQuery($this, $query);
1177
        }
1178
1179
        return $query;
1180
    }
1181
1182
    public function getDatagrid(): DatagridInterface
1183
    {
1184
        $this->buildDatagrid();
1185
1186
        return $this->datagrid;
1187
    }
1188
1189
    public function buildTabMenu(string $action, ?AdminInterface $childAdmin = null): ItemInterface
1190
    {
1191
        if ($this->loaded['tab_menu']) {
1192
            return $this->menu;
1193
        }
1194
1195
        $this->loaded['tab_menu'] = true;
1196
1197
        $menu = $this->menuFactory->createItem('root');
1198
        $menu->setChildrenAttribute('class', 'nav navbar-nav');
1199
        $menu->setExtra('translation_domain', $this->translationDomain);
1200
1201
        // Prevents BC break with KnpMenuBundle v1.x
1202
        if (method_exists($menu, 'setCurrentUri')) {
1203
            $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...
1204
        }
1205
1206
        $this->configureTabMenu($menu, $action, $childAdmin);
1207
1208
        foreach ($this->getExtensions() as $extension) {
1209
            $extension->configureTabMenu($this, $menu, $action, $childAdmin);
1210
        }
1211
1212
        $this->menu = $menu;
1213
1214
        return $this->menu;
1215
    }
1216
1217
    public function getSideMenu(string $action, ?AdminInterface $childAdmin = null): ItemInterface
1218
    {
1219
        if ($this->isChild()) {
1220
            return $this->getParent()->getSideMenu($action, $this);
1221
        }
1222
1223
        $this->buildTabMenu($action, $childAdmin);
1224
1225
        return $this->menu;
1226
    }
1227
1228
    public function getRootCode(): string
1229
    {
1230
        return $this->getRoot()->getCode();
1231
    }
1232
1233
    public function getRoot(): AdminInterface
1234
    {
1235
        if (!$this->hasParentFieldDescription()) {
1236
            return $this;
1237
        }
1238
1239
        return $this->getParentFieldDescription()->getAdmin()->getRoot();
1240
    }
1241
1242
    public function setBaseControllerName(string $baseControllerName): void
1243
    {
1244
        $this->baseControllerName = $baseControllerName;
1245
    }
1246
1247
    public function getBaseControllerName(): string
1248
    {
1249
        return $this->baseControllerName;
1250
    }
1251
1252
    public function setLabel(?string $label): void
1253
    {
1254
        $this->label = $label;
1255
    }
1256
1257
    public function getLabel(): ?string
1258
    {
1259
        return $this->label;
1260
    }
1261
1262
    public function setFilterPersister(?FilterPersisterInterface $filterPersister = null): void
1263
    {
1264
        $this->filterPersister = $filterPersister;
1265
    }
1266
1267
    public function getMaxPerPage(): int
1268
    {
1269
        $sortValues = $this->getModelManager()->getDefaultSortValues($this->class);
1270
1271
        return $sortValues['_per_page'] ?? 25;
1272
    }
1273
1274
    public function setMaxPageLinks(int $maxPageLinks): void
1275
    {
1276
        $this->maxPageLinks = $maxPageLinks;
1277
    }
1278
1279
    public function getMaxPageLinks(): int
1280
    {
1281
        return $this->maxPageLinks;
1282
    }
1283
1284
    public function getFormGroups(): array
1285
    {
1286
        return $this->formGroups;
1287
    }
1288
1289
    public function setFormGroups(array $formGroups): void
1290
    {
1291
        $this->formGroups = $formGroups;
1292
    }
1293
1294
    public function removeFieldFromFormGroup(string $key): void
1295
    {
1296
        foreach ($this->formGroups as $name => $formGroup) {
1297
            unset($this->formGroups[$name]['fields'][$key]);
1298
1299
            if (empty($this->formGroups[$name]['fields'])) {
1300
                unset($this->formGroups[$name]);
1301
            }
1302
        }
1303
    }
1304
1305
    public function reorderFormGroup(string $group, array $keys): void
1306
    {
1307
        $formGroups = $this->getFormGroups();
1308
        $formGroups[$group]['fields'] = array_merge(array_flip($keys), $formGroups[$group]['fields']);
1309
        $this->setFormGroups($formGroups);
1310
    }
1311
1312
    public function getFormTabs(): array
1313
    {
1314
        return $this->formTabs;
1315
    }
1316
1317
    public function setFormTabs(array $formTabs): void
1318
    {
1319
        $this->formTabs = $formTabs;
1320
    }
1321
1322
    public function getShowTabs(): array
1323
    {
1324
        return $this->showTabs;
1325
    }
1326
1327
    public function setShowTabs(array $showTabs): void
1328
    {
1329
        $this->showTabs = $showTabs;
1330
    }
1331
1332
    public function getShowGroups(): array
1333
    {
1334
        return $this->showGroups;
1335
    }
1336
1337
    public function setShowGroups(array $showGroups): void
1338
    {
1339
        $this->showGroups = $showGroups;
1340
    }
1341
1342
    public function reorderShowGroup(string $group, array $keys): void
1343
    {
1344
        $showGroups = $this->getShowGroups();
1345
        $showGroups[$group]['fields'] = array_merge(array_flip($keys), $showGroups[$group]['fields']);
1346
        $this->setShowGroups($showGroups);
1347
    }
1348
1349
    public function setParentFieldDescription(FieldDescriptionInterface $parentFieldDescription): void
1350
    {
1351
        $this->parentFieldDescription = $parentFieldDescription;
1352
    }
1353
1354
    public function getParentFieldDescription(): FieldDescriptionInterface
1355
    {
1356
        if (!$this->hasParentFieldDescription()) {
1357
            throw new \LogicException(sprintf(
1358
                'Admin "%s" has no parent field description.',
1359
                static::class
1360
            ));
1361
        }
1362
1363
        return $this->parentFieldDescription;
1364
    }
1365
1366
    public function hasParentFieldDescription(): bool
1367
    {
1368
        return $this->parentFieldDescription instanceof FieldDescriptionInterface;
1369
    }
1370
1371
    public function setSubject(?object $subject): void
1372
    {
1373
        if (\is_object($subject) && !is_a($subject, $this->getClass(), true)) {
1374
            throw new \LogicException(sprintf(
1375
                'Admin "%s" does not allow this subject: %s, use the one register with this admin class %s',
1376
                static::class,
1377
                \get_class($subject),
1378
                $this->getClass()
1379
            ));
1380
        }
1381
1382
        $this->subject = $subject;
1383
    }
1384
1385
    public function getSubject(): object
1386
    {
1387
        if (!$this->hasSubject()) {
1388
            throw new \LogicException(sprintf(
1389
                'Admin "%s" has no subject.',
1390
                static::class
1391
            ));
1392
        }
1393
1394
        return $this->subject;
1395
    }
1396
1397
    public function hasSubject(): bool
1398
    {
1399
        if (null === $this->subject && $this->hasRequest() && !$this->hasParentFieldDescription()) {
1400
            $id = $this->request->get($this->getIdParameter());
1401
1402
            if (null !== $id) {
1403
                $this->subject = $this->getObject($id);
1404
            }
1405
        }
1406
1407
        return null !== $this->subject;
1408
    }
1409
1410
    public function getFormFieldDescriptions(): array
1411
    {
1412
        $this->buildForm();
1413
1414
        return $this->formFieldDescriptions;
1415
    }
1416
1417
    public function getFormFieldDescription(string $name): FieldDescriptionInterface
1418
    {
1419
        $this->buildForm();
1420
1421
        if (!$this->hasFormFieldDescription($name)) {
1422
            throw new \LogicException(sprintf(
1423
                'Admin "%s" has no form field description for the field %s.',
1424
                static::class,
1425
                $name
1426
            ));
1427
        }
1428
1429
        return $this->formFieldDescriptions[$name];
1430
    }
1431
1432
    /**
1433
     * Returns true if the admin has a FieldDescription with the given $name.
1434
     */
1435
    public function hasFormFieldDescription(string $name): bool
1436
    {
1437
        $this->buildForm();
1438
1439
        return \array_key_exists($name, $this->formFieldDescriptions) ? true : false;
1440
    }
1441
1442
    public function addFormFieldDescription(string $name, FieldDescriptionInterface $fieldDescription): void
1443
    {
1444
        $this->formFieldDescriptions[$name] = $fieldDescription;
1445
    }
1446
1447
    /**
1448
     * remove a FieldDescription.
1449
     */
1450
    public function removeFormFieldDescription(string $name): void
1451
    {
1452
        unset($this->formFieldDescriptions[$name]);
1453
    }
1454
1455
    /**
1456
     * build and return the collection of form FieldDescription.
1457
     *
1458
     * @return FieldDescriptionInterface[] collection of form FieldDescription
1459
     */
1460
    public function getShowFieldDescriptions(): array
1461
    {
1462
        $this->buildShow();
1463
1464
        return $this->showFieldDescriptions;
1465
    }
1466
1467
    /**
1468
     * Returns the form FieldDescription with the given $name.
1469
     */
1470
    public function getShowFieldDescription(string $name): FieldDescriptionInterface
1471
    {
1472
        $this->buildShow();
1473
1474
        if (!$this->hasShowFieldDescription($name)) {
1475
            throw new \LogicException(sprintf(
1476
                'Admin "%s" has no show field description for the field %s.',
1477
                static::class,
1478
                $name
1479
            ));
1480
        }
1481
1482
        return $this->showFieldDescriptions[$name];
1483
    }
1484
1485
    public function hasShowFieldDescription(string $name): bool
1486
    {
1487
        $this->buildShow();
1488
1489
        return \array_key_exists($name, $this->showFieldDescriptions);
1490
    }
1491
1492
    public function addShowFieldDescription(string $name, FieldDescriptionInterface $fieldDescription): void
1493
    {
1494
        $this->showFieldDescriptions[$name] = $fieldDescription;
1495
    }
1496
1497
    public function removeShowFieldDescription(string $name): void
1498
    {
1499
        unset($this->showFieldDescriptions[$name]);
1500
    }
1501
1502
    public function getListFieldDescriptions(): array
1503
    {
1504
        $this->buildList();
1505
1506
        return $this->listFieldDescriptions;
1507
    }
1508
1509
    public function getListFieldDescription(string $name): FieldDescriptionInterface
1510
    {
1511
        $this->buildList();
1512
1513
        if (!$this->hasListFieldDescription($name)) {
1514
            throw new \LogicException(sprintf(
1515
                'Admin "%s" has no list field description for %s.',
1516
                static::class,
1517
                $name
1518
            ));
1519
        }
1520
1521
        return $this->listFieldDescriptions[$name];
1522
    }
1523
1524
    public function hasListFieldDescription(string $name): bool
1525
    {
1526
        $this->buildList();
1527
1528
        return \array_key_exists($name, $this->listFieldDescriptions) ? true : false;
1529
    }
1530
1531
    public function addListFieldDescription(string $name, FieldDescriptionInterface $fieldDescription): void
1532
    {
1533
        $this->listFieldDescriptions[$name] = $fieldDescription;
1534
    }
1535
1536
    public function removeListFieldDescription(string $name): void
1537
    {
1538
        unset($this->listFieldDescriptions[$name]);
1539
    }
1540
1541
    public function getFilterFieldDescription(string $name): FieldDescriptionInterface
1542
    {
1543
        $this->buildDatagrid();
1544
1545
        if (!$this->hasFilterFieldDescription($name)) {
1546
            throw new \LogicException(sprintf(
1547
                'Admin "%s" has no filter field description for the field %s.',
1548
                static::class,
1549
                $name
1550
            ));
1551
        }
1552
1553
        return $this->filterFieldDescriptions[$name];
1554
    }
1555
1556
    public function hasFilterFieldDescription(string $name): bool
1557
    {
1558
        $this->buildDatagrid();
1559
1560
        return \array_key_exists($name, $this->filterFieldDescriptions) ? true : false;
1561
    }
1562
1563
    public function addFilterFieldDescription(string $name, FieldDescriptionInterface $fieldDescription): void
1564
    {
1565
        $this->filterFieldDescriptions[$name] = $fieldDescription;
1566
    }
1567
1568
    public function removeFilterFieldDescription(string $name): void
1569
    {
1570
        unset($this->filterFieldDescriptions[$name]);
1571
    }
1572
1573
    public function getFilterFieldDescriptions(): array
1574
    {
1575
        $this->buildDatagrid();
1576
1577
        return $this->filterFieldDescriptions;
1578
    }
1579
1580
    public function addChild(AdminInterface $child, string $field): void
1581
    {
1582
        $parentAdmin = $this;
1583
        while ($parentAdmin->isChild() && $parentAdmin->getCode() !== $child->getCode()) {
1584
            $parentAdmin = $parentAdmin->getParent();
1585
        }
1586
1587
        if ($parentAdmin->getCode() === $child->getCode()) {
1588
            throw new \RuntimeException(sprintf(
1589
                'Circular reference detected! The child admin `%s` is already in the parent tree of the `%s` admin.',
1590
                $child->getCode(),
1591
                $this->getCode()
1592
            ));
1593
        }
1594
1595
        $this->children[$child->getCode()] = $child;
1596
1597
        $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...
1598
        $child->addParentAssociationMapping($this->getCode(), $field);
1599
    }
1600
1601
    public function hasChild(string $code): bool
1602
    {
1603
        return isset($this->children[$code]);
1604
    }
1605
1606
    public function getChildren(): array
1607
    {
1608
        return $this->children;
1609
    }
1610
1611
    public function getChild(string $code): AdminInterface
1612
    {
1613
        if (!$this->hasChild($code)) {
1614
            throw new \LogicException(sprintf(
1615
                'Admin "%s" has no child for the code %s.',
1616
                static::class,
1617
                $code
1618
            ));
1619
        }
1620
1621
        return $this->children[$code];
1622
    }
1623
1624
    public function setParent(AdminInterface $parent): void
1625
    {
1626
        $this->parent = $parent;
1627
    }
1628
1629
    public function getParent(): AdminInterface
1630
    {
1631
        if (!$this->isChild()) {
1632
            throw new \LogicException(sprintf(
1633
                'Admin "%s" has no parent.',
1634
                static::class
1635
            ));
1636
        }
1637
1638
        return $this->parent;
1639
    }
1640
1641
    final public function getRootAncestor(): AdminInterface
1642
    {
1643
        $parent = $this;
1644
1645
        while ($parent->isChild()) {
1646
            $parent = $parent->getParent();
1647
        }
1648
1649
        return $parent;
1650
    }
1651
1652
    final public function getChildDepth(): int
1653
    {
1654
        $parent = $this;
1655
        $depth = 0;
1656
1657
        while ($parent->isChild()) {
1658
            $parent = $parent->getParent();
1659
            ++$depth;
1660
        }
1661
1662
        return $depth;
1663
    }
1664
1665
    final public function getCurrentLeafChildAdmin(): ?AdminInterface
1666
    {
1667
        $child = $this->getCurrentChildAdmin();
1668
1669
        if (null === $child) {
1670
            return null;
1671
        }
1672
1673
        for ($c = $child; null !== $c; $c = $child->getCurrentChildAdmin()) {
1674
            $child = $c;
1675
        }
1676
1677
        return $child;
1678
    }
1679
1680
    public function isChild(): bool
1681
    {
1682
        return $this->parent instanceof AdminInterface;
1683
    }
1684
1685
    /**
1686
     * Returns true if the admin has children, false otherwise.
1687
     */
1688
    public function hasChildren(): bool
1689
    {
1690
        return \count($this->children) > 0;
1691
    }
1692
1693
    public function setUniqid(string $uniqid): void
1694
    {
1695
        $this->uniqid = $uniqid;
1696
    }
1697
1698
    public function getUniqid(): string
1699
    {
1700
        if (!$this->uniqid) {
1701
            $this->uniqid = sprintf('s%s', uniqid());
1702
        }
1703
1704
        return $this->uniqid;
1705
    }
1706
1707
    /**
1708
     * {@inheritdoc}
1709
     */
1710
    public function getClassnameLabel(): string
1711
    {
1712
        return $this->classnameLabel;
1713
    }
1714
1715
    public function getPersistentParameters(): array
1716
    {
1717
        $parameters = [];
1718
1719
        foreach ($this->getExtensions() as $extension) {
1720
            $params = $extension->getPersistentParameters($this);
1721
1722
            $parameters = array_merge($parameters, $params);
1723
        }
1724
1725
        return $parameters;
1726
    }
1727
1728
    /**
1729
     * {@inheritdoc}
1730
     */
1731
    public function getPersistentParameter(string $name)
1732
    {
1733
        $parameters = $this->getPersistentParameters();
1734
1735
        return $parameters[$name] ?? null;
1736
    }
1737
1738
    public function setCurrentChild(bool $currentChild): void
1739
    {
1740
        $this->currentChild = $currentChild;
1741
    }
1742
1743
    public function isCurrentChild(): bool
1744
    {
1745
        return $this->currentChild;
1746
    }
1747
1748
    /**
1749
     * Returns the current child admin instance.
1750
     *
1751
     * @return AdminInterface|null the current child admin instance
1752
     */
1753
    public function getCurrentChildAdmin(): ?AdminInterface
1754
    {
1755
        foreach ($this->children as $children) {
1756
            if ($children->isCurrentChild()) {
1757
                return $children;
1758
            }
1759
        }
1760
1761
        return null;
1762
    }
1763
1764
    public function setTranslationDomain(string $translationDomain): void
1765
    {
1766
        $this->translationDomain = $translationDomain;
1767
    }
1768
1769
    public function getTranslationDomain(): string
1770
    {
1771
        return $this->translationDomain;
1772
    }
1773
1774
    /**
1775
     * {@inheritdoc}
1776
     *
1777
     * NEXT_MAJOR: remove this method
1778
     *
1779
     * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
1780
     */
1781
    public function setTranslator(TranslatorInterface $translator): void
1782
    {
1783
        $args = \func_get_args();
1784
        if (isset($args[1]) && $args[1]) {
1785
            @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...
1786
                'The %s method is deprecated since version 3.9 and will be removed in 4.0.',
1787
                __METHOD__
1788
            ), E_USER_DEPRECATED);
1789
        }
1790
1791
        $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...
1792
    }
1793
1794
    public function getTranslationLabel(string $label, string $context = '', string $type = ''): string
1795
    {
1796
        return $this->getLabelTranslatorStrategy()->getLabel($label, $context, $type);
1797
    }
1798
1799
    public function setRequest(Request $request): void
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...
1800
    {
1801
        $this->request = $request;
1802
1803
        foreach ($this->getChildren() as $children) {
1804
            $children->setRequest($request);
1805
        }
1806
    }
1807
1808
    public function getRequest(): Request
1809
    {
1810
        if (!$this->request) {
1811
            throw new \LogicException('The Request object has not been set');
1812
        }
1813
1814
        return $this->request;
1815
    }
1816
1817
    public function hasRequest(): bool
1818
    {
1819
        return null !== $this->request;
1820
    }
1821
1822
    public function setFormContractor(FormContractorInterface $formBuilder): void
1823
    {
1824
        $this->formContractor = $formBuilder;
1825
    }
1826
1827
    public function getFormContractor(): ?FormContractorInterface
1828
    {
1829
        return $this->formContractor;
1830
    }
1831
1832
    public function setDatagridBuilder(DatagridBuilderInterface $datagridBuilder): void
1833
    {
1834
        $this->datagridBuilder = $datagridBuilder;
1835
    }
1836
1837
    public function getDatagridBuilder(): ?DatagridBuilderInterface
1838
    {
1839
        return $this->datagridBuilder;
1840
    }
1841
1842
    public function setListBuilder(ListBuilderInterface $listBuilder): void
1843
    {
1844
        $this->listBuilder = $listBuilder;
1845
    }
1846
1847
    public function getListBuilder(): ?ListBuilderInterface
1848
    {
1849
        return $this->listBuilder;
1850
    }
1851
1852
    public function setShowBuilder(?ShowBuilderInterface $showBuilder): void
1853
    {
1854
        $this->showBuilder = $showBuilder;
1855
    }
1856
1857
    public function getShowBuilder(): ?ShowBuilderInterface
1858
    {
1859
        return $this->showBuilder;
1860
    }
1861
1862
    public function setConfigurationPool(Pool $configurationPool): void
1863
    {
1864
        $this->configurationPool = $configurationPool;
1865
    }
1866
1867
    public function getConfigurationPool(): ?Pool
1868
    {
1869
        return $this->configurationPool;
1870
    }
1871
1872
    public function setRouteGenerator(RouteGeneratorInterface $routeGenerator): void
1873
    {
1874
        $this->routeGenerator = $routeGenerator;
1875
    }
1876
1877
    public function getRouteGenerator(): ?RouteGeneratorInterface
1878
    {
1879
        return $this->routeGenerator;
1880
    }
1881
1882
    public function getCode(): string
1883
    {
1884
        return $this->code;
1885
    }
1886
1887
    public function getBaseCodeRoute(): string
1888
    {
1889
        if ($this->isChild()) {
1890
            return $this->getParent()->getBaseCodeRoute().'|'.$this->getCode();
1891
        }
1892
1893
        return $this->getCode();
1894
    }
1895
1896
    public function getModelManager(): ?ModelManagerInterface
1897
    {
1898
        return $this->modelManager;
1899
    }
1900
1901
    public function setModelManager(?ModelManagerInterface $modelManager): void
1902
    {
1903
        $this->modelManager = $modelManager;
1904
    }
1905
1906
    public function getManagerType(): ?string
1907
    {
1908
        return $this->managerType;
1909
    }
1910
1911
    public function setManagerType(?string $type): void
1912
    {
1913
        $this->managerType = $type;
1914
    }
1915
1916
    public function getObjectIdentifier()
1917
    {
1918
        return $this->getCode();
1919
    }
1920
1921
    /**
1922
     * Set the roles and permissions per role.
1923
     */
1924
    public function setSecurityInformation(array $information): void
1925
    {
1926
        $this->securityInformation = $information;
1927
    }
1928
1929
    public function getSecurityInformation(): array
1930
    {
1931
        return $this->securityInformation;
1932
    }
1933
1934
    /**
1935
     * Return the list of permissions the user should have in order to display the admin.
1936
     */
1937
    public function getPermissionsShow(string $context): array
1938
    {
1939
        switch ($context) {
1940
            case self::CONTEXT_DASHBOARD:
1941
            case self::CONTEXT_MENU:
1942
            default:
1943
                return ['LIST'];
1944
        }
1945
    }
1946
1947
    public function showIn(string $context): bool
1948
    {
1949
        switch ($context) {
1950
            case self::CONTEXT_DASHBOARD:
1951
            case self::CONTEXT_MENU:
1952
            default:
1953
                return $this->isGranted($this->getPermissionsShow($context));
0 ignored issues
show
Documentation introduced by
$this->getPermissionsShow($context) is of type array, 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...
1954
        }
1955
    }
1956
1957
    public function createObjectSecurity(object $object): void
1958
    {
1959
        $this->getSecurityHandler()->createObjectSecurity($this, $object);
1960
    }
1961
1962
    public function setSecurityHandler(SecurityHandlerInterface $securityHandler): void
1963
    {
1964
        $this->securityHandler = $securityHandler;
1965
    }
1966
1967
    public function getSecurityHandler(): ?SecurityHandlerInterface
1968
    {
1969
        return $this->securityHandler;
1970
    }
1971
1972
    /**
1973
     * NEXT_MAJOR: Decide the type declaration for the $name argument, since it is
1974
     * passed as argument 1 for `SecurityHandlerInterface::isGranted()`, which
1975
     * accepts string and array.
1976
     */
1977
    public function isGranted($name, ?object $object = null): bool
1978
    {
1979
        $objectRef = $object ? sprintf('/%s#%s', spl_object_hash($object), $this->id($object)) : '';
1980
        $key = md5(json_encode($name).$objectRef);
1981
1982
        if (!\array_key_exists($key, $this->cacheIsGranted)) {
1983
            $this->cacheIsGranted[$key] = $this->securityHandler->isGranted($this, $name, $object ?: $this);
1984
        }
1985
1986
        return $this->cacheIsGranted[$key];
1987
    }
1988
1989
    /**
1990
     * NEXT_MAJOR: Decide the type declaration for the $model argument, since it is
1991
     * passed as argument 1 for `ModelManagerInterface::getUrlSafeIdentifier()`, which
1992
     * accepts null.
1993
     */
1994
    public function getUrlSafeIdentifier($model): ?string
1995
    {
1996
        return $this->getModelManager()->getUrlSafeIdentifier($model);
1997
    }
1998
1999
    /**
2000
     * NEXT_MAJOR: Decide the type declaration for the $model argument, since it is
2001
     * passed as argument 1 for `ModelManagerInterface::getNormalizedIdentifier()`, which
2002
     * accepts null.
2003
     */
2004
    public function getNormalizedIdentifier($model): ?string
2005
    {
2006
        return $this->getModelManager()->getNormalizedIdentifier($model);
2007
    }
2008
2009
    /**
2010
     * NEXT_MAJOR: Decide the type declaration for the $model argument, since it is
2011
     * passed as argument 1 for `ModelManagerInterface::getNormalizedIdentifier()`, which
2012
     * accepts null.
2013
     */
2014
    public function id($model): ?string
2015
    {
2016
        return $this->getNormalizedIdentifier($model);
2017
    }
2018
2019
    public function setValidator(ValidatorInterface $validator): void
2020
    {
2021
        $this->validator = $validator;
2022
    }
2023
2024
    public function getValidator(): ?ValidatorInterface
2025
    {
2026
        return $this->validator;
2027
    }
2028
2029
    public function getShow(): ?FieldDescriptionCollection
2030
    {
2031
        $this->buildShow();
2032
2033
        return $this->show;
2034
    }
2035
2036
    public function setFormTheme(array $formTheme): void
2037
    {
2038
        $this->formTheme = $formTheme;
2039
    }
2040
2041
    public function getFormTheme(): array
2042
    {
2043
        return $this->formTheme;
2044
    }
2045
2046
    public function setFilterTheme(array $filterTheme): void
2047
    {
2048
        $this->filterTheme = $filterTheme;
2049
    }
2050
2051
    public function getFilterTheme(): array
2052
    {
2053
        return $this->filterTheme;
2054
    }
2055
2056
    public function addExtension(AdminExtensionInterface $extension): void
2057
    {
2058
        $this->extensions[] = $extension;
2059
    }
2060
2061
    public function getExtensions(): array
2062
    {
2063
        return $this->extensions;
2064
    }
2065
2066
    public function setMenuFactory(FactoryInterface $menuFactory): void
2067
    {
2068
        $this->menuFactory = $menuFactory;
2069
    }
2070
2071
    public function getMenuFactory(): ?FactoryInterface
2072
    {
2073
        return $this->menuFactory;
2074
    }
2075
2076
    public function setRouteBuilder(RouteBuilderInterface $routeBuilder): void
2077
    {
2078
        $this->routeBuilder = $routeBuilder;
2079
    }
2080
2081
    public function getRouteBuilder(): ?RouteBuilderInterface
2082
    {
2083
        return $this->routeBuilder;
2084
    }
2085
2086
    /**
2087
     * NEXT_MAJOR: Decide the type declaration for the $object argument, since there
2088
     * are tests ensuring to accept null (`GetShortObjectDescriptionActionTest::testGetShortObjectDescriptionActionEmptyObjectIdAsJson()`).
2089
     */
2090
    public function toString($object): string
2091
    {
2092
        if (!\is_object($object)) {
2093
            return '';
2094
        }
2095
2096
        if (method_exists($object, '__toString') && null !== $object->__toString()) {
2097
            return (string) $object;
2098
        }
2099
2100
        return sprintf('%s:%s', ClassUtils::getClass($object), spl_object_hash($object));
2101
    }
2102
2103
    public function setLabelTranslatorStrategy(LabelTranslatorStrategyInterface $labelTranslatorStrategy): void
2104
    {
2105
        $this->labelTranslatorStrategy = $labelTranslatorStrategy;
2106
    }
2107
2108
    public function getLabelTranslatorStrategy(): ?LabelTranslatorStrategyInterface
2109
    {
2110
        return $this->labelTranslatorStrategy;
2111
    }
2112
2113
    public function supportsPreviewMode(): bool
2114
    {
2115
        return $this->supportsPreviewMode;
2116
    }
2117
2118
    /**
2119
     * Returns predefined per page options.
2120
     */
2121
    public function getPerPageOptions(): array
2122
    {
2123
        $perPageOptions = $this->getModelManager()->getDefaultPerPageOptions($this->class);
2124
        $perPageOptions[] = $this->getMaxPerPage();
2125
2126
        $perPageOptions = array_unique($perPageOptions);
2127
        sort($perPageOptions);
2128
2129
        return $perPageOptions;
2130
    }
2131
2132
    /**
2133
     * Set pager type.
2134
     */
2135
    public function setPagerType(string $pagerType): void
2136
    {
2137
        $this->pagerType = $pagerType;
2138
    }
2139
2140
    /**
2141
     * Get pager type.
2142
     */
2143
    public function getPagerType(): string
2144
    {
2145
        return $this->pagerType;
2146
    }
2147
2148
    /**
2149
     * Returns true if the per page value is allowed, false otherwise.
2150
     */
2151
    public function determinedPerPageValue(int $perPage): bool
2152
    {
2153
        return \in_array($perPage, $this->getPerPageOptions(), true);
2154
    }
2155
2156
    public function isAclEnabled(): bool
2157
    {
2158
        return $this->getSecurityHandler() instanceof AclSecurityHandlerInterface;
2159
    }
2160
2161
    /**
2162
     * NEXT_MAJOR: Decide the type declaration for the $object argument, since it is
2163
     * passed as argument 1 to `toString()` method, which currently accepts null.
2164
     */
2165
    public function getObjectMetadata($object): MetadataInterface
2166
    {
2167
        return new Metadata($this->toString($object));
2168
    }
2169
2170
    public function getListModes(): array
2171
    {
2172
        return $this->listModes;
2173
    }
2174
2175
    public function setListMode(string $mode): void
2176
    {
2177
        if (!$this->hasRequest()) {
2178
            throw new \RuntimeException(sprintf('No request attached to the current admin: %s', $this->getCode()));
2179
        }
2180
2181
        $this->getRequest()->getSession()->set(sprintf('%s.list_mode', $this->getCode()), $mode);
2182
    }
2183
2184
    public function getListMode(): string
2185
    {
2186
        if (!$this->hasRequest()) {
2187
            return 'list';
2188
        }
2189
2190
        return $this->getRequest()->getSession()->get(sprintf('%s.list_mode', $this->getCode()), 'list');
2191
    }
2192
2193
    public function getAccessMapping(): array
2194
    {
2195
        return $this->accessMapping;
2196
    }
2197
2198
    public function checkAccess(string $action, ?object $object = null): void
2199
    {
2200
        $access = $this->getAccess();
2201
2202
        if (!\array_key_exists($action, $access)) {
2203
            throw new \InvalidArgumentException(sprintf(
2204
                'Action "%s" could not be found in access mapping.'
2205
                .' Please make sure your action is defined into your admin class accessMapping property.',
2206
                $action
2207
            ));
2208
        }
2209
2210
        if (!\is_array($access[$action])) {
2211
            $access[$action] = [$access[$action]];
2212
        }
2213
2214
        foreach ($access[$action] as $role) {
2215
            if (false === $this->isGranted($role, $object)) {
2216
                throw new AccessDeniedException(sprintf('Access Denied to the action %s and role %s', $action, $role));
2217
            }
2218
        }
2219
    }
2220
2221
    /**
2222
     * {@inheritdoc}
2223
     */
2224
    public function hasAccess(string $action, ?object $object = null): bool
2225
    {
2226
        $access = $this->getAccess();
2227
2228
        if (!\array_key_exists($action, $access)) {
2229
            return false;
2230
        }
2231
2232
        if (!\is_array($access[$action])) {
2233
            $access[$action] = [$access[$action]];
2234
        }
2235
2236
        foreach ($access[$action] as $role) {
2237
            if (false === $this->isGranted($role, $object)) {
2238
                return false;
2239
            }
2240
        }
2241
2242
        return true;
2243
    }
2244
2245
    final public function getActionButtons(string $action, ?object $object = null): array
2246
    {
2247
        $buttonList = [];
2248
2249
        if (\in_array($action, ['tree', 'show', 'edit', 'delete', 'list', 'batch'], true)
2250
            && $this->hasAccess('create')
2251
            && $this->hasRoute('create')
2252
        ) {
2253
            $buttonList['create'] = [
2254
                'template' => $this->getTemplateRegistry()->getTemplate('button_create'),
2255
            ];
2256
        }
2257
2258
        if (\in_array($action, ['show', 'delete', 'acl', 'history'], true)
2259
            && $this->canAccessObject('edit', $object)
2260
            && $this->hasRoute('edit')
2261
        ) {
2262
            $buttonList['edit'] = [
2263
                'template' => $this->getTemplateRegistry()->getTemplate('button_edit'),
2264
            ];
2265
        }
2266
2267
        if (\in_array($action, ['show', 'edit', 'acl'], true)
2268
            && $this->canAccessObject('history', $object)
2269
            && $this->hasRoute('history')
2270
        ) {
2271
            $buttonList['history'] = [
2272
                'template' => $this->getTemplateRegistry()->getTemplate('button_history'),
2273
            ];
2274
        }
2275
2276
        if (\in_array($action, ['edit', 'history'], true)
2277
            && $this->isAclEnabled()
2278
            && $this->canAccessObject('acl', $object)
2279
            && $this->hasRoute('acl')
2280
        ) {
2281
            $buttonList['acl'] = [
2282
                'template' => $this->getTemplateRegistry()->getTemplate('button_acl'),
2283
            ];
2284
        }
2285
2286
        if (\in_array($action, ['edit', 'history', 'acl'], true)
2287
            && $this->canAccessObject('show', $object)
2288
            && \count($this->getShow()) > 0
2289
            && $this->hasRoute('show')
2290
        ) {
2291
            $buttonList['show'] = [
2292
                'template' => $this->getTemplateRegistry()->getTemplate('button_show'),
2293
            ];
2294
        }
2295
2296
        if (\in_array($action, ['show', 'edit', 'delete', 'acl', 'batch'], true)
2297
            && $this->hasAccess('list')
2298
            && $this->hasRoute('list')
2299
        ) {
2300
            $buttonList['list'] = [
2301
                'template' => $this->getTemplateRegistry()->getTemplate('button_list'),
2302
            ];
2303
        }
2304
2305
        $buttonList = $this->configureActionButtons($buttonList, $action, $object);
2306
2307
        foreach ($this->getExtensions() as $extension) {
2308
            $buttonList = $extension->configureActionButtons($this, $buttonList, $action, $object);
2309
        }
2310
2311
        return $buttonList;
2312
    }
2313
2314
    /**
2315
     * {@inheritdoc}
2316
     */
2317
    public function getDashboardActions(): array
2318
    {
2319
        $actions = [];
2320
2321
        if ($this->hasRoute('create') && $this->hasAccess('create')) {
2322
            $actions['create'] = [
2323
                'label' => 'link_add',
2324
                'translation_domain' => 'SonataAdminBundle',
2325
                'template' => $this->getTemplateRegistry()->getTemplate('action_create'),
2326
                'url' => $this->generateUrl('create'),
2327
                'icon' => 'plus-circle',
2328
            ];
2329
        }
2330
2331
        if ($this->hasRoute('list') && $this->hasAccess('list')) {
2332
            $actions['list'] = [
2333
                'label' => 'link_list',
2334
                'translation_domain' => 'SonataAdminBundle',
2335
                'url' => $this->generateUrl('list'),
2336
                'icon' => 'list',
2337
            ];
2338
        }
2339
2340
        return $actions;
2341
    }
2342
2343
    /**
2344
     * {@inheritdoc}
2345
     */
2346
    final public function showMosaicButton($isShown): void
2347
    {
2348
        if ($isShown) {
2349
            $this->listModes['mosaic'] = ['class' => static::MOSAIC_ICON_CLASS];
2350
        } else {
2351
            unset($this->listModes['mosaic']);
2352
        }
2353
    }
2354
2355
    final public function getSearchResultLink(object $object): ?string
2356
    {
2357
        foreach ($this->searchResultActions as $action) {
2358
            if ($this->hasRoute($action) && $this->hasAccess($action, $object)) {
2359
                return $this->generateObjectUrl($action, $object);
2360
            }
2361
        }
2362
2363
        return null;
2364
    }
2365
2366
    public function canAccessObject(string $action, ?object $object = null): bool
2367
    {
2368
        return $object && $this->id($object) && $this->hasAccess($action, $object);
2369
    }
2370
2371
    public function configureActionButtons(array $buttonList, string $action, ?object $object = null): array
2372
    {
2373
        return $buttonList;
2374
    }
2375
2376
    /**
2377
     * Hook to run after initialization.
2378
     */
2379
    protected function configure(): void
2380
    {
2381
    }
2382
2383
    protected function configureQuery(ProxyQueryInterface $query): ProxyQueryInterface
2384
    {
2385
        return $query;
2386
    }
2387
2388
    /**
2389
     * urlize the given word.
2390
     *
2391
     * @param string $sep the separator
2392
     */
2393
    final protected function urlize(string $word, string $sep = '_'): string
2394
    {
2395
        return strtolower(preg_replace('/[^a-z0-9_]/i', $sep.'$1', $word));
2396
    }
2397
2398
    final protected function getTemplateRegistry(): MutableTemplateRegistryInterface
2399
    {
2400
        return $this->templateRegistry;
2401
    }
2402
2403
    /**
2404
     * Returns a list of default sort values.
2405
     *
2406
     * @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...
2407
     */
2408
    final protected function getDefaultSortValues(): array
2409
    {
2410
        $defaultSortValues = [];
2411
2412
        $this->configureDefaultSortValues($defaultSortValues);
2413
2414
        foreach ($this->getExtensions() as $extension) {
2415
            $extension->configureDefaultSortValues($this, $defaultSortValues);
2416
        }
2417
2418
        return $defaultSortValues;
2419
    }
2420
2421
    /**
2422
     * Returns a list of default filters.
2423
     */
2424
    final protected function getDefaultFilterValues(): array
2425
    {
2426
        $defaultFilterValues = [];
2427
2428
        $this->configureDefaultFilterValues($defaultFilterValues);
2429
2430
        foreach ($this->getExtensions() as $extension) {
2431
            $extension->configureDefaultFilterValues($this, $defaultFilterValues);
2432
        }
2433
2434
        return $defaultFilterValues;
2435
    }
2436
2437
    protected function configureFormFields(FormMapper $form): void
2438
    {
2439
    }
2440
2441
    protected function configureListFields(ListMapper $list): void
2442
    {
2443
    }
2444
2445
    protected function configureDatagridFilters(DatagridMapper $filter): void
2446
    {
2447
    }
2448
2449
    protected function configureShowFields(ShowMapper $show): void
2450
    {
2451
    }
2452
2453
    protected function configureRoutes(RouteCollectionInterface $collection): void
2454
    {
2455
    }
2456
2457
    /**
2458
     * Allows you to customize batch actions.
2459
     *
2460
     * @param array $actions List of actions
2461
     */
2462
    protected function configureBatchActions(array $actions): array
2463
    {
2464
        return $actions;
2465
    }
2466
2467
    /**
2468
     * Configures the tab menu in your admin.
2469
     */
2470
    protected function configureTabMenu(ItemInterface $menu, string $action, ?AdminInterface $childAdmin = null): void
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...
2471
    {
2472
    }
2473
2474
    /**
2475
     * build the view FieldDescription array.
2476
     */
2477
    protected function buildShow(): void
2478
    {
2479
        if ($this->loaded['show']) {
2480
            return;
2481
        }
2482
2483
        $this->loaded['show'] = true;
2484
2485
        $this->show = $this->getShowBuilder()->getBaseList();
2486
        $mapper = new ShowMapper($this->getShowBuilder(), $this->show, $this);
2487
2488
        $this->configureShowFields($mapper);
2489
2490
        foreach ($this->getExtensions() as $extension) {
2491
            $extension->configureShowFields($mapper);
2492
        }
2493
    }
2494
2495
    /**
2496
     * build the list FieldDescription array.
2497
     */
2498
    protected function buildList(): void
2499
    {
2500
        if ($this->loaded['list']) {
2501
            return;
2502
        }
2503
2504
        $this->loaded['list'] = true;
2505
2506
        $this->list = $this->getListBuilder()->getBaseList();
2507
        $mapper = new ListMapper($this->getListBuilder(), $this->list, $this);
2508
2509
        if (\count($this->getBatchActions()) > 0 && $this->hasRequest() && !$this->getRequest()->isXmlHttpRequest()) {
2510
            $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance(
2511
                $this->getClass(),
2512
                'batch',
2513
                [
2514
                    'label' => 'batch',
2515
                    'code' => '_batch',
2516
                    'sortable' => false,
2517
                    'virtual_field' => true,
2518
                ]
2519
            );
2520
2521
            $fieldDescription->setAdmin($this);
2522
            $fieldDescription->setTemplate($this->getTemplateRegistry()->getTemplate('batch'));
2523
2524
            $mapper->add($fieldDescription, ListMapper::TYPE_BATCH);
2525
        }
2526
2527
        $this->configureListFields($mapper);
2528
2529
        foreach ($this->getExtensions() as $extension) {
2530
            $extension->configureListFields($mapper);
2531
        }
2532
2533
        if ($this->hasRequest() && $this->getRequest()->isXmlHttpRequest()) {
2534
            $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance(
2535
                $this->getClass(),
2536
                'select',
2537
                [
2538
                    'label' => false,
2539
                    'code' => '_select',
2540
                    'sortable' => false,
2541
                    'virtual_field' => false,
2542
                ]
2543
            );
2544
2545
            $fieldDescription->setAdmin($this);
2546
            $fieldDescription->setTemplate($this->getTemplateRegistry()->getTemplate('select'));
2547
2548
            $mapper->add($fieldDescription, ListMapper::TYPE_SELECT);
2549
        }
2550
    }
2551
2552
    /**
2553
     * Build the form FieldDescription collection.
2554
     */
2555
    protected function buildForm(): void
2556
    {
2557
        if ($this->loaded['form']) {
2558
            return;
2559
        }
2560
2561
        $this->loaded['form'] = true;
2562
2563
        $formBuilder = $this->getFormBuilder();
2564
        $formBuilder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event): void {
2565
            $this->preValidate($event->getData());
2566
        }, 100);
2567
2568
        $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...
2569
    }
2570
2571
    /**
2572
     * Gets the subclass corresponding to the given name.
2573
     *
2574
     * @param string $name The name of the sub class
2575
     *
2576
     * @return string the subclass
2577
     */
2578
    protected function getSubClass(string $name): string
2579
    {
2580
        if ($this->hasSubClass($name)) {
2581
            return $this->subClasses[$name];
2582
        }
2583
2584
        throw new \LogicException(sprintf('Unable to find the subclass `%s` for admin `%s`', $name, static::class));
2585
    }
2586
2587
    /**
2588
     * Attach the inline validator to the model metadata, this must be done once per admin.
2589
     */
2590
    protected function attachInlineValidator(): void
2591
    {
2592
        $admin = $this;
2593
2594
        // add the custom inline validation option
2595
        $metadata = $this->validator->getMetadataFor($this->getClass());
2596
        if (!$metadata instanceof GenericMetadata) {
2597
            throw new \UnexpectedValueException(
2598
                sprintf(
2599
                    'Cannot add inline validator for %s because its metadata is an instance of %s instead of %s',
2600
                    $this->getClass(),
2601
                    \get_class($metadata),
2602
                    GenericMetadata::class
2603
                )
2604
            );
2605
        }
2606
2607
        $metadata->addConstraint(new InlineConstraint([
2608
            'service' => $this,
2609
            'method' => static function (ErrorElement $errorElement, $object) use ($admin): void {
2610
                /* @var \Sonata\AdminBundle\Admin\AdminInterface $admin */
2611
2612
                // This avoid the main validation to be cascaded to children
2613
                // The problem occurs when a model Page has a collection of Page as property
2614
                if ($admin->hasSubject() && spl_object_hash($object) !== spl_object_hash($admin->getSubject())) {
2615
                    return;
2616
                }
2617
2618
                $admin->validate($errorElement, $object);
2619
2620
                foreach ($admin->getExtensions() as $extension) {
2621
                    $extension->validate($admin, $errorElement, $object);
2622
                }
2623
            },
2624
            'serializingWarning' => true,
2625
        ]));
2626
    }
2627
2628
    /**
2629
     * Return list routes with permissions name.
2630
     *
2631
     * @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...
2632
     */
2633
    protected function getAccess(): array
2634
    {
2635
        $access = array_merge([
2636
            'acl' => 'MASTER',
2637
            'export' => 'EXPORT',
2638
            'historyCompareRevisions' => 'EDIT',
2639
            'historyViewRevision' => 'EDIT',
2640
            'history' => 'EDIT',
2641
            'edit' => 'EDIT',
2642
            'show' => 'VIEW',
2643
            'create' => 'CREATE',
2644
            'delete' => 'DELETE',
2645
            'batchDelete' => 'DELETE',
2646
            'list' => 'LIST',
2647
        ], $this->getAccessMapping());
2648
2649
        foreach ($this->extensions as $extension) {
2650
            $access = array_merge($access, $extension->getAccessMapping($this));
2651
        }
2652
2653
        return $access;
2654
    }
2655
2656
    /**
2657
     * Configures a list of default filters.
2658
     */
2659
    protected function configureDefaultFilterValues(array &$filterValues): void
2660
    {
2661
    }
2662
2663
    /**
2664
     * Configures a list of default sort values.
2665
     *
2666
     * Example:
2667
     *   $sortValues['_sort_by'] = 'foo'
2668
     *   $sortValues['_sort_order'] = 'DESC'
2669
     */
2670
    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...
2671
    {
2672
    }
2673
2674
    /**
2675
     * Set the parent object, if any, to the provided object.
2676
     */
2677
    final protected function appendParentObject(object $object): void
2678
    {
2679
        if ($this->isChild() && $this->getParentAssociationMapping()) {
2680
            $parentAdmin = $this->getParent();
2681
            $parentObject = $parentAdmin->getObject($this->request->get($parentAdmin->getIdParameter()));
2682
2683
            if (null !== $parentObject) {
2684
                $propertyAccessor = $this->getConfigurationPool()->getPropertyAccessor();
2685
                $propertyPath = new PropertyPath($this->getParentAssociationMapping());
2686
2687
                $value = $propertyAccessor->getValue($object, $propertyPath);
2688
2689
                if (\is_array($value) || $value instanceof \ArrayAccess) {
2690
                    $value[] = $parentObject;
2691
                    $propertyAccessor->setValue($object, $propertyPath, $value);
2692
                } else {
2693
                    $propertyAccessor->setValue($object, $propertyPath, $parentObject);
2694
                }
2695
            }
2696
        } elseif ($this->hasParentFieldDescription()) {
2697
            $parentAdmin = $this->getParentFieldDescription()->getAdmin();
2698
            $parentObject = $parentAdmin->getObject($this->request->get($parentAdmin->getIdParameter()));
2699
2700
            if (null !== $parentObject) {
2701
                ObjectManipulator::setObject($object, $parentObject, $this->getParentFieldDescription());
2702
            }
2703
        }
2704
    }
2705
2706
    /**
2707
     * {@inheritdoc}
2708
     */
2709
    private function buildDatagrid(): void
2710
    {
2711
        if ($this->loaded['datagrid']) {
2712
            return;
2713
        }
2714
2715
        $this->loaded['datagrid'] = true;
2716
2717
        $filterParameters = $this->getFilterParameters();
2718
2719
        // transform _sort_by from a string to a FieldDescriptionInterface for the datagrid.
2720
        if (isset($filterParameters['_sort_by']) && \is_string($filterParameters['_sort_by'])) {
2721
            if ($this->hasListFieldDescription($filterParameters['_sort_by'])) {
2722
                $filterParameters['_sort_by'] = $this->getListFieldDescription($filterParameters['_sort_by']);
2723
            } else {
2724
                $filterParameters['_sort_by'] = $this->getModelManager()->getNewFieldDescriptionInstance(
2725
                    $this->getClass(),
2726
                    $filterParameters['_sort_by'],
2727
                    []
2728
                );
2729
2730
                $this->getListBuilder()->buildField(null, $filterParameters['_sort_by'], $this);
2731
            }
2732
        }
2733
2734
        // initialize the datagrid
2735
        $this->datagrid = $this->getDatagridBuilder()->getBaseDatagrid($this, $filterParameters);
2736
2737
        $this->datagrid->getPager()->setMaxPageLinks($this->maxPageLinks);
2738
2739
        $mapper = new DatagridMapper($this->getDatagridBuilder(), $this->datagrid, $this);
2740
2741
        // build the datagrid filter
2742
        $this->configureDatagridFilters($mapper);
2743
2744
        // ok, try to limit to add parent filter
2745
        if ($this->isChild() && $this->getParentAssociationMapping() && !$mapper->has($this->getParentAssociationMapping())) {
2746
            $mapper->add($this->getParentAssociationMapping(), null, [
2747
                'show_filter' => false,
2748
                'label' => false,
2749
                'field_type' => ModelHiddenType::class,
2750
                'field_options' => [
2751
                    'model_manager' => $this->getModelManager(),
2752
                ],
2753
                'operator_type' => HiddenType::class,
2754
            ], null, null, [
2755
                'admin_code' => $this->getParent()->getCode(),
2756
            ]);
2757
        }
2758
2759
        foreach ($this->getExtensions() as $extension) {
2760
            $extension->configureDatagridFilters($mapper);
2761
        }
2762
    }
2763
2764
    /**
2765
     * Build all the related urls to the current admin.
2766
     */
2767
    private function buildRoutes(): void
2768
    {
2769
        if ($this->loaded['routes']) {
2770
            return;
2771
        }
2772
2773
        $this->loaded['routes'] = true;
2774
2775
        $this->routes = new RouteCollection(
2776
            $this->getBaseCodeRoute(),
2777
            $this->getBaseRouteName(),
2778
            $this->getBaseRoutePattern(),
2779
            $this->getBaseControllerName()
2780
        );
2781
2782
        $this->routeBuilder->build($this, $this->routes);
2783
2784
        $this->configureRoutes($this->routes);
2785
2786
        foreach ($this->getExtensions() as $extension) {
2787
            $extension->configureRoutes($this, $this->routes);
2788
        }
2789
    }
2790
}
2791