Completed
Push — master ( e0017c...6b1304 )
by Tom
14s queued 11s
created

src/DoctrineModule/Form/Element/Proxy.php (1 issue)

Check for unnecessary variable assignments.

Unused Code Major

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\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 interface_exists;
24
use function is_callable;
25
use function is_object;
26
use function is_string;
27
use function method_exists;
28
use function sprintf;
29
use function strtolower;
30
use function trim;
31
32
class Proxy implements ObjectManagerAwareInterface
33
{
34
    use ArrayOrTraversableGuardTrait;
35
36
    /** @var mixed[]|Traversable */
37
    protected $objects;
38
39
    /** @var string */
40
    protected $targetClass;
41
42
    /** @var mixed[] */
43
    protected $valueOptions = [];
44
45
    /** @var mixed[] */
46
    protected $findMethod = [];
47
48
    /** @var mixed */
49
    protected $property;
50
51
    /** @var mixed[] */
52
    protected $optionAttributes = [];
53
54
    /** @var callable $labelGenerator A callable used to create a label based on an item in the collection an Entity */
55
    protected $labelGenerator;
56
57
    /** @var bool|null */
58
    protected $isMethod;
59
60
    /** @var ObjectManager */
61
    protected $objectManager;
62
63
    /** @var bool */
64
    protected $displayEmptyItem = false;
65
66
    /** @var string */
67
    protected $emptyItemLabel = '';
68
69
    /** @var string|null */
70
    protected $optgroupIdentifier;
71
72
    /** @var string|null */
73
    protected $optgroupDefault;
74
75
    /**
76
     * @param mixed[] $options
77
     */
78 23
    public function setOptions(array $options) : void
79
    {
80 23
        if (isset($options['object_manager'])) {
81 22
            $this->setObjectManager($options['object_manager']);
82
        }
83
84 23
        if (isset($options['target_class'])) {
85 22
            $this->setTargetClass($options['target_class']);
86
        }
87
88 23
        if (isset($options['property'])) {
89 3
            $this->setProperty($options['property']);
90
        }
91
92 23
        if (isset($options['label_generator'])) {
93 2
            $this->setLabelGenerator($options['label_generator']);
94
        }
95
96 23
        if (isset($options['find_method'])) {
97 4
            $this->setFindMethod($options['find_method']);
98
        }
99
100 23
        if (isset($options['is_method'])) {
101 1
            $this->setIsMethod($options['is_method']);
102
        }
103
104 23
        if (isset($options['display_empty_item'])) {
105 1
            $this->setDisplayEmptyItem($options['display_empty_item']);
106
        }
107
108 23
        if (isset($options['empty_item_label'])) {
109 1
            $this->setEmptyItemLabel($options['empty_item_label']);
110
        }
111
112 23
        if (isset($options['option_attributes'])) {
113 3
            $this->setOptionAttributes($options['option_attributes']);
114
        }
115
116 23
        if (isset($options['optgroup_identifier'])) {
117 4
            $this->setOptgroupIdentifier($options['optgroup_identifier']);
118
        }
119
120 23
        if (! isset($options['optgroup_default'])) {
121 23
            return;
122
        }
123
124 1
        $this->setOptgroupDefault($options['optgroup_default']);
125 1
    }
126
127
    /**
128
     * @return mixed
129
     */
130 22
    public function getValueOptions()
131
    {
132 22
        if (empty($this->valueOptions)) {
133 22
            $this->loadValueOptions();
134
        }
135
136 14
        return $this->valueOptions;
137
    }
138
139
    /**
140
     * @return mixed
141
     */
142 20
    public function getObjects()
143
    {
144 20
        $this->loadObjects();
145
146 16
        return $this->objects;
147
    }
148
149
    /**
150
     * Set the label for the empty option
151
     */
152 1
    public function setEmptyItemLabel(string $emptyItemLabel) : Proxy
153
    {
154 1
        $this->emptyItemLabel = $emptyItemLabel;
155
156 1
        return $this;
157
    }
158
159 1
    public function getEmptyItemLabel() : string
160
    {
161 1
        return $this->emptyItemLabel;
162
    }
163
164
    /**
165
     * @return mixed[]
166
     */
167 14
    public function getOptionAttributes() : array
168
    {
169 14
        return $this->optionAttributes;
170
    }
171
172
    /**
173
     * @param mixed[] $optionAttributes
174
     */
175 3
    public function setOptionAttributes(array $optionAttributes) : void
176
    {
177 3
        $this->optionAttributes = $optionAttributes;
178 3
    }
179
180
    /**
181
     * Set a flag, whether to include the empty option at the beginning or not
182
     */
183 1
    public function setDisplayEmptyItem(bool $displayEmptyItem) : Proxy
184
    {
185 1
        $this->displayEmptyItem = $displayEmptyItem;
186
187 1
        return $this;
188
    }
189
190
    public function getDisplayEmptyItem() : bool
191
    {
192
        return $this->displayEmptyItem;
193
    }
194
195
    /**
196
     * Set the object manager
197
     */
198 22
    public function setObjectManager(ObjectManager $objectManager) : void
199
    {
200 22
        $this->objectManager = $objectManager;
201 22
    }
202
203
    /**
204
     * Get the object manager
205
     */
206 20
    public function getObjectManager() : ObjectManager
207
    {
208 20
        return $this->objectManager;
209
    }
210
211
    /**
212
     * Set the FQCN of the target object
213
     */
214 22
    public function setTargetClass(string $targetClass) : Proxy
215
    {
216 22
        $this->targetClass = $targetClass;
217
218 22
        return $this;
219
    }
220
221
    /**
222
     * Get the target class
223
     */
224 20
    public function getTargetClass() : string
225
    {
226 20
        return $this->targetClass;
227
    }
228
229
    /**
230
     * Set the property to use as the label in the options
231
     */
232 3
    public function setProperty(string $property) : Proxy
233
    {
234 3
        $this->property = $property;
235
236 3
        return $this;
237
    }
238
239
    /**
240
     * @return mixed
241
     */
242
    public function getProperty()
243
    {
244
        return $this->property;
245
    }
246
247
    /**
248
     * Set the label generator callable that is responsible for generating labels for the items in the collection
249
     *
250
     * @param callable $callable A callable used to create a label based off of an Entity
251
     */
252 1
    public function setLabelGenerator(callable $callable) : void
253
    {
254 1
        $this->labelGenerator = $callable;
255 1
    }
256
257 14
    public function getLabelGenerator() : ?callable
258
    {
259 14
        return $this->labelGenerator;
260
    }
261
262 13
    public function getOptgroupIdentifier() : ?string
263
    {
264 13
        return $this->optgroupIdentifier;
265
    }
266
267 4
    public function setOptgroupIdentifier(string $optgroupIdentifier) : void
268
    {
269 4
        $this->optgroupIdentifier = (string) $optgroupIdentifier;
270 4
    }
271
272 2
    public function getOptgroupDefault() : ?string
273
    {
274 2
        return $this->optgroupDefault;
275
    }
276
277 1
    public function setOptgroupDefault(string $optgroupDefault) : void
278
    {
279 1
        $this->optgroupDefault = (string) $optgroupDefault;
280 1
    }
281
282
    /**
283
     * Set if the property is a method to use as the label in the options
284
     */
285 1
    public function setIsMethod(bool $method) : Proxy
286
    {
287 1
        $this->isMethod = (bool) $method;
288
289 1
        return $this;
290
    }
291
292 3
    public function getIsMethod() : ?bool
293
    {
294 3
        return $this->isMethod;
295
    }
296
297
    /** Set the findMethod property to specify the method to use on repository
298
     *
299
     * @param mixed[] $findMethod
300
     */
301 4
    public function setFindMethod(array $findMethod) : Proxy
302
    {
303 4
        $this->findMethod = $findMethod;
304
305 4
        return $this;
306
    }
307
308
    /**
309
     * Get findMethod definition
310
     *
311
     * @return mixed[]
312
     */
313 20
    public function getFindMethod() : array
314
    {
315 20
        return $this->findMethod;
316
    }
317
318
    /**
319
     * @param mixed $targetEntity
320
     */
321 14
    protected function generateLabel($targetEntity) : ?string
322
    {
323 14
        if ($this->getLabelGenerator() === null) {
324 13
            return null;
325
        }
326
327 1
        return call_user_func($this->getLabelGenerator(), $targetEntity);
328
    }
329
330
    /**
331
     * @param mixed $value
332
     *
333
     * @return mixed[]|mixed|object
334
     *
335
     * @throws RuntimeException
336
     */
337
    public function getValue($value)
338
    {
339
        if (! $this->getObjectManager()) {
340
            throw new RuntimeException('No object manager was set');
341
        }
342
343
        if (! $this->getTargetClass()) {
344
            throw new RuntimeException('No target class was set');
345
        }
346
347
        $metadata = $this->getObjectManager()->getClassMetadata($this->getTargetClass());
348
349
        if (is_object($value)) {
350
            if ($value instanceof Collection) {
351
                $data = [];
352
353
                foreach ($value as $object) {
354
                    $values = $metadata->getIdentifierValues($object);
355
                    $data[] = array_shift($values);
356
                }
357
358
                $value = $data;
359
            } else {
360
                $metadata   = $this->getObjectManager()->getClassMetadata(get_class($value));
361
                $identifier = $metadata->getIdentifierFieldNames();
362
363
                // TODO: handle composite (multiple) identifiers
364
                if ($identifier !== null && count($identifier) > 1) {
365
                    //$value = $key;
366
                    $todo = true;
0 ignored issues
show
$todo is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
367
                } else {
368
                    $value = current($metadata->getIdentifierValues($value));
369
                }
370
            }
371
        }
372
373
        return $value;
374
    }
375
376
    /**
377
     * Load objects
378
     *
379
     * @throws RuntimeException
380
     * @throws Exception\InvalidRepositoryResultException
381
     */
382 20
    protected function loadObjects() : void
383
    {
384 20
        if (! empty($this->objects)) {
385
            return;
386
        }
387
388 20
        $findMethod = (array) $this->getFindMethod();
389
390 20
        if (! $findMethod) {
391 16
            $findMethodName = 'findAll';
392 16
            $repository     = $this->objectManager->getRepository($this->targetClass);
393 16
            $objects        = $repository->findAll();
394
        } else {
395 4
            if (! isset($findMethod['name'])) {
396 1
                throw new RuntimeException('No method name was set');
397
            }
398
399 3
            $findMethodName   = $findMethod['name'];
400 3
            $findMethodParams = isset($findMethod['params']) ? array_change_key_case($findMethod['params']) : [];
401 3
            $repository       = $this->objectManager->getRepository($this->targetClass);
402
403 3
            if (! method_exists($repository, $findMethodName)) {
404 1
                throw new RuntimeException(
405 1
                    sprintf(
406 1
                        'Method "%s" could not be found in repository "%s"',
407 1
                        $findMethodName,
408 1
                        get_class($repository)
409
                    )
410
                );
411
            }
412
413 2
            $r    = new ReflectionMethod($repository, $findMethodName);
414 2
            $args = [];
415
416 2
            foreach ($r->getParameters() as $param) {
417 2
                if (array_key_exists(strtolower($param->getName()), $findMethodParams)) {
418 1
                    $args[] = $findMethodParams[strtolower($param->getName())];
419 2
                } elseif ($param->isDefaultValueAvailable()) {
420 1
                    $args[] = $param->getDefaultValue();
421 1
                } elseif (! $param->isOptional()) {
422 1
                    throw new RuntimeException(
423 1
                        sprintf(
424
                            'Required parameter "%s" with no default value for method "%s" in repository "%s"'
425 1
                            . ' was not provided',
426 1
                            $param->getName(),
427 1
                            $findMethodName,
428 1
                            get_class($repository)
429
                        )
430
                    );
431
                }
432
            }
433
434 1
            $objects = $r->invokeArgs($repository, $args);
435
        }
436
437 17
        $this->guardForArrayOrTraversable(
438 17
            $objects,
439 17
            sprintf('%s::%s() return value', get_class($repository), $findMethodName),
440 17
            'DoctrineModule\Form\Element\Exception\InvalidRepositoryResultException'
441
        );
442
443 16
        $this->objects = $objects;
444 16
    }
445
446
    /**
447
     * Load value options
448
     *
449
     * @throws RuntimeException
450
     */
451 22
    protected function loadValueOptions() : void
452
    {
453 22
        if (! $this->objectManager) {
454 1
            throw new RuntimeException('No object manager was set');
455
        }
456
457 21
        if (! $this->targetClass) {
458 1
            throw new RuntimeException('No target class was set');
459
        }
460
461 20
        $metadata         = $this->getObjectManager()->getClassMetadata($this->getTargetClass());
462 20
        $identifier       = $metadata->getIdentifierFieldNames();
463 20
        $objects          = $this->getObjects();
464 16
        $options          = [];
465 16
        $optionAttributes = [];
466
467 16
        if ($this->displayEmptyItem) {
468 1
            $options[''] = $this->getEmptyItemLabel();
469
        }
470
471 16
        foreach ($objects as $key => $object) {
472 14
            $generatedLabel = $this->generateLabel($object);
473 14
            if ($generatedLabel !== null) {
474 1
                $label = $generatedLabel;
475 13
            } elseif ($this->property) {
476 3
                $property = $this->property;
477 3
                if (($this->getIsMethod() === false || $this->getIsMethod() === null)
478 3
                    && ! $metadata->hasField($property)
479
                ) {
480
                    throw new RuntimeException(
481
                        sprintf(
482
                            'Property "%s" could not be found in object "%s"',
483
                            $property,
484
                            $this->getTargetClass()
485
                        )
486
                    );
487
                }
488
489 3
                $getter = 'get' . Inflector::classify($property);
490
491 3
                if (! is_callable([$object, $getter])) {
492
                    throw new RuntimeException(
493
                        sprintf('Method "%s::%s" is not callable', $this->targetClass, $getter)
494
                    );
495
                }
496
497 3
                $label = $object->{$getter}();
498
            } else {
499 10
                if (! is_callable([$object, '__toString'])) {
500
                    throw new RuntimeException(
501
                        sprintf(
502
                            '%s must have a "__toString()" method defined if you have not set a property'
503
                            . ' or method to use.',
504
                            $this->getTargetClass()
505
                        )
506
                    );
507
                }
508
509 10
                $label = (string) $object;
510
            }
511
512 14
            if ($identifier !== null && count($identifier) > 1) {
513
                $value = $key;
514
            } else {
515 14
                $value = current($metadata->getIdentifierValues($object));
516
            }
517
518 14
            foreach ($this->getOptionAttributes() as $optionKey => $optionValue) {
519 3
                if (is_string($optionValue)) {
520 1
                    $optionAttributes[$optionKey] = $optionValue;
521
522 1
                    continue;
523
                }
524
525 2
                if (is_callable($optionValue)) {
526 1
                    $callableValue                = call_user_func($optionValue, $object);
527 1
                    $optionAttributes[$optionKey] = (string) $callableValue;
528
529 1
                    continue;
530
                }
531
532 1
                throw new RuntimeException(
533 1
                    sprintf(
534
                        'Parameter "option_attributes" expects an array of key => value where value is of type'
535 1
                        . '"string" or "callable". Value of type "%s" found.',
536 1
                        gettype($optionValue)
537
                    )
538
                );
539
            }
540
541
            // If no optgroup_identifier has been configured, apply default handling and continue
542 13
            if ($this->getOptgroupIdentifier() === null) {
543 9
                $options[] = ['label' => $label, 'value' => $value, 'attributes' => $optionAttributes];
544
545 9
                continue;
546
            }
547
548
            // optgroup_identifier found, handle grouping
549 4
            $optgroupGetter = 'get' . Inflector::classify($this->getOptgroupIdentifier());
550
551 4
            if (! is_callable([$object, $optgroupGetter])) {
552 1
                throw new RuntimeException(
553 1
                    sprintf('Method "%s::%s" is not callable', $this->targetClass, $optgroupGetter)
554
                );
555
            }
556
557 3
            $optgroup = $object->{$optgroupGetter}();
558
559
            // optgroup_identifier contains a valid group-name. Handle default grouping.
560 3
            if ($optgroup !== null && trim($optgroup) !== '') {
561 2
                $options[$optgroup]['label']     = $optgroup;
562 2
                $options[$optgroup]['options'][] = [
563 2
                    'label'      => $label,
564 2
                    'value'      => $value,
565 2
                    'attributes' => $optionAttributes,
566
                ];
567
568 2
                continue;
569
            }
570
571 2
            $optgroupDefault = $this->getOptgroupDefault();
572
573
            // No optgroup_default has been provided. Line up without a group
574 2
            if ($optgroupDefault === null) {
575 1
                $options[] = ['label' => $label, 'value' => $value, 'attributes' => $optionAttributes];
576
577 1
                continue;
578
            }
579
580
            // Line up entry with optgroup_default
581 1
            $options[$optgroupDefault]['label']     = $optgroupDefault;
582 1
            $options[$optgroupDefault]['options'][] = [
583 1
                'label'      => $label,
584 1
                'value'      => $value,
585 1
                'attributes' => $optionAttributes,
586
            ];
587
        }
588
589 14
        $this->valueOptions = $options;
590 14
    }
591
}
592
593
interface_exists(ObjectManager::class);
594