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

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

23 Mar 2026 08:30PM UTC coverage: 98.58% (-0.1%) from 98.693%
23458645259

Pull #119

github

web-flow
Merge bd0a075f2 into d14ae3d85
Pull Request #119: Fix/issue 101

568 of 579 new or added lines in 54 files covered. (98.1%)

3889 of 3945 relevant lines covered (98.58%)

559.58 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,034✔
32
    {
33
        $compositionValidatorKeys = $schema->getCompositionValidatorKeys();
2,034✔
34

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

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

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

43
        // if the generator is immutable no validation on value updates are required
44
        if ($generatorConfiguration->isImmutable() || empty($validatorPropertyMap)) {
299✔
45
            return;
256✔
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
299✔
56
    {
57
        $validatorPropertyMap = [];
299✔
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) {
299✔
61
            if (!is_a($validator, AbstractComposedPropertyValidator::class)) {
299✔
62
                continue;
21✔
63
            }
64

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

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

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

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

100
        return $validatorPropertyMap;
299✔
101
    }
102

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

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

117
            $schema->addMethod(
299✔
118
                "validateComposition_$validatorIndex",
299✔
119
                new RenderedMethod(
299✔
120
                    $schema,
299✔
121
                    $generatorConfiguration,
299✔
122
                    'CompositionValidation.phptpl',
299✔
123
                    [
299✔
124
                        'validator' => $compositionValidator,
299✔
125
                        'schema' => $schema,
299✔
126
                        'index' => $validatorIndex,
299✔
127
                        'viewHelper' => new RenderHelper($generatorConfiguration),
299✔
128
                    ],
299✔
129
                )
299✔
130
            );
299✔
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