Completed
Pull Request — master (#5101)
by Marko
03:57
created

BaseFieldDescription::isSortable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Admin;
15
16
use Doctrine\Common\Inflector\Inflector;
17
use Sonata\AdminBundle\Exception\NoValueException;
18
19
/**
20
 * A FieldDescription hold the information about a field. A typical
21
 * admin instance contains different collections of fields.
22
 *
23
 * - form: used by the form
24
 * - list: used by the list
25
 * - filter: used by the list filter
26
 *
27
 * Some options are global across the different contexts, other are
28
 * context specifics.
29
 *
30
 * Global options :
31
 *   - type (m): define the field type (use to tweak the form or the list)
32
 *   - template (o) : the template used to render the field
33
 *   - name (o) : the name used (label in the form, title in the list)
34
 *   - link_parameters (o) : add link parameter to the related Admin class when
35
 *                           the Admin.generateUrl is called
36
 *   - code : the method name to retrieve the related value
37
 *   - associated_tostring : (deprecated, use associated_property option)
38
 *                           the method to retrieve the "string" representation
39
 *                           of the collection element.
40
 *   - associated_property : property path to retrieve the "string" representation
41
 *                           of the collection element.
42
 *
43
 * Form Field options :
44
 *   - field_type (o): the widget class to use to render the field
45
 *   - field_options (o): the options to give to the widget
46
 *   - edit (o) : list|inline|standard (only used for associated admin)
47
 *      - list : open a popup where the user can search, filter and click on one field
48
 *               to select one item
49
 *      - inline : the associated form admin is embedded into the current form
50
 *      - standard : the associated admin is created through a popup
51
 *
52
 * List Field options :
53
 *   - identifier (o): if set to true a link appear on to edit the element
54
 *
55
 * Filter Field options :
56
 *   - options (o): options given to the Filter object
57
 *   - field_type (o): the widget class to use to render the field
58
 *   - field_options (o): the options to give to the widget
59
 *
60
 * @author Thomas Rabaix <[email protected]>
61
 */
62
abstract class BaseFieldDescription implements FieldDescriptionInterface
63
{
64
    /**
65
     * @var string the field name
66
     */
67
    protected $name;
68
69
    /**
70
     * @var string|int the type
71
     */
72
    protected $type;
73
74
    /**
75
     * @var string|int the original mapping type
76
     */
77
    protected $mappingType;
78
79
    /**
80
     * @var string the field name (of the form)
81
     */
82
    protected $fieldName;
83
84
    /**
85
     * @var array the ORM association mapping
86
     */
87
    protected $associationMapping;
88
89
    /**
90
     * @var array the ORM field information
91
     */
92
    protected $fieldMapping;
93
94
    /**
95
     * @var array the ORM parent mapping association
96
     */
97
    protected $parentAssociationMappings;
98
99
    /**
100
     * @var string the template name
101
     */
102
    protected $template;
103
104
    /**
105
     * @var array the option collection
106
     */
107
    protected $options = [];
108
109
    /**
110
     * @var AdminInterface|null the parent Admin instance
111
     */
112
    protected $parent = null;
113
114
    /**
115
     * @var AdminInterface the related admin instance
116
     */
117
    protected $admin;
118
119
    /**
120
     * @var AdminInterface the associated admin class if the object is associated to another entity
121
     */
122
    protected $associationAdmin;
123
124
    /**
125
     * @var string the help message to display
126
     */
127
    protected $help;
128
129
    /**
130
     * @var array[] cached object field getters
131
     */
132
    private static $fieldGetters = [];
133
134
    public function setFieldName($fieldName): void
135
    {
136
        $this->fieldName = $fieldName;
137
    }
138
139
    public function getFieldName()
140
    {
141
        return $this->fieldName;
142
    }
143
144
    public function setName($name): void
145
    {
146
        $this->name = $name;
147
148
        if (!$this->getFieldName()) {
149
            $this->setFieldName(substr(strrchr('.'.$name, '.'), 1));
150
        }
151
    }
152
153
    public function getName()
154
    {
155
        return $this->name;
156
    }
157
158
    public function getOption($name, $default = null)
159
    {
160
        return isset($this->options[$name]) ? $this->options[$name] : $default;
161
    }
162
163
    public function setOption($name, $value): void
164
    {
165
        $this->options[$name] = $value;
166
    }
167
168
    public function setOptions(array $options): void
169
    {
170
        // set the type if provided
171
        if (isset($options['type'])) {
172
            $this->setType($options['type']);
173
            unset($options['type']);
174
        }
175
176
        // remove property value
177
        if (isset($options['template'])) {
178
            $this->setTemplate($options['template']);
179
            unset($options['template']);
180
        }
181
182
        // set help if provided
183
        if (isset($options['help'])) {
184
            $this->setHelp($options['help']);
185
            unset($options['help']);
186
        }
187
188
        // set default placeholder
189
        if (!isset($options['placeholder'])) {
190
            $options['placeholder'] = 'short_object_description_placeholder';
191
        }
192
193
        if (!isset($options['link_parameters'])) {
194
            $options['link_parameters'] = [];
195
        }
196
197
        $this->options = $options;
198
    }
199
200
    public function getOptions()
201
    {
202
        return $this->options;
203
    }
204
205
    public function setTemplate($template): void
206
    {
207
        $this->template = $template;
208
    }
209
210
    public function getTemplate()
211
    {
212
        return $this->template;
213
    }
214
215
    public function setType($type): void
216
    {
217
        $this->type = $type;
218
    }
219
220
    public function getType()
221
    {
222
        return $this->type;
223
    }
224
225
    public function setParent(AdminInterface $parent): void
226
    {
227
        $this->parent = $parent;
228
    }
229
230
    public function getParent()
231
    {
232
        return $this->parent;
233
    }
234
235
    public function getAssociationMapping()
236
    {
237
        return $this->associationMapping;
238
    }
239
240
    public function getFieldMapping()
241
    {
242
        return $this->fieldMapping;
243
    }
244
245
    public function getParentAssociationMappings()
246
    {
247
        return $this->parentAssociationMappings;
248
    }
249
250
    public function setAssociationAdmin(AdminInterface $associationAdmin): void
251
    {
252
        $this->associationAdmin = $associationAdmin;
253
        $this->associationAdmin->setParentFieldDescription($this);
254
    }
255
256
    public function getAssociationAdmin()
257
    {
258
        return $this->associationAdmin;
259
    }
260
261
    public function hasAssociationAdmin()
262
    {
263
        return null !== $this->associationAdmin;
264
    }
265
266
    public function getFieldValue($object, $fieldName)
267
    {
268
        if ($this->isVirtual() || null === $object) {
269
            return;
270
        }
271
272
        $getters = [];
273
        $parameters = [];
274
275
        // prefer method name given in the code option
276
        if ($this->getOption('code')) {
277
            $getters[] = $this->getOption('code');
278
        }
279
        // parameters for the method given in the code option
280
        if ($this->getOption('parameters')) {
281
            $parameters = $this->getOption('parameters');
282
        }
283
284
        if (is_string($fieldName) && '' !== $fieldName) {
285
            if ($this->hasCachedFieldGetter($object, $fieldName)) {
286
                return $this->callCachedGetter($object, $fieldName, $parameters);
287
            }
288
289
            $camelizedFieldName = Inflector::classify($fieldName);
290
291
            $getters[] = 'get'.$camelizedFieldName;
292
            $getters[] = 'is'.$camelizedFieldName;
293
            $getters[] = 'has'.$camelizedFieldName;
294
        }
295
296
        foreach ($getters as $getter) {
297
            if (method_exists($object, $getter) && is_callable([$object, $getter])) {
298
                $this->cacheFieldGetter($object, $fieldName, 'getter', $getter);
299
300
                return call_user_func_array([$object, $getter], $parameters);
301
            }
302
        }
303
304
        if (method_exists($object, '__call')) {
305
            $this->cacheFieldGetter($object, $fieldName, 'call');
306
307
            return call_user_func_array([$object, '__call'], [$fieldName, $parameters]);
308
        }
309
310
        if (isset($object->{$fieldName})) {
311
            $this->cacheFieldGetter($object, $fieldName, 'var');
312
313
            return $object->{$fieldName};
314
        }
315
316
        throw new NoValueException(sprintf('Unable to retrieve the value of `%s`', $this->getName()));
317
    }
318
319
    public function setAdmin(AdminInterface $admin): void
320
    {
321
        $this->admin = $admin;
322
    }
323
324
    public function getAdmin()
325
    {
326
        return $this->admin;
327
    }
328
329
    public function mergeOption($name, array $options = []): void
330
    {
331
        if (!isset($this->options[$name])) {
332
            $this->options[$name] = [];
333
        }
334
335
        if (!is_array($this->options[$name])) {
336
            throw new \RuntimeException(sprintf('The key `%s` does not point to an array value', $name));
337
        }
338
339
        $this->options[$name] = array_merge($this->options[$name], $options);
340
    }
341
342
    public function mergeOptions(array $options = []): void
343
    {
344
        $this->setOptions(array_merge_recursive($this->options, $options));
345
    }
346
347
    public function setMappingType($mappingType): void
348
    {
349
        $this->mappingType = $mappingType;
350
    }
351
352
    public function getMappingType()
353
    {
354
        return $this->mappingType;
355
    }
356
357
    /**
358
     * Defines the help message.
359
     *
360
     * @param string $help
361
     */
362
    public function setHelp($help): void
363
    {
364
        $this->help = $help;
365
    }
366
367
    public function getHelp()
368
    {
369
        return $this->help;
370
    }
371
372
    public function getLabel()
373
    {
374
        return $this->getOption('label');
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->getOption('label'); of type array|null adds the type array to the return on line 374 which is incompatible with the return type declared by the interface Sonata\AdminBundle\Admin...tionInterface::getLabel of type string.
Loading history...
375
    }
376
377
    public function isSortable(): bool
378
    {
379
        return false !== $this->getOption('sortable', false);
380
    }
381
382
    public function getSortFieldMapping()
383
    {
384
        return $this->getOption('sort_field_mapping');
385
    }
386
387
    public function getSortParentAssociationMapping()
388
    {
389
        return $this->getOption('sort_parent_association_mappings');
390
    }
391
392
    public function getTranslationDomain()
393
    {
394
        return $this->getOption('translation_domain') ?: $this->getAdmin()->getTranslationDomain();
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->getOption('transl...getTranslationDomain(); of type array|string adds the type array to the return on line 394 which is incompatible with the return type declared by the interface Sonata\AdminBundle\Admin...e::getTranslationDomain of type string.
Loading history...
395
    }
396
397
    public function isVirtual(): bool
398
    {
399
        return false !== $this->getOption('virtual_field', false);
400
    }
401
402
    private function getFieldGetterKey($object, $fieldName): ?string
403
    {
404
        if (!\is_string($fieldName)) {
405
            return null;
406
        }
407
408
        if (!is_object($object)) {
409
            return null;
410
        }
411
        $components = [\get_class($object), $fieldName];
412
413
        $code = $this->getOption('code');
414
        if (\is_string($code) && '' !== $code) {
415
            $components[] = $code;
416
        }
417
418
        return implode('-', $components);
419
    }
420
421
    private function hasCachedFieldGetter($object, $fieldName): bool
422
    {
423
        return isset(
424
            self::$fieldGetters[$this->getFieldGetterKey($object, $fieldName)]
425
        );
426
    }
427
428
    private function callCachedGetter($object, $fieldName, array $parameters = [])
429
    {
430
        $getterKey = $this->getFieldGetterKey($object, $fieldName);
431
        if (self::$fieldGetters[$getterKey]['method'] === 'getter') {
432
            return \call_user_func_array(
433
                [$object, self::$fieldGetters[$getterKey]['getter']],
434
                $parameters
435
            );
436
        } elseif (self::$fieldGetters[$getterKey]['method'] === 'call') {
437
            return \call_user_func_array(
438
                [$object, '__call'],
439
                [$fieldName, $parameters]
440
            );
441
        }
442
443
        return $object->{$fieldName};
444
    }
445
446
    private function cacheFieldGetter($object, $fieldName, $method, $getter = null): void
447
    {
448
        $getterKey = $this->getFieldGetterKey($object, $fieldName);
449
        if (null !== $getterKey) {
450
            self::$fieldGetters[$getterKey] = [
451
                'method' => $method,
452
            ];
453
            if (null !== $getter) {
454
                self::$fieldGetters[$getterKey]['getter'] = $getter;
455
            }
456
        }
457
    }
458
}
459