Completed
Branch dynamicfields (a10e1d)
by Derek Stephen
02:30
created

AbstractFormRenderer   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 234
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 82%

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 7
dl 0
loc 234
ccs 82
cts 100
cp 0.82
rs 9.8
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A resetDom() 0 6 1
A render() 0 13 1
A setFormAttributes() 0 14 2
A getMethod() 0 4 2
A getId() 0 4 2
A processFields() 0 14 3
A renderField() 0 17 3
A createNewDynamicContainerBlockIfNeeded() 0 11 4
A dynamicFormCheck() 0 15 3
A finaliseDynamicBlockIfNeeded() 0 7 3
A renderError() 0 9 3
A createLabelElement() 0 9 2
A addRequiredAsterisk() 0 9 1
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 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 47
    public function __construct()
61
    {
62 47
        $this->resetDom();
63 47
    }
64
65
    /**
66
     *  resets dom
67
     */
68 47
    private function resetDom()
69
    {
70 47
        $this->setDom(new DOMDocument());
71 47
        $this->form = $this->getDom()->createElement('form');
72 47
        $this->errorRenderer = new DefaultErrorRender($this->dom);
73 47
    }
74
75
    /**
76
     * @param FormInterface $form
77
     * @param bool $displayErrors
78
     * @return string
79
     */
80 28
    public function render(FormInterface $form, $displayErrors = true)
81
    {
82 28
        $this->displayErrors = $displayErrors;
83 28
        $this->setFormAttributes($form);
84
85 28
        $fields = $form->getFields();
86 28
        $this->processFields($fields);
87
88 22
        $this->getDom()->appendChild($this->form);
89 22
        $html = $this->getDom()->saveHTML();
90 22
        $this->resetDom();
91 22
        return $html;
92
    }
93
94
    /**
95
     * @param FormInterface $form
96
     */
97 28
    private function setFormAttributes(FormInterface $form)
98
    {
99 28
        $attributes = $form->getAttributes();
100 28
        foreach ($attributes as $key => $value) {
101 28
            $this->form->setAttribute($key, $value);
102
        }
103
104
        // set Id as name or method as post if not set
105 28
        $method = $this->getMethod($form);
106 28
        $id = $this->getId($form);
107
108 28
        $this->form->setAttribute('id', $id);
109 28
        $this->form->setAttribute('method', $method);
110 28
    }
111
112
    /**
113
     * @param FormInterface $form
114
     * @return string
115
     */
116 28
    private function getMethod(FormInterface $form)
117
    {
118 28
        return $form->getMethod() ?: AbstractForm::METHOD_POST;
119
    }
120
121
    /**
122
     * @param FormInterface $form
123
     * @return string
124
     */
125 28
    private function getId(FormInterface $form)
126
    {
127 28
        return $form->getId() ?: $this->form->getAttribute('name');
128
    }
129
130 28
    private function processFields(FieldCollection $fields, $dynamicTriggerValue = null)
131
    {
132 28
        $count = $fields->count();
133 28
        $x = 1;
134 28
        $fields->rewind();
135 28
        while ($fields->valid()) {
136 26
            $this->field = $fields->current();
137 26
            $finaliseDynamicBlock = ($x == $count) ? true : false;
138 26
            $this->renderField($dynamicTriggerValue, $finaliseDynamicBlock);
139 20
            $x ++ ;
140 20
            $fields->next();
141
        }
142 22
        $fields->rewind();
143 22
    }
144
145
    /**
146
     * @param null $dynamicTriggerValue
147
     * @param bool $finaliseDynamicBlock
148
     */
149 26
    public function renderField($dynamicTriggerValue = null, $finaliseDynamicBlock = false)
150
    {
151 26
        $this->createNewDynamicContainerBlockIfNeeded($dynamicTriggerValue);
152
153 26
        $this->block = $this->createElement('div');
154 26
        $this->label = $this->renderFieldLabel();
155 26
        $this->element = $this->field->getRenderer()->render($this->dom, $this->field);
156 20
        $this->errors = $this->field->isValid() ? null : $this->renderError();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->field->isValid() ... : $this->renderError() can also be of type object<Del\Form\Renderer\DOMElement>. However, the property $errors is declared as type object<DOMElement>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
157 20
        $this->block = $this->renderFieldBlock();
158
159 20
        is_null($dynamicTriggerValue)
160 20
            ? $this->form->appendChild($this->block)
161
            : $this->dynamicContainerBlock->appendChild($this->block);
162
163 20
        $this->dynamicFormCheck();
164 20
        $this->finaliseDynamicBlockIfNeeded($finaliseDynamicBlock);
165 20
    }
166
167
    /**
168
     * This creates a containing div for dynamic fields which appear only on another fields value
169
     * @param null $dynamicTriggerValue
170
     */
171 26
    private function createNewDynamicContainerBlockIfNeeded($dynamicTriggerValue)
172
    {
173 26
        if (!isset($this->dynamicContainerBlock) && $dynamicTriggerValue !== null) {
174
            $this->dynamicContainerBlock = $this->createElement('div');
175
            $this->dynamicContainerBlock->setAttribute('data-dynamic-form', $this->dynamicFormParentName);
176
            $this->dynamicContainerBlock->setAttribute('data-dynamic-form-trigger-value', $dynamicTriggerValue);
177
            $this->dynamicContainerBlock->setAttribute('class', 'dynamic-form-block trigger'.$this->dynamicFormParentName);
178
            $this->dynamicContainerBlock->setAttribute('id', $this->dynamicFormParentName.$dynamicTriggerValue);
179
            $this->dynamicFormVisible == false ? $this->dynamicContainerBlock->setAttribute('style', 'display: none;') : null;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
180
        }
181 26
    }
182
183
    /**
184
     *  Checks current field being processed for dynamic sub forms
185
     */
186 20
    private function dynamicFormCheck()
187
    {
188 20
        if ($this->field->hasDynamicForms()) {
189
            $this->dynamicFormParentName = $this->field->getName();
190
            $value = $this->field->getValue();
191
            $forms = $this->field->getDynamicForms();
192
            $this->includeDynamicFormJavascript = true;
193
            foreach ($forms as $dynamicTriggerValue => $form) {
194
                $this->dynamicFormVisible = ($value == $dynamicTriggerValue);
195
                $dynamicFields = $form->getFields();
196
                $this->processFields($dynamicFields, $dynamicTriggerValue);
197
            }
198
            unset($this->dynamicFormParentName);
199
        }
200 20
    }
201
202
    /**
203
     * @param bool $finaliseDynamicBlock
204
     */
205 20
    private function finaliseDynamicBlockIfNeeded($finaliseDynamicBlock)
206
    {
207 20
        if (isset($this->dynamicContainerBlock) && $finaliseDynamicBlock === true) {
208
            $this->form->appendChild($this->dynamicContainerBlock);
209
            unset($this->dynamicContainerBlock);
210
        }
211 20
    }
212
213
214
    /**
215
     * @return DOMElement|null
216
     */
217 5
    public function renderError()
218
    {
219 5
        $errorBlock = null;
220 5
        if ($this->errorRenderer->shouldRender($this->field) && $this->displayErrors === true) {
221 4
            $this->block->setAttribute('class', 'has-error ');
222 4
            $errorBlock = $this->errorRenderer->render($this->field);
223
        }
224 5
        return $errorBlock;
225
    }
226
227
    /**
228
     * @return DOMElement
229
     */
230 26
    protected function createLabelElement()
231
    {
232 26
        $label = $this->createElement('label');
233 26
        $label->setAttribute('for', $this->field->getId());
234 26
        if ($this->field->isRequired()) {
235 5
            $label = $this->addRequiredAsterisk($label);
236
        }
237 26
        return $label;
238
    }
239
240
    /**
241
     * @param DomElement $label
242
     * @return DomElement
243
     */
244 5
    public function addRequiredAsterisk(DomElement $label)
245
    {
246 5
        $span = $this->createElement('span');
247 5
        $span->setAttribute('class', 'text-danger');
248 5
        $text = $this->createText('* ');
249 5
        $span->appendChild($text);
250 5
        $label->appendChild($span);
251 5
        return $label;
252
    }
253
}