Choices   A
last analyzed

Complexity

Total Complexity 7

Size/Duplication

Total Lines 73
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 17
dl 0
loc 73
ccs 19
cts 19
cp 1
rs 10
c 0
b 0
f 0
wmc 7

3 Methods

Rating   Name   Duplication   Size   Complexity  
A generateCodeForChildBuilder() 0 16 3
A __construct() 0 30 1
A applyOnChildBuilder() 0 13 3
1
<?php
2
3
namespace Bdf\Form\Attribute\Element;
4
5
use Attribute;
6
use Bdf\Form\AbstractElementBuilder;
7
use Bdf\Form\Attribute\AttributeForm;
8
use Bdf\Form\Attribute\ChildBuilderAttributeInterface;
9
use Bdf\Form\Attribute\Processor\CodeGenerator\AttributesProcessorGenerator;
10
use Bdf\Form\Attribute\Processor\GenerateConfiguratorStrategy;
11
use Bdf\Form\Child\ChildBuilderInterface;
12
use Bdf\Form\Choice\ArrayChoice;
13
use Bdf\Form\Choice\Choiceable;
14
use Bdf\Form\Choice\ChoiceBuilderTrait;
15
use Bdf\Form\Choice\ChoiceInterface;
16
use Bdf\Form\Choice\LazyChoice;
17
use Bdf\Form\ElementBuilderInterface;
18
use Bdf\Form\Leaf\StringElementBuilder;
19
use Nette\PhpGenerator\Literal;
20
21
/**
22
 * Define available values choice for the element
23
 *
24
 * The choices can be :
25
 * - a simple array of values (without labels)
26
 * - an associative array for provide a label (in key), and inner value (in value)
27
 * - a method name for resolving choices in lazy way
28
 *
29
 * Note: this attribute is not repeatable
30
 *
31
 * This attribute is equivalent to call :
32
 * <code>
33
 * $builder->string('foo')->choices(['bar', 'baz']);
34
 * </code>
35
 *
36
 * Usage:
37
 * <code>
38
 * class MyForm extends AttributeForm
39
 * {
40
 *     #[Choices(['bar', 'rab'])]
41
 *     private StringElement $foo;
42
 *
43
 *     #[Choices(['My label' => 'v1', 'Other label' => 'v2'])]
44
 *     private StringElement $bar;
45
 *
46
 *     #[Choices('loadBazValues', 'Invalid value')]
47
 *     private StringElement $baz;
48
 *
49
 *     // For dynamic choices, or with complex logic
50
 *     public function loadBazValues(): array
51
 *     {
52
 *         $values = [];
53
 *
54
 *         foreach (BazEntity::all() as $baz) {
55
 *             $values[$baz->label()] = $baz->id();
56
 *         }
57
 *
58
 *         return $values;
59
 *     }
60
 * }
61
 * </code>
62
 *
63
 * @implements ChildBuilderAttributeInterface<\Bdf\Form\ElementBuilderInterface>
64
 *
65
 * @see ChoiceBuilderTrait::choices() The called method
66
 * @see Choiceable Supported element type
67
 * @see ArrayChoice Used when an array is given as parameter
68
 * @see LazyChoice Used when a method name is given as parameter
69
 */
70
#[Attribute(Attribute::TARGET_PROPERTY)]
71
final class Choices implements ChildBuilderAttributeInterface
72
{
73 3
    public function __construct(
74
        /**
75
         * Choice provider
76
         *
77
         * Can be a method name for load choices. The method must be public and declared on the form class,
78
         * with the prototype `public function (): array`
79
         *
80
         * If the value is an array, the key will be used as label (displayed value), and the value as real value
81
         * The label is not required.
82
         *
83
         * @var literal-string|array
84
         * @readonly
85
         */
86
        private string|array $choices,
87
        /**
88
         * The error message
89
         * If not provided, a default message will be used
90
         *
91
         * @var string|null
92
         * @readonly
93
         */
94
        private ?string $message = null,
95
        /**
96
         * Extra constraint options
97
         *
98
         * @var array
99
         * @readonly
100
         */
101
        private array $options = [],
102
    ) {
103 3
    }
104
105
    /**
106
     * {@inheritdoc}
107
     */
108 1
    public function applyOnChildBuilder(AttributeForm $form, ChildBuilderInterface $builder): void
109
    {
110 1
        $options = $this->options;
111
112 1
        if ($this->message) {
113 1
            $options['message'] = $options['multipleMessage'] = $this->message;
114
        }
115
116
        // Q&D fix for psalm because it does not recognize trait as type
117
        /** @var StringElementBuilder $builder */
118 1
        $builder->choices(
119 1
            is_string($this->choices) ? new LazyChoice([$form, $this->choices]) : $this->choices,
120 1
            $options
121 1
        );
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127 2
    public function generateCodeForChildBuilder(string $name, AttributesProcessorGenerator $generator, AttributeForm $form): void
128
    {
129 2
        $options = $this->options;
130
131 2
        if ($this->message) {
132 2
            $options['message'] = $options['multipleMessage'] = $this->message;
133
        }
134
135 2
        if (is_string($this->choices)) {
136 2
            $generator->use(LazyChoice::class);
137 2
            $choices = new Literal('new LazyChoice([$form, ?])', [$this->choices]);
138
        } else {
139 2
            $choices = $this->choices;
140
        }
141
142 2
        $generator->line('$?->choices(?, ?);', [$name, $choices, $options]);
143
    }
144
}
145