Completed
Branch dynamicfields (fd80d4)
by Derek Stephen
06:25
created

createNewDynamicContainerBlockIfNeeded()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 5.667

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 3
cts 9
cp 0.3333
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 2
nop 1
crap 5.667
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 47
    public function __construct()
58
    {
59 47
        $this->resetDom();
60 47
    }
61
62
    /**
63
     *  resets dom
64
     */
65 47
    private function resetDom()
66
    {
67 47
        $this->setDom(new DOMDocument());
68 47
        $this->form = $this->getDom()->createElement('form');
69 47
        $this->errorRenderer = new DefaultErrorRender($this->dom);
70 47
    }
71
72
    /**
73
     * @param FormInterface $form
74
     * @param bool $displayErrors
75
     * @return string
76
     */
77 28
    public function render(FormInterface $form, $displayErrors = true)
78
    {
79 28
        $this->displayErrors = $displayErrors;
80 28
        $this->setFormAttributes($form);
81
82 28
        $fields = $form->getFields();
83 28
        $this->processFields($fields);
84
85 22
        $this->getDom()->appendChild($this->form);
86 22
        $html = $this->getDom()->saveHTML();
87 22
        $this->resetDom();
88 22
        return $html;
89
    }
90
91
    /**
92
     * @param FormInterface $form
93
     */
94 28
    private function setFormAttributes(FormInterface $form)
95
    {
96 28
        $attributes = $form->getAttributes();
97 28
        foreach ($attributes as $key => $value) {
98 28
            $this->form->setAttribute($key, $value);
99
        }
100
101
        // set Id as name or method as post if not set
102 28
        $method = $this->getMethod($form);
103 28
        $id = $this->getId($form);
104
105 28
        $this->form->setAttribute('id', $id);
106 28
        $this->form->setAttribute('method', $method);
107 28
    }
108
109
    /**
110
     * @param FormInterface $form
111
     * @return string
112
     */
113 28
    private function getMethod(FormInterface $form)
114
    {
115 28
        return $form->getMethod() ?: AbstractForm::METHOD_POST;
116
    }
117
118
    /**
119
     * @param FormInterface $form
120
     * @return string
121
     */
122 28
    private function getId(FormInterface $form)
123
    {
124 28
        return $form->getId() ?: $this->form->getAttribute('name');
125
    }
126
127 28
    private function processFields(FieldCollection $fields, $dynamicTriggerValue = null)
128
    {
129 28
        $count = $fields->count();
130 28
        $x = 1;
131 28
        $fields->rewind();
132 28
        while ($fields->valid()) {
133 26
            $this->field = $fields->current();
134 26
            $finaliseDynamicBlock = ($x == $count) ? true : false;
135 26
            $this->renderField($dynamicTriggerValue, $finaliseDynamicBlock);
136 20
            $x ++ ;
137 20
            $fields->next();
138
        }
139 22
        $fields->rewind();
140 22
    }
141
142
    /**
143
     * @param null $dynamicTriggerValue
144
     * @param bool $finaliseDynamicBlock
145
     */
146 26
    public function renderField($dynamicTriggerValue = null, $finaliseDynamicBlock = false)
147
    {
148 26
        $this->createNewDynamicContainerBlockIfNeeded($dynamicTriggerValue);
149
150 26
        $this->block = $this->createElement('div');
151 26
        $this->label = $this->renderFieldLabel();
152 26
        $this->element = $this->field->getRenderer()->render($this->dom, $this->field);
153 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...
154 20
        $this->block = $this->renderFieldBlock();
155
156 20
        is_null($dynamicTriggerValue)
157 20
            ? $this->form->appendChild($this->block)
158
            : $this->dynamicContainerBlock->appendChild($this->block);
159
160 20
        $this->dynamicFormCheck();
161 20
        $this->finaliseDynamicBlockIfNeeded($finaliseDynamicBlock);
162 20
    }
163
164
    /**
165
     * This creates a containing div for dynamic fields which appear only on another fields value
166
     * @param null $dynamicTriggerValue
167
     */
168 26
    private function createNewDynamicContainerBlockIfNeeded($dynamicTriggerValue)
169
    {
170 26
        if (!isset($this->dynamicContainerBlock) && $dynamicTriggerValue !== null) {
171
            $this->dynamicContainerBlock = $this->createElement('div');
172
            $this->dynamicContainerBlock->setAttribute('data-dynamic-form', $this->dynamicFormParentName);
173
            $this->dynamicContainerBlock->setAttribute('data-dynamic-form-trigger-value', $dynamicTriggerValue);
174
            $this->dynamicContainerBlock->setAttribute('style', 'display: none;');
175
            $this->dynamicContainerBlock->setAttribute('class', 'dynamic-form-block trigger'.$this->dynamicFormParentName);
176
            $this->dynamicContainerBlock->setAttribute('id', $this->dynamicFormParentName.$dynamicTriggerValue);
177
        }
178 26
    }
179
180
    /**
181
     *  Checks current field being processed for dynamic sub forms
182
     */
183 20
    private function dynamicFormCheck()
184
    {
185 20
        if ($this->field->hasDynamicForms()) {
186
            $this->dynamicFormParentName = $this->field->getName();
187
            $forms = $this->field->getDynamicForms();
188
            $this->includeDynamicFormJavascript = true;
189
            foreach ($forms as $dynamicTriggerValue => $form) {
190
                $dynamicFields = $form->getFields();
191
                $this->processFields($dynamicFields, $dynamicTriggerValue);
192
            }
193
            unset($this->dynamicFormParentName);
194
        }
195 20
    }
196
197
    /**
198
     * @param bool $finaliseDynamicBlock
199
     */
200 20
    private function finaliseDynamicBlockIfNeeded($finaliseDynamicBlock)
201
    {
202 20
        if (isset($this->dynamicContainerBlock) && $finaliseDynamicBlock === true) {
203
            $this->form->appendChild($this->dynamicContainerBlock);
204
            unset($this->dynamicContainerBlock);
205
        }
206 20
    }
207
208
209
    /**
210
     * @return DOMElement|null
211
     */
212 5
    public function renderError()
213
    {
214 5
        $errorBlock = null;
215 5
        if ($this->errorRenderer->shouldRender($this->field) && $this->displayErrors === true) {
216 4
            $this->block->setAttribute('class', 'has-error ');
217 4
            $errorBlock = $this->errorRenderer->render($this->field);
218
        }
219 5
        return $errorBlock;
220
    }
221
222
    /**
223
     * @return DOMElement
224
     */
225 26
    protected function createLabelElement()
226
    {
227 26
        $label = $this->createElement('label');
228 26
        $label->setAttribute('for', $this->field->getId());
229 26
        if ($this->field->isRequired()) {
230 5
            $label = $this->addRequiredAsterisk($label);
231
        }
232 26
        return $label;
233
    }
234
235
    /**
236
     * @param DomElement $label
237
     * @return DomElement
238
     */
239 5
    public function addRequiredAsterisk(DomElement $label)
240
    {
241 5
        $span = $this->createElement('span');
242 5
        $span->setAttribute('class', 'text-danger');
243 5
        $text = $this->createText('* ');
244 5
        $span->appendChild($text);
245 5
        $label->appendChild($span);
246 5
        return $label;
247
    }
248
}