AbstractFormRenderer::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
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