• 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

75.34
/src/Components/BaseComponent.php
1
<?php
2

3
namespace Northwestern\SysDev\DynamicForms\Components;
4

5
use Illuminate\Contracts\Support\MessageBag;
6
use Illuminate\Support\Arr;
7
use Illuminate\Support\MessageBag as MessageBagImpl;
8
use Illuminate\Validation\Factory;
9
use Northwestern\SysDev\DynamicForms\Calculation\CalculationInterface;
10
use Northwestern\SysDev\DynamicForms\Calculation\JSONCalculation;
11
use Northwestern\SysDev\DynamicForms\Conditional\ConditionalInterface;
12
use Northwestern\SysDev\DynamicForms\Conditional\JSONConditional;
13
use Northwestern\SysDev\DynamicForms\Conditional\SimpleConditional;
14
use Northwestern\SysDev\DynamicForms\Errors\CalculationNotImplemented;
15
use Northwestern\SysDev\DynamicForms\Errors\ConditionalNotImplemented;
16
use Northwestern\SysDev\DynamicForms\Errors\InvalidDefinitionError;
17
use Northwestern\SysDev\DynamicForms\Errors\ValidationNotImplementedError;
18

19
/**
20
 * Implements common functionality for all components.
21
 */
22
abstract class BaseComponent implements ComponentInterface
23
{
24
    protected mixed $submissionValue = null;
25

26
    public static function type(): string
27
    {
28
        return static::TYPE;
27✔
29
    }
30

31
    public function __construct(
32
        protected string $key,
33
        protected ?string $label,
34
        protected ?string $errorLabel,
35
        protected array $components,
36
        protected array $validations,
37
        protected bool $hasMultipleValues,
38
        protected ?array $conditional,
39
        protected ?string $customConditional,
40
        protected string $case,
41
        protected null|array|string $calculateValue,
42
        protected mixed $defaultValue,
43
        protected array $additional,
44
    ) {
45
        //
46
    }
24✔
47

48
    public function canValidate(): bool
49
    {
50
        return true;
33✔
51
    }
52

53
    public function key(): string
54
    {
55
        return $this->key;
41✔
56
    }
57

58
    public function label(): ?string
59
    {
60
        return $this->label;
41✔
61
    }
62

63
    public function errorLabel(): ?string
64
    {
65
        return $this->errorLabel;
41✔
66
    }
67

68
    public function components(): array
69
    {
70
        return $this->components;
41✔
71
    }
72

73
    public function hasMultipleValues(): bool
74
    {
75
        return $this->hasMultipleValues;
42✔
76
    }
77

78
    /**
79
     * Informs the validation code if this should be treated as a multi-value field.
80
     *
81
     * There are cases where components always store data as multiple values, even in single-
82
     * value mode. The validator needs to handle that correctly, but we do not want to inadvertently
83
     * perform a transformation that makes the data incompatible with viewing/editing it again.
84
     */
85
    protected function hasMultipleValuesForValidation(): bool
86
    {
87
        return $this->hasMultipleValues();
14✔
88
    }
89

90
    public function hasConditional(): bool
91
    {
92
        return $this->conditional || $this->customConditional;
41✔
93
    }
94

95
    public function conditional(): ?ConditionalInterface
96
    {
97
        if (! $this->hasConditional()) {
31✔
98
            return null;
27✔
99
        }
100

101
        if ($this->customConditional) {
4✔
102
            throw new ConditionalNotImplemented($this->key(), ConditionalNotImplemented::CUSTOM_JS);
×
103
        }
104

105
        if (Arr::get($this->conditional, 'json')) {
4✔
106
            return new JSONConditional(Arr::get($this->conditional, 'json'));
×
107
        }
108

109
        return new SimpleConditional(
4✔
110
            Arr::get($this->conditional, 'show'),
4✔
111
            Arr::get($this->conditional, 'when'),
4✔
112
            Arr::get($this->conditional, 'eq'),
4✔
113
        );
4✔
114
    }
115

116
    public function isCalculated(): bool
117
    {
118
        return $this->calculateValue !== null;
41✔
119
    }
120

121
    public function calculation(): ?CalculationInterface
122
    {
123
        if (! $this->calculateValue) {
29✔
124
            return null;
27✔
125
        }
126

127
        if (is_string($this->calculateValue)) {
2✔
128
            throw new CalculationNotImplemented($this->key(), CalculationNotImplemented::CUSTOM_JS);
×
129
        }
130

131
        return new JSONCalculation($this->calculateValue);
2✔
132
    }
133

134
    public function submissionValue(): mixed
135
    {
136
        $value = $this->submissionValue;
47✔
137

138
        foreach ($this->transformations() as $transform) {
47✔
139
            $value = $transform($value);
19✔
140
        }
141

142
        return $value;
47✔
143
    }
144

145
    public function setSubmissionValue(mixed $value): void
146
    {
147
        $this->submissionValue = $value;
15✔
148
    }
149

150
    public function validate(): MessageBag
151
    {
152
        $fieldLabel = $this->errorLabel() ?? $this->label() ?? $this->key();
297✔
153

154
        $validator = app()->make('validator');
297✔
155
        $bag = new MessageBagImpl;
297✔
156

157
        if (! $this->canValidate()) {
297✔
158
            return $bag;
×
159
        }
160

161
        if ($this->hasMultipleValuesForValidation()) {
297✔
162
            foreach ($this->submissionValue() as $index => $submissionValue) {
151✔
163
                $bag = $this->mergeErrorBags($bag, $this->processValidations(
143✔
164
                    $this->key(),
143✔
165
                    $this->errorLabel() ?? sprintf('%s (%s)', $fieldLabel, $index + 1),
143✔
166
                    $submissionValue,
143✔
167
                    $validator
143✔
168
                ));
143✔
169
            }
170

171
            return $bag->merge($this->postProcessValidationsForMultiple($this->key()));
151✔
172
        }
173

174
        return $this->mergeErrorBags(
146✔
175
            new MessageBagImpl,
146✔
176
            $this->processValidations($this->key(), $fieldLabel, $this->submissionValue(), $validator)
146✔
177
        );
146✔
178
    }
179

180
    /**
181
     * Handles merging validation error MessageBags together, accounting for custom error messages.
182
     *
183
     * A custom error message overwrites all other error messages, so any number of errors in the
184
     * $mergeFrom bag will be consolidated down into one error using the custom message.
185
     *
186
     * If no custom message is set for the component, this just merges the two bags together without
187
     * any other modification.
188
     */
189
    protected function mergeErrorBags(MessageBag $mergeInto, MessageBag $mergeFrom): MessageBag
190
    {
191
        if ($this->validation('customMessage') && $mergeFrom->isNotEmpty()) {
14✔
192
            $mergeFrom = new MessageBagImpl([
×
193
                Arr::first($mergeFrom->keys()) => $this->validation('customMessage'),
×
194
            ]);
×
195
        }
196

197
        return $mergeInto->merge($mergeFrom);
14✔
198
    }
199

200
    public function transformations(): array
201
    {
202
        $transformations = [];
14✔
203

204
        if ($this->case === CaseEnum::UPPER) {
14✔
205
            $transformations['case'] = fn ($value) => is_string($value) ? strtoupper($value) : $value;
1✔
206
        } elseif ($this->case === CaseEnum::LOWER) {
13✔
207
            $transformations['case'] = fn ($value) => is_string($value) ? strtolower($value) : $value;
×
208
        }
209

210
        return $transformations;
14✔
211
    }
212

213
    public function defaultValue(): mixed
214
    {
215
        return $this->defaultValue;
27✔
216
    }
217

218
    /**
219
     * Populates the error bag with validation failures.
220
     *
221
     * If you have a layout component (canValidate() returns false),
222
     * this does not need to be implemented -- the validate() method
223
     * is smart enough not to call this.
224
     *
225
     * When ::hasMultipleValues() is true, the validate() method will
226
     * call this once for each submitted value. The ::TODO() method is
227
     * called afterwards to do any special processing on the values in
228
     * aggregate; logic like that does *not* belong in this method.
229
     *
230
     * @param string $fieldKey Field key, taking into account custom labels
231
     * @param mixed $submissionValue One single value
232
     * @param Factory $validator Illuminate validation factory, usage of which is optional (but common!)
233
     * @return MessageBag
234
     */
235
    protected function processValidations(string $fieldKey, string $fieldLabel, mixed $submissionValue, Factory $validator): MessageBag
236
    {
237
        throw new ValidationNotImplementedError($this->type());
×
238
    }
239

240
    /**
241
     * When in multiple values mode, performs additional validations on the submissionValues.
242
     *
243
     * This will ensure required multiple-value fields have at least one value. Extend this method
244
     * if more intricate logic is needed.
245
     *
246
     * @param string $fieldKey Field key, taking into account custom labels
247
     * @return MessageBag
248
     */
249
    protected function postProcessValidationsForMultiple(string $fieldKey): MessageBag
250
    {
251
        if (! $this->hasMultipleValuesForValidation()) {
×
252
            throw new InvalidDefinitionError(
×
253
                sprintf('%s called but component is multiple => false; cannot process', __METHOD__),
×
254
                $this->key()
×
255
            );
×
256
        }
257

258
        $bag = new MessageBagImpl;
×
259
        if ($this->validation('required') && count($this->submissionValue ?? []) == 0) {
×
260
            $bag->add($fieldKey, __('validation.required', ['attribute' => $fieldKey]));
×
261
        }
262

263
        return $bag;
×
264
    }
265

266
    protected function validation(string $name): mixed
267
    {
268
        return Arr::get($this->validations, $name);
14✔
269
    }
270
}
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

© 2025 Coveralls, Inc