1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace DoctrineModule\Form\Element; |
6
|
|
|
|
7
|
|
|
use Doctrine\Common\Collections\Collection; |
8
|
|
|
use Doctrine\Common\Inflector\Inflector; |
9
|
|
|
use Doctrine\Common\Persistence\ObjectManager; |
10
|
|
|
use DoctrineModule\Persistence\ObjectManagerAwareInterface; |
11
|
|
|
use Laminas\Stdlib\Guard\ArrayOrTraversableGuardTrait; |
12
|
|
|
use ReflectionMethod; |
13
|
|
|
use RuntimeException; |
14
|
|
|
use Traversable; |
15
|
|
|
use function array_change_key_case; |
16
|
|
|
use function array_key_exists; |
17
|
|
|
use function array_shift; |
18
|
|
|
use function call_user_func; |
19
|
|
|
use function count; |
20
|
|
|
use function current; |
21
|
|
|
use function get_class; |
22
|
|
|
use function gettype; |
23
|
|
|
use function is_callable; |
24
|
|
|
use function is_object; |
25
|
|
|
use function is_string; |
26
|
|
|
use function method_exists; |
27
|
|
|
use function sprintf; |
28
|
|
|
use function strtolower; |
29
|
|
|
use function trim; |
30
|
|
|
|
31
|
|
|
class Proxy implements ObjectManagerAwareInterface |
32
|
|
|
{ |
33
|
|
|
use ArrayOrTraversableGuardTrait; |
34
|
|
|
|
35
|
|
|
/** @var mixed[]|Traversable */ |
36
|
|
|
protected $objects; |
37
|
|
|
|
38
|
|
|
/** @var string */ |
39
|
|
|
protected $targetClass; |
40
|
|
|
|
41
|
|
|
/** @var mixed[] */ |
42
|
|
|
protected $valueOptions = []; |
43
|
|
|
|
44
|
|
|
/** @var mixed[] */ |
45
|
|
|
protected $findMethod = []; |
46
|
|
|
|
47
|
|
|
/** @var mixed */ |
48
|
|
|
protected $property; |
49
|
|
|
|
50
|
|
|
/** @var mixed[] */ |
51
|
|
|
protected $optionAttributes = []; |
52
|
|
|
|
53
|
|
|
/** @var callable $labelGenerator A callable used to create a label based on an item in the collection an Entity */ |
54
|
|
|
protected $labelGenerator; |
55
|
|
|
|
56
|
|
|
/** @var bool|null */ |
57
|
|
|
protected $isMethod; |
58
|
|
|
|
59
|
|
|
/** @var ObjectManager */ |
60
|
|
|
protected $objectManager; |
61
|
|
|
|
62
|
|
|
/** @var bool */ |
63
|
|
|
protected $displayEmptyItem = false; |
64
|
|
|
|
65
|
|
|
/** @var string */ |
66
|
|
|
protected $emptyItemLabel = ''; |
67
|
|
|
|
68
|
|
|
/** @var string|null */ |
69
|
|
|
protected $optgroupIdentifier; |
70
|
|
|
|
71
|
|
|
/** @var string|null */ |
72
|
|
|
protected $optgroupDefault; |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* @param mixed[] $options |
76
|
|
|
*/ |
77
|
23 |
|
public function setOptions(array $options) : void |
78
|
|
|
{ |
79
|
23 |
|
if (isset($options['object_manager'])) { |
80
|
22 |
|
$this->setObjectManager($options['object_manager']); |
81
|
|
|
} |
82
|
|
|
|
83
|
23 |
|
if (isset($options['target_class'])) { |
84
|
22 |
|
$this->setTargetClass($options['target_class']); |
85
|
|
|
} |
86
|
|
|
|
87
|
23 |
|
if (isset($options['property'])) { |
88
|
3 |
|
$this->setProperty($options['property']); |
89
|
|
|
} |
90
|
|
|
|
91
|
23 |
|
if (isset($options['label_generator'])) { |
92
|
2 |
|
$this->setLabelGenerator($options['label_generator']); |
|
|
|
|
93
|
|
|
} |
94
|
|
|
|
95
|
23 |
|
if (isset($options['find_method'])) { |
96
|
4 |
|
$this->setFindMethod($options['find_method']); |
97
|
|
|
} |
98
|
|
|
|
99
|
23 |
|
if (isset($options['is_method'])) { |
100
|
1 |
|
$this->setIsMethod($options['is_method']); |
101
|
|
|
} |
102
|
|
|
|
103
|
23 |
|
if (isset($options['display_empty_item'])) { |
104
|
1 |
|
$this->setDisplayEmptyItem($options['display_empty_item']); |
105
|
|
|
} |
106
|
|
|
|
107
|
23 |
|
if (isset($options['empty_item_label'])) { |
108
|
1 |
|
$this->setEmptyItemLabel($options['empty_item_label']); |
109
|
|
|
} |
110
|
|
|
|
111
|
23 |
|
if (isset($options['option_attributes'])) { |
112
|
3 |
|
$this->setOptionAttributes($options['option_attributes']); |
113
|
|
|
} |
114
|
|
|
|
115
|
23 |
|
if (isset($options['optgroup_identifier'])) { |
116
|
4 |
|
$this->setOptgroupIdentifier($options['optgroup_identifier']); |
117
|
|
|
} |
118
|
|
|
|
119
|
23 |
|
if (! isset($options['optgroup_default'])) { |
120
|
23 |
|
return; |
121
|
|
|
} |
122
|
|
|
|
123
|
1 |
|
$this->setOptgroupDefault($options['optgroup_default']); |
124
|
1 |
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* @return mixed |
128
|
|
|
*/ |
129
|
22 |
|
public function getValueOptions() |
130
|
|
|
{ |
131
|
22 |
|
if (empty($this->valueOptions)) { |
132
|
22 |
|
$this->loadValueOptions(); |
133
|
|
|
} |
134
|
|
|
|
135
|
14 |
|
return $this->valueOptions; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* @return mixed |
140
|
|
|
*/ |
141
|
20 |
|
public function getObjects() |
142
|
|
|
{ |
143
|
20 |
|
$this->loadObjects(); |
144
|
|
|
|
145
|
16 |
|
return $this->objects; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Set the label for the empty option |
150
|
|
|
*/ |
151
|
1 |
|
public function setEmptyItemLabel(string $emptyItemLabel) : Proxy |
152
|
|
|
{ |
153
|
1 |
|
$this->emptyItemLabel = $emptyItemLabel; |
154
|
|
|
|
155
|
1 |
|
return $this; |
156
|
|
|
} |
157
|
|
|
|
158
|
1 |
|
public function getEmptyItemLabel() : string |
159
|
|
|
{ |
160
|
1 |
|
return $this->emptyItemLabel; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* @return mixed[] |
165
|
|
|
*/ |
166
|
14 |
|
public function getOptionAttributes() : array |
167
|
|
|
{ |
168
|
14 |
|
return $this->optionAttributes; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* @param mixed[] $optionAttributes |
173
|
|
|
*/ |
174
|
3 |
|
public function setOptionAttributes(array $optionAttributes) : void |
175
|
|
|
{ |
176
|
3 |
|
$this->optionAttributes = $optionAttributes; |
177
|
3 |
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Set a flag, whether to include the empty option at the beginning or not |
181
|
|
|
*/ |
182
|
1 |
|
public function setDisplayEmptyItem(bool $displayEmptyItem) : Proxy |
183
|
|
|
{ |
184
|
1 |
|
$this->displayEmptyItem = $displayEmptyItem; |
185
|
|
|
|
186
|
1 |
|
return $this; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
public function getDisplayEmptyItem() : bool |
190
|
|
|
{ |
191
|
|
|
return $this->displayEmptyItem; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* Set the object manager |
196
|
|
|
*/ |
197
|
22 |
|
public function setObjectManager(ObjectManager $objectManager) : void |
198
|
|
|
{ |
199
|
22 |
|
$this->objectManager = $objectManager; |
200
|
22 |
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* Get the object manager |
204
|
|
|
*/ |
205
|
20 |
|
public function getObjectManager() : ObjectManager |
206
|
|
|
{ |
207
|
20 |
|
return $this->objectManager; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* Set the FQCN of the target object |
212
|
|
|
*/ |
213
|
22 |
|
public function setTargetClass(string $targetClass) : Proxy |
214
|
|
|
{ |
215
|
22 |
|
$this->targetClass = $targetClass; |
216
|
|
|
|
217
|
22 |
|
return $this; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Get the target class |
222
|
|
|
*/ |
223
|
20 |
|
public function getTargetClass() : string |
224
|
|
|
{ |
225
|
20 |
|
return $this->targetClass; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* Set the property to use as the label in the options |
230
|
|
|
*/ |
231
|
3 |
|
public function setProperty(string $property) : Proxy |
232
|
|
|
{ |
233
|
3 |
|
$this->property = $property; |
234
|
|
|
|
235
|
3 |
|
return $this; |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* @return mixed |
240
|
|
|
*/ |
241
|
|
|
public function getProperty() |
242
|
|
|
{ |
243
|
|
|
return $this->property; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Set the label generator callable that is responsible for generating labels for the items in the collection |
248
|
|
|
* |
249
|
|
|
* @param callable $callable A callable used to create a label based off of an Entity |
250
|
|
|
*/ |
251
|
1 |
|
public function setLabelGenerator(callable $callable) : void |
252
|
|
|
{ |
253
|
1 |
|
$this->labelGenerator = $callable; |
254
|
1 |
|
} |
255
|
|
|
|
256
|
14 |
|
public function getLabelGenerator() : ?callable |
257
|
|
|
{ |
258
|
14 |
|
return $this->labelGenerator; |
259
|
|
|
} |
260
|
|
|
|
261
|
13 |
|
public function getOptgroupIdentifier() : ?string |
262
|
|
|
{ |
263
|
13 |
|
return $this->optgroupIdentifier; |
264
|
|
|
} |
265
|
|
|
|
266
|
4 |
|
public function setOptgroupIdentifier(string $optgroupIdentifier) : void |
267
|
|
|
{ |
268
|
4 |
|
$this->optgroupIdentifier = (string) $optgroupIdentifier; |
269
|
4 |
|
} |
270
|
|
|
|
271
|
2 |
|
public function getOptgroupDefault() : ?string |
272
|
|
|
{ |
273
|
2 |
|
return $this->optgroupDefault; |
274
|
|
|
} |
275
|
|
|
|
276
|
1 |
|
public function setOptgroupDefault(string $optgroupDefault) : void |
277
|
|
|
{ |
278
|
1 |
|
$this->optgroupDefault = (string) $optgroupDefault; |
279
|
1 |
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Set if the property is a method to use as the label in the options |
283
|
|
|
*/ |
284
|
1 |
|
public function setIsMethod(bool $method) : Proxy |
285
|
|
|
{ |
286
|
1 |
|
$this->isMethod = (bool) $method; |
287
|
|
|
|
288
|
1 |
|
return $this; |
289
|
|
|
} |
290
|
|
|
|
291
|
3 |
|
public function getIsMethod() : ?bool |
292
|
|
|
{ |
293
|
3 |
|
return $this->isMethod; |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
/** Set the findMethod property to specify the method to use on repository |
297
|
|
|
* |
298
|
|
|
* @param mixed[] $findMethod |
299
|
|
|
*/ |
300
|
4 |
|
public function setFindMethod(array $findMethod) : Proxy |
301
|
|
|
{ |
302
|
4 |
|
$this->findMethod = $findMethod; |
303
|
|
|
|
304
|
4 |
|
return $this; |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* Get findMethod definition |
309
|
|
|
* |
310
|
|
|
* @return mixed[] |
311
|
|
|
*/ |
312
|
20 |
|
public function getFindMethod() : array |
313
|
|
|
{ |
314
|
20 |
|
return $this->findMethod; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* @param mixed $targetEntity |
319
|
|
|
*/ |
320
|
14 |
|
protected function generateLabel($targetEntity) : ?string |
321
|
|
|
{ |
322
|
14 |
|
if ($this->getLabelGenerator() === null) { |
323
|
13 |
|
return null; |
324
|
|
|
} |
325
|
|
|
|
326
|
1 |
|
return call_user_func($this->getLabelGenerator(), $targetEntity); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* @param mixed $value |
331
|
|
|
* |
332
|
|
|
* @return mixed[]|mixed|object |
333
|
|
|
* |
334
|
|
|
* @throws RuntimeException |
335
|
|
|
*/ |
336
|
|
|
public function getValue($value) |
337
|
|
|
{ |
338
|
|
|
if (! $this->getObjectManager()) { |
339
|
|
|
throw new RuntimeException('No object manager was set'); |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
if (! $this->getTargetClass()) { |
343
|
|
|
throw new RuntimeException('No target class was set'); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
$metadata = $this->getObjectManager()->getClassMetadata($this->getTargetClass()); |
347
|
|
|
|
348
|
|
|
if (is_object($value)) { |
349
|
|
|
if ($value instanceof Collection) { |
350
|
|
|
$data = []; |
351
|
|
|
|
352
|
|
|
foreach ($value as $object) { |
353
|
|
|
$values = $metadata->getIdentifierValues($object); |
354
|
|
|
$data[] = array_shift($values); |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
$value = $data; |
358
|
|
|
} else { |
359
|
|
|
$metadata = $this->getObjectManager()->getClassMetadata(get_class($value)); |
360
|
|
|
$identifier = $metadata->getIdentifierFieldNames(); |
361
|
|
|
|
362
|
|
|
// TODO: handle composite (multiple) identifiers |
363
|
|
|
if ($identifier !== null && count($identifier) > 1) { |
364
|
|
|
//$value = $key; |
365
|
|
|
$todo = true; |
|
|
|
|
366
|
|
|
} else { |
367
|
|
|
$value = current($metadata->getIdentifierValues($value)); |
368
|
|
|
} |
369
|
|
|
} |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
return $value; |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
/** |
376
|
|
|
* Load objects |
377
|
|
|
* |
378
|
|
|
* @throws RuntimeException |
379
|
|
|
* @throws Exception\InvalidRepositoryResultException |
380
|
|
|
*/ |
381
|
20 |
|
protected function loadObjects() : void |
382
|
|
|
{ |
383
|
20 |
|
if (! empty($this->objects)) { |
384
|
|
|
return; |
385
|
|
|
} |
386
|
|
|
|
387
|
20 |
|
$findMethod = (array) $this->getFindMethod(); |
388
|
|
|
|
389
|
20 |
|
if (! $findMethod) { |
|
|
|
|
390
|
16 |
|
$findMethodName = 'findAll'; |
391
|
16 |
|
$repository = $this->objectManager->getRepository($this->targetClass); |
392
|
16 |
|
$objects = $repository->findAll(); |
393
|
|
|
} else { |
394
|
4 |
|
if (! isset($findMethod['name'])) { |
395
|
1 |
|
throw new RuntimeException('No method name was set'); |
396
|
|
|
} |
397
|
|
|
|
398
|
3 |
|
$findMethodName = $findMethod['name']; |
399
|
3 |
|
$findMethodParams = isset($findMethod['params']) ? array_change_key_case($findMethod['params']) : []; |
400
|
3 |
|
$repository = $this->objectManager->getRepository($this->targetClass); |
401
|
|
|
|
402
|
3 |
|
if (! method_exists($repository, $findMethodName)) { |
403
|
1 |
|
throw new RuntimeException( |
404
|
1 |
|
sprintf( |
405
|
1 |
|
'Method "%s" could not be found in repository "%s"', |
406
|
1 |
|
$findMethodName, |
407
|
1 |
|
get_class($repository) |
408
|
|
|
) |
409
|
|
|
); |
410
|
|
|
} |
411
|
|
|
|
412
|
2 |
|
$r = new ReflectionMethod($repository, $findMethodName); |
413
|
2 |
|
$args = []; |
414
|
|
|
|
415
|
2 |
|
foreach ($r->getParameters() as $param) { |
416
|
2 |
|
if (array_key_exists(strtolower($param->getName()), $findMethodParams)) { |
|
|
|
|
417
|
1 |
|
$args[] = $findMethodParams[strtolower($param->getName())]; |
|
|
|
|
418
|
2 |
|
} elseif ($param->isDefaultValueAvailable()) { |
419
|
1 |
|
$args[] = $param->getDefaultValue(); |
420
|
1 |
|
} elseif (! $param->isOptional()) { |
421
|
1 |
|
throw new RuntimeException( |
422
|
1 |
|
sprintf( |
423
|
|
|
'Required parameter "%s" with no default value for method "%s" in repository "%s"' |
424
|
1 |
|
. ' was not provided', |
425
|
1 |
|
$param->getName(), |
|
|
|
|
426
|
1 |
|
$findMethodName, |
427
|
1 |
|
get_class($repository) |
428
|
|
|
) |
429
|
|
|
); |
430
|
|
|
} |
431
|
|
|
} |
432
|
|
|
|
433
|
1 |
|
$objects = $r->invokeArgs($repository, $args); |
434
|
|
|
} |
435
|
|
|
|
436
|
17 |
|
$this->guardForArrayOrTraversable( |
437
|
17 |
|
$objects, |
438
|
17 |
|
sprintf('%s::%s() return value', get_class($repository), $findMethodName), |
439
|
17 |
|
'DoctrineModule\Form\Element\Exception\InvalidRepositoryResultException' |
440
|
|
|
); |
441
|
|
|
|
442
|
16 |
|
$this->objects = $objects; |
443
|
16 |
|
} |
444
|
|
|
|
445
|
|
|
/** |
446
|
|
|
* Load value options |
447
|
|
|
* |
448
|
|
|
* @throws RuntimeException |
449
|
|
|
*/ |
450
|
22 |
|
protected function loadValueOptions() : void |
451
|
|
|
{ |
452
|
22 |
|
if (! $this->objectManager) { |
453
|
1 |
|
throw new RuntimeException('No object manager was set'); |
454
|
|
|
} |
455
|
|
|
|
456
|
21 |
|
if (! $this->targetClass) { |
457
|
1 |
|
throw new RuntimeException('No target class was set'); |
458
|
|
|
} |
459
|
|
|
|
460
|
20 |
|
$metadata = $this->getObjectManager()->getClassMetadata($this->getTargetClass()); |
461
|
20 |
|
$identifier = $metadata->getIdentifierFieldNames(); |
462
|
20 |
|
$objects = $this->getObjects(); |
463
|
16 |
|
$options = []; |
464
|
16 |
|
$optionAttributes = []; |
465
|
|
|
|
466
|
16 |
|
if ($this->displayEmptyItem) { |
467
|
1 |
|
$options[''] = $this->getEmptyItemLabel(); |
468
|
|
|
} |
469
|
|
|
|
470
|
16 |
|
foreach ($objects as $key => $object) { |
471
|
14 |
|
$generatedLabel = $this->generateLabel($object); |
472
|
14 |
|
if ($generatedLabel !== null) { |
473
|
1 |
|
$label = $generatedLabel; |
474
|
13 |
|
} elseif ($this->property) { |
475
|
3 |
|
$property = $this->property; |
476
|
3 |
|
if (($this->getIsMethod() === false || $this->getIsMethod() === null) |
477
|
3 |
|
&& ! $metadata->hasField($property) |
478
|
|
|
) { |
479
|
|
|
throw new RuntimeException( |
480
|
|
|
sprintf( |
481
|
|
|
'Property "%s" could not be found in object "%s"', |
482
|
|
|
$property, |
483
|
|
|
$this->getTargetClass() |
484
|
|
|
) |
485
|
|
|
); |
486
|
|
|
} |
487
|
|
|
|
488
|
3 |
|
$getter = 'get' . Inflector::classify($property); |
489
|
|
|
|
490
|
3 |
|
if (! is_callable([$object, $getter])) { |
491
|
|
|
throw new RuntimeException( |
492
|
|
|
sprintf('Method "%s::%s" is not callable', $this->targetClass, $getter) |
493
|
|
|
); |
494
|
|
|
} |
495
|
|
|
|
496
|
3 |
|
$label = $object->{$getter}(); |
497
|
|
|
} else { |
498
|
10 |
|
if (! is_callable([$object, '__toString'])) { |
499
|
|
|
throw new RuntimeException( |
500
|
|
|
sprintf( |
501
|
|
|
'%s must have a "__toString()" method defined if you have not set a property' |
502
|
|
|
. ' or method to use.', |
503
|
|
|
$this->getTargetClass() |
504
|
|
|
) |
505
|
|
|
); |
506
|
|
|
} |
507
|
|
|
|
508
|
10 |
|
$label = (string) $object; |
509
|
|
|
} |
510
|
|
|
|
511
|
14 |
|
if ($identifier !== null && count($identifier) > 1) { |
512
|
|
|
$value = $key; |
513
|
|
|
} else { |
514
|
14 |
|
$value = current($metadata->getIdentifierValues($object)); |
515
|
|
|
} |
516
|
|
|
|
517
|
14 |
|
foreach ($this->getOptionAttributes() as $optionKey => $optionValue) { |
518
|
3 |
|
if (is_string($optionValue)) { |
519
|
1 |
|
$optionAttributes[$optionKey] = $optionValue; |
520
|
|
|
|
521
|
1 |
|
continue; |
522
|
|
|
} |
523
|
|
|
|
524
|
2 |
|
if (is_callable($optionValue)) { |
525
|
1 |
|
$callableValue = call_user_func($optionValue, $object); |
526
|
1 |
|
$optionAttributes[$optionKey] = (string) $callableValue; |
527
|
|
|
|
528
|
1 |
|
continue; |
529
|
|
|
} |
530
|
|
|
|
531
|
1 |
|
throw new RuntimeException( |
532
|
1 |
|
sprintf( |
533
|
|
|
'Parameter "option_attributes" expects an array of key => value where value is of type' |
534
|
1 |
|
. '"string" or "callable". Value of type "%s" found.', |
535
|
1 |
|
gettype($optionValue) |
536
|
|
|
) |
537
|
|
|
); |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
// If no optgroup_identifier has been configured, apply default handling and continue |
541
|
13 |
|
if ($this->getOptgroupIdentifier() === null) { |
542
|
9 |
|
$options[] = ['label' => $label, 'value' => $value, 'attributes' => $optionAttributes]; |
543
|
|
|
|
544
|
9 |
|
continue; |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
// optgroup_identifier found, handle grouping |
548
|
4 |
|
$optgroupGetter = 'get' . Inflector::classify($this->getOptgroupIdentifier()); |
549
|
|
|
|
550
|
4 |
|
if (! is_callable([$object, $optgroupGetter])) { |
551
|
1 |
|
throw new RuntimeException( |
552
|
1 |
|
sprintf('Method "%s::%s" is not callable', $this->targetClass, $optgroupGetter) |
553
|
|
|
); |
554
|
|
|
} |
555
|
|
|
|
556
|
3 |
|
$optgroup = $object->{$optgroupGetter}(); |
557
|
|
|
|
558
|
|
|
// optgroup_identifier contains a valid group-name. Handle default grouping. |
559
|
3 |
|
if ($optgroup !== null && trim($optgroup) !== '') { |
560
|
2 |
|
$options[$optgroup]['label'] = $optgroup; |
561
|
2 |
|
$options[$optgroup]['options'][] = [ |
562
|
2 |
|
'label' => $label, |
563
|
2 |
|
'value' => $value, |
564
|
2 |
|
'attributes' => $optionAttributes, |
565
|
|
|
]; |
566
|
|
|
|
567
|
2 |
|
continue; |
568
|
|
|
} |
569
|
|
|
|
570
|
2 |
|
$optgroupDefault = $this->getOptgroupDefault(); |
571
|
|
|
|
572
|
|
|
// No optgroup_default has been provided. Line up without a group |
573
|
2 |
|
if ($optgroupDefault === null) { |
574
|
1 |
|
$options[] = ['label' => $label, 'value' => $value, 'attributes' => $optionAttributes]; |
575
|
|
|
|
576
|
1 |
|
continue; |
577
|
|
|
} |
578
|
|
|
|
579
|
|
|
// Line up entry with optgroup_default |
580
|
1 |
|
$options[$optgroupDefault]['label'] = $optgroupDefault; |
581
|
1 |
|
$options[$optgroupDefault]['options'][] = [ |
582
|
1 |
|
'label' => $label, |
583
|
1 |
|
'value' => $value, |
584
|
1 |
|
'attributes' => $optionAttributes, |
585
|
|
|
]; |
586
|
|
|
} |
587
|
|
|
|
588
|
14 |
|
$this->valueOptions = $options; |
589
|
14 |
|
} |
590
|
|
|
} |
591
|
|
|
|
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: