-
Notifications
You must be signed in to change notification settings - Fork 554
/
BaseFormField.php
185 lines (151 loc) · 4.85 KB
/
BaseFormField.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
<?php
namespace A17\Twill\Services\Forms\Fields;
use A17\Twill\Services\Forms\Contracts\CanRenderForBlocks;
use A17\Twill\Services\Forms\Traits\RenderForBlocks;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Str;
use ReflectionClass;
abstract class BaseFormField implements CanRenderForBlocks
{
use RenderForBlocks;
/**
* @var \A17\Twill\View\Components\Fields\TwillFormComponent
*/
protected function __construct(
protected string $component,
protected ?string $name = null,
protected ?string $label = null,
protected ?string $note = null,
protected ?bool $required = false,
protected ?bool $disabled = false,
protected mixed $default = null,
protected ?array $connectedTo = null,
/**
* A list of mandatory properties in order of their component
* constructor.
*/
protected array $mandatoryProperties = []
) {
}
abstract public static function make(): static;
/**
* Set the name of the field, if no label is set yet, this method will also update that.
*/
public function name(string $name): static
{
$this->name = $name;
if (! $this->label) {
$this->label(Str::headline($name));
}
return $this;
}
public function default(mixed $default): static
{
$this->default = $default;
return $this;
}
/**
* Set the label of the field, you can use twillTrans('') Laravel translatable strings here.
*/
public function label(string $label): static
{
$this->label = $label;
return $this;
}
/**
* Add a note to the field to display on the form.
*/
public function note(string $note): static
{
$this->note = $note;
return $this;
}
/**
* Marks the field as mandatory, however you still need to add validation rules.
*/
public function required(bool $required = true): static
{
$this->required = $required;
return $this;
}
/**
* Marks the field as disabled.
*
* There might be some fields not supporting this.
*/
public function disabled(bool $disabled = true): static
{
$this->disabled = $disabled;
return $this;
}
public function connectedTo(string $fieldName, mixed $fieldValues, array $options = [])
{
$this->connectedTo = [
'fieldName' => $fieldName,
'fieldValues' => $fieldValues,
...$options,
];
return $this;
}
public function render(): View
{
$vars = collect(get_object_vars($this))->except(['component']);
$args = [];
$class = new ReflectionClass($this->component);
foreach ($class->getConstructor()->getParameters() as $parameter) {
if ($vars->has($parameter->getName())) {
$args[$parameter->getName()] = $this->getValue($parameter->getName());
}
}
$args += $this->getAdditionalConstructorArguments();
if ($this->mandatoryProperties !== []) {
foreach ($this->mandatoryProperties as $property) {
if (! $this->{$property}) {
throw new \InvalidArgumentException(
"Missing required field property '$property' on " . $this::class
);
}
$args[$property] = $this->getValue($property);
}
// If the view component has mandatory parameters we construct it
// slightly different from regular ones.
// This allows more control.
$component = $class->newInstance(...$args);
} else {
/** @var \A17\Twill\View\Components\Fields\TwillFormComponent $component */
$component = $class->newInstance(...$args);
}
if ($this->connectedTo) {
return view('twill::partials.form.utils._connected_fields', [
...$this->connectedTo,
'renderForBlocks' => $this->forBlocks(),
'slot' => $component->render(),
]);
}
return $component->render();
}
/**
* Gets the property but also allows the class to overwrite it
* if needed.
*
* This can be done by adding `get{}`
*/
private function getValue(string $property): mixed
{
$method = 'get' . Str::studly($property);
if (method_exists($this, $method)) {
return $this->$method();
}
return $this->$property;
}
/**
* In render we dynamically build the constructor arguments.
*
* In exceptional cases such as browser we have more conditionals and
* we can use this method to set those.
*/
protected function getAdditionalConstructorArguments(): array
{
return [];
}
}