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