Passed
Push — 5.x ( 2153e4...85c8c3 )
by Enjoys
16:21
created

Form::validate()   A

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 2
Bugs 0 Features 0
Metric Value
eloc 1
c 2
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 Enjoys\Forms;
6
7
use Closure;
8
use Enjoys\Forms\Elements\Csrf;
9
use Enjoys\Forms\Interfaces\DefaultsHandlerInterface;
10
use Enjoys\Forms\Traits;
11
use Enjoys\ServerRequestWrapper;
12
use Enjoys\ServerRequestWrapperInterface;
13
use Enjoys\Session\Session;
14
use Enjoys\Traits\Options;
15
use HttpSoft\ServerRequest\ServerRequestCreator;
16
use Webmozart\Assert\Assert;
17
18
use function strtoupper;
19
20
class Form
21
{
22
    use Traits\Attributes;
23
    use Options;
24
    use Traits\Container {
25
        addElement as private parentAddElement;
26
    }
27
28
    private const _ALLOWED_FORM_METHOD_ = ['GET', 'POST'];
29
30
    public const _TOKEN_CSRF_ = '_token_csrf';
31
    public const _TOKEN_SUBMIT_ = '_token_submit';
32
33
    public const ATTRIBUTES_DESC = '_desc_attributes_';
34
    public const ATTRIBUTES_VALIDATE = '_validate_attributes_';
35
    public const ATTRIBUTES_LABEL = '_label_attributes_';
36
    public const ATTRIBUTES_FIELDSET = '_fieldset_attributes_';
37
    public const ATTRIBUTES_FILLABLE_BASE = '_fillable_base_attributes_';
38
39
40
    private string $method = 'POST';
41
    private ?string $action = null;
42
    private ?string $id = null;
43
44
    private ServerRequestWrapperInterface $request;
45
    private DefaultsHandlerInterface $defaultsHandler;
46
47
    private bool $submitted = false;
48
    private Session $session;
49
50
    /**
51
     * @throws Exception\ExceptionRule
52
     */
53 90
    public function __construct(
54
        string $method = 'POST',
55
        string $action = null,
56
        string $id = null,
57
        ServerRequestWrapperInterface $request = null,
58
        DefaultsHandlerInterface $defaultsHandler = null,
59
        Session $session = null
60
    ) {
61 90
        $this->request = $request ?? new ServerRequestWrapper(ServerRequestCreator::createFromGlobals());
0 ignored issues
show
Deprecated Code introduced by
The class Enjoys\ServerRequestWrapper has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

61
        $this->request = $request ?? /** @scrutinizer ignore-deprecated */ new ServerRequestWrapper(ServerRequestCreator::createFromGlobals());
Loading history...
62 90
        $this->session = $session ?? new Session();
63 90
        $this->defaultsHandler = $defaultsHandler ?? new DefaultsHandler();
64
65 90
        $this->setMethod($method);
66 90
        $this->setAction($action);
67 90
        $this->setId($id);
68
    }
69
70
    /**
71
     * Возвращает true если форма отправлена и валидна.
72
     * На валидацию форма проверяется по умолчанию, если использовать параметр $validate
73
     * false, проверка будет только на отправку формы
74
     * @param bool $validate
75
     * @return bool
76
     */
77 5
    public function isSubmitted(bool $validate = true): bool
78
    {
79 5
        if ($this->submitted === false) {
80 1
            return false;
81
        }
82 4
        if ($validate !== false) {
83 3
            return $this->validate();
84
        }
85 1
        return true;
86
    }
87
88 3
    public function validate(): bool
89
    {
90 3
        return Validator::check($this->getElements());
91
    }
92
93
94
    /**
95
     * @param array|Closure():array $data
96
     * @return $this
97
     */
98 27
    public function setDefaults(array|Closure $data): Form
99
    {
100 27
        if ($this->submitted === true) {
101 7
            $data = array_filter(
102 7
                match ($this->getMethod()) {
103 2
                    'GET' => $this->getRequest()->getQueryData()->toArray(),
104 5
                    'POST' => $this->getRequest()->getPostData()->toArray(),
105 7
                    default => [],
106
                },
107 7
                function ($k) {
108 7
                    return !in_array($k, [self::_TOKEN_CSRF_, self::_TOKEN_SUBMIT_]);
109
                },
110
                ARRAY_FILTER_USE_KEY
111
            );
112
        }
113
114 27
        if ($data instanceof Closure) {
0 ignored issues
show
introduced by
$data is never a sub-type of Closure.
Loading history...
115 2
            $data = $data();
116
            /** @psalm-suppress RedundantConditionGivenDocblockType */
117 2
            Assert::isArray($data);
118
        }
119
120 26
        $this->defaultsHandler->setData($data);
121 26
        return $this;
122
    }
123
124
125
    /**
126
     *
127
     * Если prepare() возвращает false, то элемент добавляется,
128
     * если true, то элемент добавлен в коллекцию не будет.
129
     * @use Element::setForm()
130
     * @use Element::prepare()
131
     * @param Element $element
132
     * @param string|null $before
133
     * @param string|null $after
134
     * @return $this
135
     */
136 90
    public function addElement(Element $element, string $before = null, string $after = null): self
137
    {
138 90
        $element->setForm($this);
139 90
        return $this->parentAddElement($element, $before, $after);
140
    }
141
142
143 90
    public function getDefaultsHandler(): DefaultsHandlerInterface
144
    {
145 90
        return $this->defaultsHandler;
146
    }
147
148 90
    public function getRequest(): ServerRequestWrapperInterface
149
    {
150 90
        return $this->request;
151
    }
152
153
    /**
154
     * @throws Exception\ExceptionRule
155
     */
156 90
    public function setMethod(string $method): void
157
    {
158 90
        if (in_array(strtoupper($method), self::_ALLOWED_FORM_METHOD_)) {
159 89
            $this->method = strtoupper($method);
160
        }
161 90
        $this->setAttribute(AttributeFactory::create('method', $this->method));
162 90
        $this->setOption('method', $this->method, false);
163 90
        $this->addElement(new Csrf($this->session));
164 90
        $this->setTokenSubmitElement();
165
    }
166
167 90
    public function getMethod(): string
168
    {
169 90
        return $this->method;
170
    }
171
172 90
    public function setAction(?string $action): self
173
    {
174 90
        $this->action = $action;
175 90
        $this->setAttribute(AttributeFactory::create('action', $this->action));
176 90
        $this->setOption('action', $this->action, false);
177 90
        $this->setTokenSubmitElement();
178 90
        return $this;
179
    }
180
181 5
    public function getAction(): ?string
182
    {
183 5
        return $this->action;
184
    }
185
186 90
    public function setId(?string $id): Form
187
    {
188 90
        $this->id = $id;
189 90
        $this->setAttribute(AttributeFactory::create('id', $this->id));
190 90
        $this->setOption('id', $this->id, false);
191 90
        $this->setTokenSubmitElement();
192 90
        return $this;
193
    }
194
195
196 2
    public function getId(): ?string
197
    {
198 2
        return $this->id;
199
    }
200
201 90
    private function setTokenSubmitElement(): void
202
    {
203 90
        $tokenSubmit = new TokenSubmit($this);
204 90
        $this->addElement($tokenSubmit->getElement());
205
206 90
        $this->submitted = $tokenSubmit->validate();
207
208
        // after every update the token submit, form needs check
209
        // and update defaults if form has been submitted.
210
        // Maybe in future will refactor this code
211 90
        if ($this->submitted === true) {
212 1
            $this->setDefaults([]);
213
        }
214
    }
215
216
//    /**
217
//     * Вывод формы в Renderer
218
//     * @param \Enjoys\Forms\Interfaces\RendererInterface $renderer
219
//     * @return mixed Возвращается любой формат, в зависимоти от renderer`а, может
220
//     * вернутся строка в html, или, например, xml или массив, все зависит от рендерера.
221
//     */
222
//    public function render(\Enjoys\Forms\Interfaces\RendererInterface $renderer): mixed
223
//    {
224
//        $renderer->setForm($this);
225
//        return $renderer->output();
226
//    }
227
}
228