Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
89.58% covered (warning)
89.58%
43 / 48
76.92% covered (warning)
76.92%
10 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
FormBuildContext
89.58% covered (warning)
89.58%
43 / 48
76.92% covered (warning)
76.92%
10 / 13
23.60
0.00% covered (danger)
0.00%
0 / 1
 __construct
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
 isMultipart
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isSensitive
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getApieContext
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 withApieContext
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getComponentFactory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFilledInValue
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 getValidationError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMissingValidationErrors
72.73% covered (warning)
72.73%
8 / 11
0.00% covered (danger)
0.00%
0 / 1
7.99
 getValidationErrorsInContext
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFormName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createTranslationLabel
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 createChildContext
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2namespace Apie\HtmlBuilders;
3
4use Apie\Core\BoundedContext\BoundedContextId;
5use Apie\Core\Context\ApieContext;
6use Apie\Core\ContextConstants;
7use Apie\Core\Translator\Lists\TranslationStringSet;
8use Apie\Core\ValueObjects\Utils;
9use Apie\HtmlBuilders\Factories\FormComponentFactory;
10use Apie\HtmlBuilders\Interfaces\ComponentInterface;
11use Apie\HtmlBuilders\ValueObjects\FormName;
12use Psr\Http\Message\UploadedFileInterface;
13use ReflectionClass;
14
15final class FormBuildContext
16{
17    private FormName $formName;
18
19    private bool $sensitive = false;
20
21    /**
22     * @var array<string|int, string> $validationErrors
23     */
24    private array $validationErrors;
25
26    /**
27     * @var array<string|int, mixed>|string|int|float|UploadedFileInterface|null $filledIn
28     */
29    private array|string|int|float|null|UploadedFileInterface $filledIn;
30
31    /**
32     * @param array<string|int, mixed> $filledIn
33     */
34    public function __construct(
35        private FormComponentFactory $formComponentFactory,
36        private ApieContext $context,
37        array $filledIn,
38        private bool $multipart = false
39    ) {
40        $this->filledIn = $filledIn;
41        $this->formName = new FormName();
42        $this->validationErrors = $context->hasContext(ContextConstants::VALIDATION_ERRORS)
43            ? $context->getContext(ContextConstants::VALIDATION_ERRORS)
44            : [];
45    }
46
47    public function isMultipart(): bool
48    {
49        return $this->multipart;
50    }
51
52    public function isSensitive(): bool
53    {
54        return $this->sensitive;
55    }
56
57    public function getApieContext(): ApieContext
58    {
59        return $this->context;
60    }
61
62    public function withApieContext(string $key, mixed $value): FormBuildContext
63    {
64        $res = clone $this;
65        $res->context = $res->context->withContext($key, $value);
66        return $res;
67    }
68
69    public function getComponentFactory(): FormComponentFactory
70    {
71        return $this->formComponentFactory;
72    }
73
74    public function getFilledInValue(mixed $defaultValue = null, bool $toString = false): mixed
75    {
76        $result = $this->filledIn ?? $defaultValue;
77        if ($toString) {
78            if (is_array($result)) {
79                return $defaultValue;
80            }
81            return Utils::toString($result);
82        }
83        return $result;
84    }
85
86    public function getValidationError(): string|null
87    {
88        return $this->validationErrors[$this->formName->toValidationErrorKey()] ?? null;
89    }
90
91    /**
92     * @param array<string, ComponentInterface> $childComponents
93     *
94     * @return array<int|string, string>
95     */
96    public function getMissingValidationErrors(array $childComponents): array
97    {
98        $result = $this->getValidationErrorsInContext();
99        $missingValidationErrors = [];
100        foreach (array_keys($childComponents) as $propertyName) {
101            $prefix = $propertyName . '.';
102            $found = false;
103            foreach (array_keys($result) as $key) {
104                if ($key !== $propertyName || !str_starts_with($key, $prefix)) {
105                    $found = true;
106                }
107            }
108            if (!$found && isset($result[$propertyName])) {
109                $missingValidationErrors[$propertyName] = $result[$propertyName];
110            }
111        }
112
113        return $missingValidationErrors;
114    }
115
116    /**
117     * @return array<int|string, string>
118     */
119    public function getValidationErrorsInContext(): array
120    {
121        return $this->validationErrors;
122    }
123
124    public function getFormName(): FormName
125    {
126        return $this->formName;
127    }
128
129    public function createTranslationLabel(): TranslationStringSet
130    {
131        $translations = [];
132        $boundedContextId = $this->context->getContext(ContextConstants::BOUNDED_CONTEXT_ID, false);
133        $resourceName = $this->context->getContext(ContextConstants::RESOURCE_NAME, false);
134        $resourceName ??= $this->context->getContext(ContextConstants::METHOD_CLASS, false);
135        $resourceName ??= $this->context->getContext(ContextConstants::SERVICE_CLASS, false);
136        // TODO add more variations
137        $translations[] = $this->formName->createTranslationString(
138            new ReflectionClass($resourceName),
139            $boundedContextId ? new BoundedContextId($boundedContextId) : null,
140        );
141        return new TranslationStringSet($translations);
142    }
143
144    public function createChildContext(string $propertyName, ?bool $sensitive = null): self
145    {
146        $result = clone $this;
147        $result->formName = $this->formName->createChildForm($propertyName);
148        $result->sensitive = $sensitive ?? $this->sensitive;
149        $filledIn = $this->filledIn[$propertyName] ?? null;
150        $result->filledIn = $filledIn;
151        return $result;
152    }
153}