1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Del\Form\Renderer; |
6
|
|
|
|
7
|
|
|
use Del\Form\Collection\FieldCollection; |
8
|
|
|
use Del\Form\AbstractForm; |
9
|
|
|
use Del\Form\Field\FieldInterface; |
10
|
|
|
use Del\Form\FormInterface; |
11
|
|
|
use Del\Form\Renderer\Error\DefaultErrorRender; |
12
|
|
|
use Del\Form\Renderer\Error\ErrorRendererInterface; |
13
|
|
|
use Del\Form\Traits\HasDomTrait; |
14
|
|
|
use DOMDocument; |
15
|
|
|
use DOMElement; |
16
|
|
|
|
17
|
|
|
abstract class AbstractFormRenderer implements FormRendererInterface |
18
|
|
|
{ |
19
|
|
|
use HasDomTrait; |
20
|
|
|
|
21
|
|
|
protected DOMElement $form; |
22
|
|
|
protected bool $displayErrors; |
23
|
|
|
protected ErrorRendererInterface $errorRenderer; |
24
|
|
|
protected DOMElement $label; |
25
|
|
|
protected mixed $element; |
26
|
|
|
protected ?DOMElement $errors = null; |
27
|
|
|
protected DOMElement $block; |
28
|
|
|
protected DOMElement $dynamicContainerBlock; |
29
|
|
|
protected FieldInterface $field; |
30
|
|
|
private bool $includeDynamicFormJavascript = false; |
31
|
|
|
private string $dynamicFormParentName = ''; |
32
|
|
|
private bool $dynamicFormVisible = false; |
33
|
|
|
|
34
|
67 |
|
public function __construct() |
35
|
|
|
{ |
36
|
67 |
|
$this->resetDom(); |
37
|
|
|
} |
38
|
|
|
|
39
|
67 |
|
private function resetDom(): void |
40
|
|
|
{ |
41
|
67 |
|
$this->setDom(new DOMDocument()); |
42
|
67 |
|
$this->form = $this->getDom()->createElement('form'); |
43
|
67 |
|
$this->errorRenderer = new DefaultErrorRender($this->dom); |
44
|
|
|
} |
45
|
|
|
|
46
|
36 |
|
public function render(FormInterface $form, $displayErrors = true): string |
47
|
|
|
{ |
48
|
36 |
|
$this->displayErrors = $displayErrors; |
49
|
36 |
|
$this->setFormAttributes($form); |
50
|
|
|
|
51
|
36 |
|
$fields = $form->getFields(); |
52
|
36 |
|
$this->processFields($fields); |
53
|
|
|
|
54
|
29 |
|
$this->getDom()->appendChild($this->form); |
55
|
29 |
|
$html = $this->getDom()->saveHTML(); |
56
|
29 |
|
$this->resetDom(); |
57
|
|
|
|
58
|
29 |
|
$html .= $this->addDynamicFormJavascript(); |
59
|
|
|
|
60
|
29 |
|
return $html; |
61
|
|
|
} |
62
|
|
|
|
63
|
36 |
|
private function setFormAttributes(FormInterface $form): void |
64
|
|
|
{ |
65
|
36 |
|
$attributes = $form->getAttributes(); |
66
|
36 |
|
foreach ($attributes as $key => $value) { |
67
|
36 |
|
$this->form->setAttribute($key, $value); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
// set Id as name or method as post if not set |
71
|
36 |
|
$method = $this->getMethod($form); |
72
|
36 |
|
$id = $this->getId($form); |
73
|
|
|
|
74
|
36 |
|
$this->form->setAttribute('id', $id); |
75
|
36 |
|
$this->form->setAttribute('method', $method); |
76
|
|
|
} |
77
|
|
|
|
78
|
36 |
|
private function getMethod(FormInterface $form): string |
79
|
|
|
{ |
80
|
36 |
|
return $form->getMethod() ?: AbstractForm::METHOD_POST; |
81
|
|
|
} |
82
|
|
|
|
83
|
36 |
|
private function getId(FormInterface $form): string |
84
|
|
|
{ |
85
|
36 |
|
return $form->getId() ?: $this->form->getAttribute('name'); |
86
|
|
|
} |
87
|
|
|
|
88
|
36 |
|
private function processFields(FieldCollection $fields, $dynamicTriggerValue = null): void |
89
|
|
|
{ |
90
|
36 |
|
$count = $fields->count(); |
91
|
36 |
|
$x = 1; |
92
|
36 |
|
$fields->rewind(); |
93
|
|
|
|
94
|
36 |
|
while ($fields->valid()) { |
95
|
34 |
|
$this->field = $fields->current(); |
96
|
34 |
|
$finaliseDynamicBlock = ($x == $count) ? true : false; |
97
|
34 |
|
$this->renderField($dynamicTriggerValue, $finaliseDynamicBlock); |
98
|
27 |
|
$x++; |
99
|
27 |
|
$fields->next(); |
100
|
|
|
} |
101
|
|
|
|
102
|
29 |
|
$fields->rewind(); |
103
|
|
|
} |
104
|
|
|
|
105
|
34 |
|
public function renderField($dynamicTriggerValue = null, $finaliseDynamicBlock = false): void |
106
|
|
|
{ |
107
|
34 |
|
$this->createNewDynamicContainerBlockIfNeeded($dynamicTriggerValue); |
108
|
|
|
|
109
|
34 |
|
$this->block = $this->createElement('div'); |
110
|
34 |
|
$this->label = $this->renderFieldLabel(); |
111
|
34 |
|
$this->element = $this->field->getRenderer()->render($this->dom, $this->field); |
112
|
27 |
|
$this->errors = $this->field->isValid() ? null : $this->renderError(); |
113
|
27 |
|
$this->block = $this->renderFieldBlock(); |
114
|
|
|
|
115
|
27 |
|
is_null($dynamicTriggerValue) |
116
|
27 |
|
? $this->form->appendChild($this->block) |
117
|
1 |
|
: $this->dynamicContainerBlock->appendChild($this->block); |
118
|
|
|
|
119
|
27 |
|
$this->dynamicFormCheck(); |
120
|
27 |
|
$this->finaliseDynamicBlockIfNeeded($finaliseDynamicBlock); |
121
|
|
|
} |
122
|
|
|
|
123
|
34 |
|
private function createNewDynamicContainerBlockIfNeeded($dynamicTriggerValue): void |
124
|
|
|
{ |
125
|
34 |
|
if (!isset($this->dynamicContainerBlock) && $dynamicTriggerValue !== null) { |
126
|
1 |
|
$this->dynamicContainerBlock = $this->createElement('div'); |
127
|
1 |
|
$this->dynamicContainerBlock->setAttribute('data-dynamic-form', $this->dynamicFormParentName); |
128
|
1 |
|
$this->dynamicContainerBlock->setAttribute('data-dynamic-form-trigger-value', (string) $dynamicTriggerValue); |
129
|
1 |
|
$this->dynamicContainerBlock->setAttribute('class', 'dynamic-form-block trigger'.$this->dynamicFormParentName); |
130
|
1 |
|
$this->dynamicContainerBlock->setAttribute('id', $this->dynamicFormParentName.$dynamicTriggerValue); |
131
|
1 |
|
$this->dynamicFormVisible === false ? $this->dynamicContainerBlock->setAttribute('style', 'display: none;') : null; |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Checks current field being processed for dynamic sub forms |
137
|
|
|
*/ |
138
|
27 |
|
private function dynamicFormCheck(): void |
139
|
|
|
{ |
140
|
27 |
|
if ($this->field->hasDynamicForms()) { |
141
|
1 |
|
$this->dynamicFormParentName = $this->field->getName(); |
142
|
1 |
|
$value = $this->field->getValue(); |
143
|
1 |
|
$forms = $this->field->getDynamicForms(); |
144
|
1 |
|
$this->includeDynamicFormJavascript = true; |
145
|
1 |
|
foreach ($forms as $dynamicTriggerValue => $form) { |
146
|
1 |
|
$this->dynamicFormVisible = ($value == $dynamicTriggerValue); |
147
|
1 |
|
$dynamicFields = $form->getFields(); |
148
|
1 |
|
$this->processFields($dynamicFields, $dynamicTriggerValue); |
149
|
|
|
} |
150
|
1 |
|
unset($this->dynamicFormParentName); |
151
|
|
|
} |
152
|
|
|
} |
153
|
|
|
|
154
|
27 |
|
private function finaliseDynamicBlockIfNeeded(bool $finaliseDynamicBlock) |
155
|
|
|
{ |
156
|
27 |
|
if (isset($this->dynamicContainerBlock) && $finaliseDynamicBlock === true) { |
157
|
1 |
|
$this->form->appendChild($this->dynamicContainerBlock); |
158
|
1 |
|
unset($this->dynamicContainerBlock); |
159
|
|
|
} |
160
|
|
|
} |
161
|
|
|
|
162
|
6 |
|
public function renderError(): ?DOMElement |
163
|
|
|
{ |
164
|
6 |
|
$errorBlock = null; |
165
|
|
|
|
166
|
6 |
|
if ($this->errorRenderer->shouldRender($this->field) && $this->displayErrors === true) { |
167
|
4 |
|
$this->block->setAttribute('class', 'has-error '); |
168
|
4 |
|
$errorBlock = $this->errorRenderer->render($this->field); |
169
|
|
|
} |
170
|
|
|
|
171
|
6 |
|
return $errorBlock; |
172
|
|
|
} |
173
|
|
|
|
174
|
34 |
|
protected function createLabelElement(): DOMElement |
175
|
|
|
{ |
176
|
34 |
|
$label = $this->createElement('label'); |
177
|
34 |
|
$label->setAttribute('for', $this->field->getId() ?? ''); |
178
|
34 |
|
if ($this->field->isRequired()) { |
179
|
6 |
|
$label = $this->addRequiredAsterisk($label); |
180
|
|
|
} |
181
|
34 |
|
return $label; |
182
|
|
|
} |
183
|
|
|
|
184
|
6 |
|
public function addRequiredAsterisk(DomElement $label): DomElement |
185
|
|
|
{ |
186
|
6 |
|
$span = $this->createElement('span'); |
187
|
6 |
|
$span->setAttribute('class', 'text-danger'); |
188
|
6 |
|
$text = $this->createText('* '); |
189
|
6 |
|
$span->appendChild($text); |
190
|
6 |
|
$label->appendChild($span); |
191
|
6 |
|
return $label; |
192
|
|
|
} |
193
|
|
|
|
194
|
29 |
|
private function addDynamicFormJavascript(): string |
195
|
|
|
{ |
196
|
29 |
|
if ($this->includeDynamicFormJavascript === true) { |
197
|
1 |
|
return "<script type=\"text/javascript\"> |
198
|
|
|
$(document).ready(function(){ |
199
|
|
|
$('.dynamic-form-block').each(function(){ |
200
|
|
|
var Id = $(this).prop('id'); |
201
|
|
|
var parentField = $(this).attr('data-dynamic-form'); |
202
|
|
|
var parentValue = $(this).attr('data-dynamic-form-trigger-value'); |
203
|
|
|
|
204
|
|
|
$('input[name=\"'+parentField+'\"]').change(function(){ |
205
|
|
|
var val = $(this).val(); |
206
|
|
|
if (val == parentValue) { |
207
|
|
|
$('.trigger'+parentField).each(function(){ |
208
|
|
|
$(this).attr('style', 'display: none;'); |
209
|
|
|
}); |
210
|
|
|
$('#'+Id).attr('style', 'display: block;'); |
211
|
|
|
} |
212
|
|
|
}); |
213
|
|
|
}); |
214
|
|
|
}); |
215
|
|
|
</script> |
216
|
1 |
|
"; |
217
|
|
|
} |
218
|
28 |
|
return ''; |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
|