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

NIT-Administrative-Systems / dynamic-forms / 8086028315

28 Feb 2024 07:29PM UTC coverage: 92.374% (-0.07%) from 92.445%
8086028315

push

github

web-flow
Additional getter for $additional (#441)

* Add getter for the other fields in $additional

* v0.14 changelog

0 of 1 new or added line in 1 file covered. (0.0%)

1187 of 1285 relevant lines covered (92.37%)

187.68 hits per line

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

75.32
/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;
81✔
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
    }
72✔
47

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

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

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

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

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

73
    public function hasMultipleValues(): bool
74
    {
75
        return $this->hasMultipleValues;
132✔
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();
48✔
88
    }
89

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

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

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

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

109
        if (Arr::exists($this->conditional, 'show') && Arr::exists($this->conditional, 'when') && Arr::exists($this->conditional, 'eq')) {
18✔
110
            return new SimpleConditional(
18✔
111
                Arr::get($this->conditional, 'show'),
18✔
112
                Arr::get($this->conditional, 'when'),
18✔
113
                Arr::get($this->conditional, 'eq'),
18✔
114
            );
18✔
115
        }
116
        // otherwise ignore the condition
117
        return null;
3✔
118
    }
119

120
    public function isCalculated(): bool
121
    {
122
        return $this->calculateValue !== null;
129✔
123
    }
124

125
    public function calculation(): ?CalculationInterface
126
    {
127
        if (! $this->calculateValue) {
87✔
128
            return null;
81✔
129
        }
130

131
        if (is_string($this->calculateValue)) {
6✔
132
            throw new CalculationNotImplemented($this->key(), CalculationNotImplemented::CUSTOM_JS);
×
133
        }
134

135
        return new JSONCalculation($this->calculateValue);
6✔
136
    }
137

138
    public function submissionValue(): mixed
139
    {
140
        $value = $this->submissionValue;
147✔
141

142
        foreach ($this->transformations() as $transform) {
147✔
143
            $value = $transform($value);
57✔
144
        }
145

146
        return $value;
147✔
147
    }
148

149
    public function setSubmissionValue(mixed $value): void
150
    {
151
        $this->submissionValue = $value;
51✔
152
    }
153

154
    public function validate(): MessageBag
155
    {
156
        $fieldLabel = $this->errorLabel() ?? $this->label() ?? $this->key();
900✔
157

158
        $validator = app()->make('validator');
900✔
159
        $bag = new MessageBagImpl;
900✔
160

161
        if (! $this->canValidate()) {
900✔
162
            return $bag;
×
163
        }
164

165
        if ($this->hasMultipleValuesForValidation()) {
900✔
166
            foreach ($this->submissionValue() as $index => $submissionValue) {
453✔
167
                $bag = $this->mergeErrorBags($bag, $this->processValidations(
429✔
168
                    $this->key(),
429✔
169
                    $this->errorLabel() ?? sprintf('%s (%s)', $fieldLabel, $index + 1),
429✔
170
                    $submissionValue,
429✔
171
                    $validator
429✔
172
                ));
429✔
173
            }
174

175
            return $bag->merge($this->postProcessValidationsForMultiple($this->key()));
453✔
176
        }
177

178
        return $this->mergeErrorBags(
447✔
179
            new MessageBagImpl,
447✔
180
            $this->processValidations($this->key(), $fieldLabel, $this->submissionValue(), $validator)
447✔
181
        );
447✔
182
    }
183

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

201
        return $mergeInto->merge($mergeFrom);
48✔
202
    }
203

204
    public function transformations(): array
205
    {
206
        $transformations = [];
48✔
207

208
        if ($this->case === CaseEnum::UPPER) {
48✔
209
            $transformations['case'] = fn ($value) => is_string($value) ? strtoupper($value) : $value;
3✔
210
        } elseif ($this->case === CaseEnum::LOWER) {
45✔
211
            $transformations['case'] = fn ($value) => is_string($value) ? strtolower($value) : $value;
×
212
        }
213

214
        return $transformations;
48✔
215
    }
216

217
    public function defaultValue(): mixed
218
    {
219
        return $this->defaultValue;
81✔
220
    }
221

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

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

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

267
        return $bag;
×
268
    }
269

270
    public function validation(string $name): mixed
271
    {
272
        return Arr::get($this->validations, $name);
129✔
273
    }
274

275
    public function validations(): array
276
    {
277
        return $this->validations;
81✔
278
    }
279

280
    public function additional(string $key): mixed
281
    {
NEW
282
        return Arr::get($this->additional, $key);
×
283
    }
284
}
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