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

wol-soft / php-json-schema-model-generator / 23458879572

23 Mar 2026 08:36PM UTC coverage: 98.58% (-0.02%) from 98.604%
23458879572

push

github

web-flow
Merge pull request #120 from wol-soft/fix/issue-98

Fix/issue 98

9 of 10 new or added lines in 2 files covered. (90.0%)

3889 of 3945 relevant lines covered (98.58%)

553.63 hits per line

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

98.57
/src/SchemaProcessor/PostProcessor/Internal/CompositionValidationPostProcessor.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace PHPModelGenerator\SchemaProcessor\PostProcessor\Internal;
6

7
use PHPModelGenerator\Model\GeneratorConfiguration;
8
use PHPModelGenerator\Model\Property\Property;
9
use PHPModelGenerator\Model\Property\PropertyInterface;
10
use PHPModelGenerator\Model\Property\PropertyType;
11
use PHPModelGenerator\Model\Schema;
12
use PHPModelGenerator\Model\SchemaDefinition\JsonSchema;
13
use PHPModelGenerator\Model\Validator\AbstractComposedPropertyValidator;
14
use PHPModelGenerator\SchemaProcessor\Hook\SetterBeforeValidationHookInterface;
15
use PHPModelGenerator\SchemaProcessor\PostProcessor\PostProcessor;
16
use PHPModelGenerator\SchemaProcessor\PostProcessor\RenderedMethod;
17
use PHPModelGenerator\Utils\RenderHelper;
18

19
/**
20
 * Class CompositionValidationPostProcessor
21
 *
22
 * The CompositionValidationPostProcessor adds methods to models which require composition validations on object level
23
 * to validate the compositions.
24
 *
25
 * Additionally extends setter methods to also validate compositions if the updated property is part of a composition
26
 *
27
 * @package PHPModelGenerator\SchemaProcessor\PostProcessor\Internal
28
 */
29
class CompositionValidationPostProcessor extends PostProcessor
30
{
31
    public function process(Schema $schema, GeneratorConfiguration $generatorConfiguration): void
2,017✔
32
    {
33
        $compositionValidatorKeys = $schema->getCompositionValidatorKeys();
2,017✔
34

35
        if (empty($compositionValidatorKeys)) {
2,017✔
36
            return;
2,017✔
37
        }
38

39
        $validatorPropertyMap = $this->generateValidatorPropertyMap($schema);
282✔
40

41
        $this->addValidationMethods($schema, $generatorConfiguration, $compositionValidatorKeys);
282✔
42

43
        // if the generator is immutable no validation on value updates are required
44
        if ($generatorConfiguration->isImmutable() || empty($validatorPropertyMap)) {
282✔
45
            return;
239✔
46
        }
47

48
        $this->addValidationCallsToSetterMethods($schema, $validatorPropertyMap);
43✔
49
    }
50

51
    /**
52
     * Set up a map containing the properties and the corresponding composition validators which must be checked when
53
     * the property is updated
54
     */
55
    private function generateValidatorPropertyMap(Schema $schema): array
282✔
56
    {
57
        $validatorPropertyMap = [];
282✔
58

59
        // get all base validators which are composed value validators and set up a map of affected object properties
60
        foreach ($schema->getBaseValidators() as $validatorIndex => $validator) {
282✔
61
            if (!is_a($validator, AbstractComposedPropertyValidator::class)) {
282✔
62
                continue;
21✔
63
            }
64

65
            foreach ($validator->getComposedProperties() as $composedProperty) {
282✔
66
                if ($composedProperty->getNestedSchema() === null) {
282✔
NEW
67
                    continue;
×
68
                }
69

70
                foreach ($composedProperty->getNestedSchema()->getProperties() as $property) {
282✔
71
                    if (!isset($validatorPropertyMap[$property->getName()])) {
280✔
72
                        $validatorPropertyMap[$property->getName()] = [];
280✔
73
                    }
74

75
                    $validatorPropertyMap[$property->getName()][] = $validatorIndex;
280✔
76
                }
77
            }
78
        }
79

80
        if (!empty($validatorPropertyMap)) {
282✔
81
            $schema->addProperty(
280✔
82
                (new Property(
280✔
83
                    'propertyValidationState',
280✔
84
                    new PropertyType('array'),
280✔
85
                    new JsonSchema(__FILE__, []),
280✔
86
                    'Track the internal validation state of composed validations',
280✔
87
                ))
280✔
88
                    ->setInternal(true)
280✔
89
                    ->setDefaultValue(
280✔
90
                        array_fill_keys(
280✔
91
                            array_unique(
280✔
92
                                array_merge(...array_values($validatorPropertyMap)),
280✔
93
                            ),
280✔
94
                            [],
280✔
95
                        )
280✔
96
                    ),
280✔
97
            );
280✔
98
        }
99

100
        return $validatorPropertyMap;
282✔
101
    }
102

103
    /**
104
     * @param int[] $compositionValidatorKeys
105
     */
106
    private function addValidationMethods(
282✔
107
        Schema $schema,
108
        GeneratorConfiguration $generatorConfiguration,
109
        array $compositionValidatorKeys,
110
    ): void {
111
        foreach ($compositionValidatorKeys as $validatorIndex) {
282✔
112
            /** @var AbstractComposedPropertyValidator $compositionValidator */
113
            $compositionValidator = $schema->getBaseValidators()[$validatorIndex];
282✔
114

115
            $compositionValidator->setScope($schema);
282✔
116

117
            $schema->addMethod(
282✔
118
                "validateComposition_$validatorIndex",
282✔
119
                new RenderedMethod(
282✔
120
                    $schema,
282✔
121
                    $generatorConfiguration,
282✔
122
                    'CompositionValidation.phptpl',
282✔
123
                    [
282✔
124
                        'validator' => $compositionValidator,
282✔
125
                        'schema' => $schema,
282✔
126
                        'index' => $validatorIndex,
282✔
127
                        'viewHelper' => new RenderHelper($generatorConfiguration),
282✔
128
                    ],
282✔
129
                )
282✔
130
            );
282✔
131
        }
132
    }
133

134
    /**
135
     * Add internal calls to validation methods to the setters which are part of a composition validation. The
136
     * validation methods will validate the state of all compositions when the value is updated.
137
     */
138
    private function addValidationCallsToSetterMethods(Schema $schema, array $validatorPropertyMap): void
43✔
139
    {
140
        $schema->addSchemaHook(new class ($validatorPropertyMap) implements SetterBeforeValidationHookInterface {
43✔
141
            public function __construct(protected array $validatorPropertyMap)
142
            {}
43✔
143

144
            public function getCode(PropertyInterface $property, bool $batchUpdate = false): string
145
            {
146
                return join(
43✔
147
                    "\n",
43✔
148
                    array_map(
43✔
149
                        static fn(int $validatorIndex): string =>
43✔
150
                            sprintf('$this->validateComposition_%s($modelData);', $validatorIndex),
43✔
151
                        array_unique($this->validatorPropertyMap[$property->getName()] ?? []),
43✔
152
                    )
43✔
153
                );
43✔
154
            }
155
        });
43✔
156
    }
157
}
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