AbstractFormRenderer::processFields()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 13
ccs 11
cts 11
cp 1
rs 9.9332
cc 3
nc 3
nop 2
crap 3
1
<?php
2
/**
3
 * User: delboy1978uk
4
 * Date: 07/12/2016
5
 * Time: 01:54
6
 */
7
8
namespace Del\Form\Renderer;
9
10
use Del\Form\Collection\FieldCollection;
11
use Del\Form\AbstractForm;
12
use Del\Form\Field\FieldInterface;
13
use Del\Form\FormInterface;
14
use Del\Form\Renderer\Error\DefaultErrorRender;
15
use Del\Form\Renderer\Error\ErrorRendererInterface;
16
use Del\Form\Traits\HasDomTrait;
17
use DOMDocument;
18
use DOMElement;
19
20
abstract class AbstractFormRenderer implements FormRendererInterface
21
{
22
    use HasDomTrait;
23
24
    /** @var DOMElement $form */
25
    protected $form;
26
27
    /** @var bool $displayErrors */
28
    protected $displayErrors;
29
30
    /** @var ErrorRendererInterface $errorRenderer */
31
    protected $errorRenderer;
32
33
    /** @var DOMElement $label The label element */
34
    protected $label;
35
36
    /** @var DOMElement $element the field element */
37
    protected $element;
38
39
    /** @var null|\DOMElement $errors The error block html */
40
    protected $errors;
41
42
    /** @var DOMElement $block The containing html block */
43
    protected $block;
44
45
    /** @var DOMElement $dynamicContainerBlock */
46
    protected $dynamicContainerBlock;
47
48
    /** @var FieldInterface $field The current field being processed */
49
    protected $field;
50
51
    /** @var bool $includeDynamicFormJavascript */
52
    private $includeDynamicFormJavascript = false;
53
54
    /** @var string $dynamicFormParentName */
55
    private $dynamicFormParentName = '';
56
57
    /** @var bool $dynamicFormVisible */
58
    private $dynamicFormVisible = false;
59
60 66
    public function __construct()
61
    {
62 66
        $this->resetDom();
63
    }
64
65
    /**
66
     *  resets dom
67
     */
68 66
    private function resetDom()
69
    {
70 66
        $this->setDom(new DOMDocument());
71 66
        $this->form = $this->getDom()->createElement('form');
72 66
        $this->errorRenderer = new DefaultErrorRender($this->dom);
73
    }
74
75
    /**
76
     * @param FormInterface $form
77
     * @param bool $displayErrors
78
     * @return string
79
     */
80 36
    public function render(FormInterface $form, $displayErrors = true)
81
    {
82 36
        $this->displayErrors = $displayErrors;
83 36
        $this->setFormAttributes($form);
84
85 36
        $fields = $form->getFields();
86 36
        $this->processFields($fields);
87
88 29
        $this->getDom()->appendChild($this->form);
89 29
        $html = $this->getDom()->saveHTML();
90 29
        $this->resetDom();
91
92 29
        $html .= $this->addDynamicFormJavascript();
93 29
        return $html;
94
    }
95
96
    /**
97
     * @param FormInterface $form
98
     */
99 36
    private function setFormAttributes(FormInterface $form)
100
    {
101 36
        $attributes = $form->getAttributes();
102 36
        foreach ($attributes as $key => $value) {
103 36
            $this->form->setAttribute($key, $value);
104
        }
105
106
        // set Id as name or method as post if not set
107 36
        $method = $this->getMethod($form);
108 36
        $id = $this->getId($form);
109
110 36
        $this->form->setAttribute('id', $id);
111 36
        $this->form->setAttribute('method', $method);
112
    }
113
114
    /**
115
     * @param FormInterface $form
116
     * @return string
117
     */
118 36
    private function getMethod(FormInterface $form)
119
    {
120 36
        return $form->getMethod() ?: AbstractForm::METHOD_POST;
121
    }
122
123
    /**
124
     * @param FormInterface $form
125
     * @return string
126
     */
127 36
    private function getId(FormInterface $form)
128
    {
129 36
        return $form->getId() ?: $this->form->getAttribute('name');
130
    }
131
132
    /**
133
     * @param FieldCollection $fields
134
     * @param string $dynamicTriggerValue
135
     */
136 36
    private function processFields(FieldCollection $fields, $dynamicTriggerValue = null)
137
    {
138 36
        $count = $fields->count();
139 36
        $x = 1;
140 36
        $fields->rewind();
141 36
        while ($fields->valid()) {
142 34
            $this->field = $fields->current();
143 34
            $finaliseDynamicBlock = ($x == $count) ? true : false;
144 34
            $this->renderField($dynamicTriggerValue, $finaliseDynamicBlock);
145 27
            $x++;
146 27
            $fields->next();
147
        }
148 29
        $fields->rewind();
149
    }
150
151
    /**
152
     * @param string $dynamicTriggerValue
153
     * @param bool $finaliseDynamicBlock
154
     */
155 34
    public function renderField($dynamicTriggerValue = null, $finaliseDynamicBlock = false)
156
    {
157 34
        $this->createNewDynamicContainerBlockIfNeeded($dynamicTriggerValue);
158
159 34
        $this->block = $this->createElement('div');
160 34
        $this->label = $this->renderFieldLabel();
161 34
        $this->element = $this->field->getRenderer()->render($this->dom, $this->field);
162 27
        $this->errors = $this->field->isValid() ? null : $this->renderError();
163 27
        $this->block = $this->renderFieldBlock();
164
165 27
        is_null($dynamicTriggerValue)
166 27
            ? $this->form->appendChild($this->block)
167 1
            : $this->dynamicContainerBlock->appendChild($this->block);
168
169 27
        $this->dynamicFormCheck();
170 27
        $this->finaliseDynamicBlockIfNeeded($finaliseDynamicBlock);
171
    }
172
173
    /**
174
     * This creates a containing div for dynamic fields which appear only on another fields value
175
     * @param null|string $dynamicTriggerValue
176
     */
177 34
    private function createNewDynamicContainerBlockIfNeeded($dynamicTriggerValue)
178
    {
179 34
        if (!isset($this->dynamicContainerBlock) && $dynamicTriggerValue !== null) {
180 1
            $this->dynamicContainerBlock = $this->createElement('div');
181 1
            $this->dynamicContainerBlock->setAttribute('data-dynamic-form', $this->dynamicFormParentName);
182 1
            $this->dynamicContainerBlock->setAttribute('data-dynamic-form-trigger-value', $dynamicTriggerValue);
183 1
            $this->dynamicContainerBlock->setAttribute('class', 'dynamic-form-block trigger'.$this->dynamicFormParentName);
184 1
            $this->dynamicContainerBlock->setAttribute('id', $this->dynamicFormParentName.$dynamicTriggerValue);
185 1
            $this->dynamicFormVisible === false ? $this->dynamicContainerBlock->setAttribute('style', 'display: none;') : null;
186
        }
187
    }
188
189
    /**
190
     *  Checks current field being processed for dynamic sub forms
191
     */
192 27
    private function dynamicFormCheck()
193
    {
194 27
        if ($this->field->hasDynamicForms()) {
195 1
            $this->dynamicFormParentName = $this->field->getName();
196 1
            $value = $this->field->getValue();
197 1
            $forms = $this->field->getDynamicForms();
198 1
            $this->includeDynamicFormJavascript = true;
199 1
            foreach ($forms as $dynamicTriggerValue => $form) {
200 1
                $this->dynamicFormVisible = ($value == $dynamicTriggerValue);
201 1
                $dynamicFields = $form->getFields();
202 1
                $this->processFields($dynamicFields, $dynamicTriggerValue);
203
            }
204 1
            unset($this->dynamicFormParentName);
205
        }
206
    }
207
208
    /**
209
     * @param bool $finaliseDynamicBlock
210
     */
211 27
    private function finaliseDynamicBlockIfNeeded($finaliseDynamicBlock)
212
    {
213 27
        if (isset($this->dynamicContainerBlock) && $finaliseDynamicBlock === true) {
214 1
            $this->form->appendChild($this->dynamicContainerBlock);
215 1
            unset($this->dynamicContainerBlock);
216
        }
217
    }
218
219
220
    /**
221
     * @return DOMElement|null
222
     */
223 6
    public function renderError()
224
    {
225 6
        $errorBlock = null;
226 6
        if ($this->errorRenderer->shouldRender($this->field) && $this->displayErrors === true) {
227 4
            $this->block->setAttribute('class', 'has-error ');
228 4
            $errorBlock = $this->errorRenderer->render($this->field);
229
        }
230 6
        return $errorBlock;
231
    }
232
233
    /**
234
     * @return \DOMElement
235
     */
236 34
    protected function createLabelElement()
237
    {
238 34
        $label = $this->createElement('label');
239 34
        $label->setAttribute('for', $this->field->getId() ?? '');
240 34
        if ($this->field->isRequired()) {
241 6
            $label = $this->addRequiredAsterisk($label);
242
        }
243 34
        return $label;
244
    }
245
246
    /**
247
     * @param DomElement $label
248
     * @return DomElement
249
     */
250 6
    public function addRequiredAsterisk(DomElement $label)
251
    {
252 6
        $span = $this->createElement('span');
253 6
        $span->setAttribute('class', 'text-danger');
254 6
        $text = $this->createText('* ');
255 6
        $span->appendChild($text);
256 6
        $label->appendChild($span);
257 6
        return $label;
258
    }
259
260
    /**
261
     * @return string
262
     */
263 29
    private function addDynamicFormJavascript()
264
    {
265 29
        if ($this->includeDynamicFormJavascript === true) {
266 1
            return "<script type=\"text/javascript\">
267
                $(document).ready(function(){
268
                    $('.dynamic-form-block').each(function(){
269
                        var Id = $(this).prop('id');
270
                        var parentField = $(this).attr('data-dynamic-form');
271
                        var parentValue = $(this).attr('data-dynamic-form-trigger-value');
272
            
273
                        $('input[name=\"'+parentField+'\"]').change(function(){
274
                            var val = $(this).val();
275
                            if (val == parentValue) {
276
                                $('.trigger'+parentField).each(function(){
277
                                    $(this).attr('style', 'display: none;');
278
                                });
279
                                $('#'+Id).attr('style', 'display: block;');
280
                            }
281
                        });
282
                    });
283
                });
284
            </script>
285 1
            ";
286
        }
287 28
        return '';
288
    }
289
}
290