• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

NIT-Administrative-Systems / dynamic-forms / 3874968769

pending completion
3874968769

push

github

GitHub
PHP 8.2 Support (#383)

1182 of 1279 relevant lines covered (92.42%)

62.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.75
/src/Forms/ValidatedForm.php
1
<?php
2

3
namespace Northwestern\SysDev\DynamicForms\Forms;
4

5
use Illuminate\Contracts\Support\MessageBag;
6
use Illuminate\Contracts\Validation\Validator;
7
use Illuminate\Support\Arr;
8
use Illuminate\Support\Collection;
9
use Illuminate\Support\MessageBag as MessageBagImpl;
10
use Northwestern\SysDev\DynamicForms\Components\ComponentInterface;
11
use Northwestern\SysDev\DynamicForms\FileComponentRegistry;
12
use Northwestern\SysDev\DynamicForms\Forms\Concerns\HandlesTree;
13

14
class ValidatedForm implements Validator
15
{
16
    use HandlesTree;
17

18
    protected array $components;
19
    protected array $flatComponents;
20
    protected array $forgetComponentKeys = [];
21

22
    protected Collection $values;
23
    protected MessageBag $messages;
24

25
    public function __construct(array $components, array $values)
26
    {
27
        $this->components = $components;
14✔
28
        $messageBag = new MessageBagImpl;
14✔
29
        $transformedValues = collect();
14✔
30

31
        $this->populateComponentTreeWithData($this->components, $values);
14✔
32
        $this->flatComponents = $this->flattenComponents($this->components);
14✔
33

34
        $this->processComponentTreeCalculations($this->components);
14✔
35
        $this->processComponentTreeConditionals($this->components, shouldForget: false);
14✔
36

37
        $this->flatComponents = $this->flatValidatableComponents($this->components);
14✔
38

39
        foreach ($this->flatComponents as $component) {
14✔
40
            $messageBag->merge($component->validate());
14✔
41
            $transformedValues->put($component->key(), $component->submissionValue());
14✔
42
        }
43

44
        // If any components are unknown or were removed by conditional logic, discard the corresponding value
45
        $this->values = $transformedValues->only(array_keys($this->flatComponents));
14✔
46
        $this->messages = $messageBag;
14✔
47
    }
48

49
    /**
50
     * Gets validation failure messages.
51
     */
52
    public function messages(): MessageBag
53
    {
54
        return $this->messages;
14✔
55
    }
56

57
    /**
58
     * Whether or not the submission is valid.
59
     */
60
    public function isValid(): bool
61
    {
62
        return $this->messages()->isEmpty();
14✔
63
    }
64

65
    /**
66
     * Returns the cleaned & transformed form submission data.
67
     *
68
     * Any keys submitted for components that are either unknown or
69
     * hidden will be stripped out, similar to how $request->validate()
70
     * will only return fields that have been explicitly given rules.
71
     */
72
    public function values(): array
73
    {
74
        return $this->values->all();
14✔
75
    }
76

77
    /**
78
     * Returns the list of File objects in a validated request.
79
     */
80
    public function allFiles(): array
81
    {
82
        $list = [];
1✔
83
        foreach ($this->values as $component) {
1✔
84
            if (is_array($component)) { //files always present as multivalued
1✔
85
                foreach ($component as $subComponent) {
1✔
86
                    if (is_array($subComponent) && array_key_exists('storage', $subComponent)) {
1✔
87
                        //get storage driver and check if file exists
88
                        $storageDriver = resolve(resolve(FileComponentRegistry::class)->get($subComponent['storage']));
1✔
89
                        if ($storageDriver->findObject($subComponent['key'])) {
1✔
90
                            $list[] = $subComponent;
1✔
91
                        }
92
                    }
93
                }
94
            }
95
        }
96

97
        return $list;
1✔
98
    }
99

100
    /**
101
     * @param ComponentInterface[] $components
102
     */
103
    private function populateComponentTreeWithData(array $components, array $data): void
104
    {
105
        foreach ($components as $component) {
14✔
106
            if (Arr::has($data, $component->key())) {
14✔
107
                $component->setSubmissionValue(Arr::get($data, $component->key()));
14✔
108
            }
109

110
            $this->populateComponentTreeWithData($component->components(), $data);
14✔
111
        }
112
    }
113

114
    /**
115
     * @param ComponentInterface[] $components
116
     */
117
    private function processComponentTreeCalculations(array $components): void
118
    {
119
        $values = $this->valuesWhileProcessingForm();
14✔
120

121
        foreach ($components as $component) {
14✔
122
            if ($component->isCalculated()) {
14✔
123
                $calculation = $component->calculation();
2✔
124

125
                $component->setSubmissionValue($calculation($values));
2✔
126
            }
127

128
            $this->processComponentTreeCalculations($component->components());
14✔
129
        }
130
    }
131

132
    /**
133
     * @param ComponentInterface[] $components
134
     */
135
    private function processComponentTreeConditionals(array $components, bool $shouldForget): void
136
    {
137
        $values = $this->valuesWhileProcessingForm();
14✔
138

139
        foreach ($components as $component) {
14✔
140
            // Once we're in forget mode, forget all the children recursively.
141
            if ($shouldForget) {
14✔
142
                $this->forgetComponentKeys[] = $component->key();
1✔
143
                $this->processComponentTreeConditionals($component->components(), shouldForget: true);
1✔
144

145
                continue;
1✔
146
            }
147

148
            if ($component->hasConditional()) {
14✔
149
                $condition = $component->conditional();
4✔
150

151
                if (! $condition($values)) {
4✔
152
                    $this->forgetComponentKeys[] = $component->key();
3✔
153
                    $this->processComponentTreeConditionals($component->components(), shouldForget: true);
3✔
154

155
                    continue;
3✔
156
                }
157
            }
158

159
            $this->processComponentTreeConditionals($component->components(), shouldForget: false);
14✔
160
        }
161
    }
162

163
    /**
164
     * @param ComponentInterface[] $components
165
     */
166
    private function flatValidatableComponents(array $components): array
167
    {
168
        return collect($this->flattenComponents($components))
14✔
169
            ->reject(fn (ComponentInterface $c) => in_array($c->key(), $this->forgetComponentKeys))
14✔
170
            ->filter(fn (ComponentInterface $c) => $c->canValidate())
14✔
171
            ->all();
14✔
172
    }
173

174
    /**
175
     * Generates the values from the components' submissionValue.
176
     *
177
     * This can be run multiple times during a recursive function so the freshest values are available at each step.
178
     */
179
    private function valuesWhileProcessingForm(): array
180
    {
181
        return collect($this->flatComponents)
14✔
182
            ->mapWithKeys(fn (ComponentInterface $c, string $key) => [$key => $c->submissionValue()])
14✔
183
            ->all();
14✔
184
    }
185

186
    /**
187
     * Return a list of components that should be validated.
188
     *
189
     * Calculations will be run on the unvalidated data before processing
190
     * the conditionals (just as they would be in the UI), since those
191
     * values may be needed for the conditional logic.
192
     *
193
     * Conditional logic that excludes fields will be evaluated here
194
     * and may remove components from consideration.
195
     *
196
     * @return ComponentInterface[]
197
     */
198
    protected function processComponentTree(array $components, array $values): array
199
    {
200
        // Populate the components with their data so we can evaluate conditionals
201
        $data = collect($values)->only($components->keys());
×
202
    }
203

204
    /**
205
     * @internal
206
     */
207
    public function getMessageBag()
208
    {
209
        return $this->messages();
1✔
210
    }
211

212
    /**
213
     * @internal
214
     */
215
    public function validate()
216
    {
217
        throw new \Exception('Not implemented');
×
218
    }
219

220
    /**
221
     * @internal
222
     */
223
    public function validated()
224
    {
225
        return $this->values();
1✔
226
    }
227

228
    /**
229
     * @internal
230
     */
231
    public function fails()
232
    {
233
        return ! $this->isValid();
1✔
234
    }
235

236
    /**
237
     * @internal
238
     */
239
    public function failed()
240
    {
241
        return $this->messages->keys();
1✔
242
    }
243

244
    /**
245
     * @internal
246
     */
247
    public function sometimes($attribute, $rules, callable $callback)
248
    {
249
        throw new \Exception('Not implemented');
×
250
    }
251

252
    /**
253
     * @internal
254
     */
255
    public function after($callback)
256
    {
257
        throw new \Exception('Not implemented');
×
258
    }
259

260
    /**
261
     * @internal
262
     */
263
    public function errors()
264
    {
265
        return $this->messages();
1✔
266
    }
267
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc