Skip to content

Commit b97f782

Browse files
committed
Serialization of pattern properties
pattern properties combined with filters
1 parent e02e215 commit b97f782

26 files changed

+524
-94
lines changed

src/Model/Validator/PatternPropertiesValidator.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class PatternPropertiesValidator extends PropertyTemplateValidator
2727
private $validationProperty;
2828
/** @var string */
2929
private $pattern;
30+
/** @var string */
31+
private $key;
3032
/** @var bool */
3133
private $collectPatternProperties = false;
3234

@@ -47,6 +49,8 @@ public function __construct(
4749
JsonSchema $propertyStructure
4850
) {
4951
$this->pattern = $pattern;
52+
$this->key = md5($propertyStructure->getJson()['key'] ?? $this->pattern);
53+
5054
$propertyFactory = new PropertyFactory(new PropertyProcessorFactory());
5155

5256
$this->validationProperty = $propertyFactory->create(
@@ -61,7 +65,7 @@ public function __construct(
6165
new Property($schema->getClassName(), null, $propertyStructure),
6266
DIRECTORY_SEPARATOR . 'Validator' . DIRECTORY_SEPARATOR . 'PatternProperties.phptpl',
6367
[
64-
'patternHash' => md5($propertyStructure->getJson()['key'] ?? $this->pattern),
68+
'patternHash' => $this->key,
6569
'pattern' => "/{$this->pattern}/",
6670
'validationProperty' => $this->validationProperty,
6771
'generatorConfiguration' => $schemaProcessor->getGeneratorConfiguration(),
@@ -100,6 +104,22 @@ public function getPattern(): string
100104
return $this->pattern;
101105
}
102106

107+
/**
108+
* @return string
109+
*/
110+
public function getKey(): string
111+
{
112+
return $this->key;
113+
}
114+
115+
/**
116+
* @return PropertyInterface
117+
*/
118+
public function getValidationProperty(): PropertyInterface
119+
{
120+
return $this->validationProperty;
121+
}
122+
103123
/**
104124
* Initialize all variables which are required to execute a property names validator
105125
*

src/ModelGenerator.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
use PHPModelGenerator\Exception\RenderException;
1010
use PHPModelGenerator\Exception\SchemaException;
1111
use PHPModelGenerator\Model\GeneratorConfiguration;
12-
use PHPModelGenerator\SchemaProcessor\PostProcessor\Internal\CompositionValidationPostProcessor;
13-
use PHPModelGenerator\SchemaProcessor\PostProcessor\Internal\SerializationPostProcessor;
12+
use PHPModelGenerator\SchemaProcessor\PostProcessor\Internal\ {
13+
CompositionValidationPostProcessor,
14+
ExtendObjectPropertiesMatchingPatternPropertiesPostProcessor,
15+
SerializationPostProcessor
16+
};
1417
use PHPModelGenerator\SchemaProcessor\PostProcessor\PostProcessor;
1518
use PHPModelGenerator\SchemaProcessor\RenderQueue;
1619
use PHPModelGenerator\SchemaProcessor\SchemaProcessor;
@@ -39,12 +42,14 @@ public function __construct(GeneratorConfiguration $generatorConfiguration = nul
3942
{
4043
$this->generatorConfiguration = $generatorConfiguration ?? new GeneratorConfiguration();
4144

45+
// add internal post processors which must always be executed
46+
$this
47+
->addPostProcessor(new CompositionValidationPostProcessor())
48+
->addPostProcessor(new ExtendObjectPropertiesMatchingPatternPropertiesPostProcessor());
49+
4250
if ($this->generatorConfiguration->hasSerializationEnabled()) {
4351
$this->addPostProcessor(new SerializationPostProcessor());
4452
}
45-
46-
// add internal post processors which must always be executed
47-
$this->addPostProcessor(new CompositionValidationPostProcessor());
4853
}
4954

5055
/**

src/PropertyProcessor/Filter/FilterProcessor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public function process(
5050

5151
$transformingFilter = null;
5252
// apply a different priority to each filter to make sure the order is kept
53-
$filterPriority = 10;
53+
$filterPriority = 10 + count($property->getValidators());
5454

5555
foreach ($filterList as $filterToken) {
5656
$filterOptions = [];

src/SchemaProcessor/Hook/SchemaHookResolver.php

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,22 @@ public function resolveConstructorAfterValidationHook(): string
2929

3030
public function resolveGetterHook(PropertyInterface $property): string
3131
{
32-
return $this->resolveHookWithProperty(GetterHookInterface::class, $property);
32+
return $this->resolveHook(GetterHookInterface::class, $property);
3333
}
3434

35-
public function resolveSetterBeforeValidationHook(PropertyInterface $property): string
35+
public function resolveSetterBeforeValidationHook(PropertyInterface $property, bool $batchUpdate = false): string
3636
{
37-
return $this->resolveHookWithProperty(SetterBeforeValidationHookInterface::class, $property);
37+
return $this->resolveHook(SetterBeforeValidationHookInterface::class, $property, $batchUpdate);
3838
}
3939

40-
public function resolveSetterAfterValidationHook(PropertyInterface $property): string
40+
public function resolveSetterAfterValidationHook(PropertyInterface $property, bool $batchUpdate = false): string
4141
{
42-
return $this->resolveHookWithProperty(SetterAfterValidationHookInterface::class, $property);
42+
return $this->resolveHook(SetterAfterValidationHookInterface::class, $property, $batchUpdate);
43+
}
44+
45+
public function resolveSerializationHook(): string
46+
{
47+
return $this->resolveHook(SerializationHookInterface::class);
4348
}
4449

4550
private function getHooks(string $filterHook): array
@@ -52,22 +57,12 @@ function (SchemaHookInterface $hook) use ($filterHook): bool {
5257
);
5358
}
5459

55-
private function resolveHook(string $filterHook): string
56-
{
57-
return join(
58-
"\n\n",
59-
array_map(function ($hook): string {
60-
return $hook->getCode();
61-
}, $this->getHooks($filterHook))
62-
);
63-
}
64-
65-
private function resolveHookWithProperty(string $filterHook, PropertyInterface $property): string
60+
private function resolveHook(string $filterHook, ...$parameters): string
6661
{
6762
return join(
6863
"\n\n",
69-
array_map(function ($hook) use ($property): string {
70-
return $hook->getCode($property);
64+
array_map(function ($hook) use ($parameters): string {
65+
return $hook->getCode(...$parameters);
7166
}, $this->getHooks($filterHook))
7267
);
7368
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PHPModelGenerator\SchemaProcessor\Hook;
6+
7+
interface SerializationHookInterface extends SchemaHookInterface
8+
{
9+
public function getCode(): string;
10+
}

src/SchemaProcessor/Hook/SetterAfterValidationHookInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88

99
interface SetterAfterValidationHookInterface extends SchemaHookInterface
1010
{
11-
public function getCode(PropertyInterface $property): string;
11+
public function getCode(PropertyInterface $property, bool $batchUpdate = false): string;
1212
}

src/SchemaProcessor/Hook/SetterBeforeValidationHookInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88

99
interface SetterBeforeValidationHookInterface extends SchemaHookInterface
1010
{
11-
public function getCode(PropertyInterface $property): string;
11+
public function getCode(PropertyInterface $property, bool $batchUpdate = false): string;
1212
}

src/SchemaProcessor/PostProcessor/Internal/CompositionValidationPostProcessor.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public function __construct(array $validatorPropertyMap)
147147
$this->validatorPropertyMap = $validatorPropertyMap;
148148
}
149149

150-
public function getCode(PropertyInterface $property): string
150+
public function getCode(PropertyInterface $property, bool $batchUpdate = false): string
151151
{
152152
return join(
153153
"\n",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PHPModelGenerator\SchemaProcessor\PostProcessor\Internal;
6+
7+
use PHPModelGenerator\Exception\SchemaException;
8+
use PHPModelGenerator\Filter\TransformingFilterInterface;
9+
use PHPModelGenerator\Model\GeneratorConfiguration;
10+
use PHPModelGenerator\Model\Property\PropertyInterface;
11+
use PHPModelGenerator\Model\Schema;
12+
use PHPModelGenerator\Model\Validator;
13+
use PHPModelGenerator\Model\Validator\FilterValidator;
14+
use PHPModelGenerator\Model\Validator\PatternPropertiesValidator;
15+
use PHPModelGenerator\Model\Validator\PropertyValidatorInterface;
16+
use PHPModelGenerator\PropertyProcessor\Filter\FilterProcessor;
17+
use PHPModelGenerator\SchemaProcessor\Hook\SetterBeforeValidationHookInterface;
18+
use PHPModelGenerator\SchemaProcessor\PostProcessor\PostProcessor;
19+
20+
class ExtendObjectPropertiesMatchingPatternPropertiesPostProcessor extends PostProcessor
21+
{
22+
/**
23+
* @param Schema $schema
24+
* @param GeneratorConfiguration $generatorConfiguration
25+
*
26+
* @throws SchemaException
27+
*/
28+
public function process(Schema $schema, GeneratorConfiguration $generatorConfiguration): void
29+
{
30+
$this->transferPatternPropertiesFilterToProperty($schema, $generatorConfiguration);
31+
32+
$schema->addSchemaHook(
33+
new class ($schema) implements SetterBeforeValidationHookInterface {
34+
/** @var Schema */
35+
private $schema;
36+
37+
public function __construct(Schema $schema)
38+
{
39+
$this->schema = $schema;
40+
}
41+
42+
public function getCode(PropertyInterface $property, bool $batchUpdate = false): string
43+
{
44+
$json = $this->schema->getJsonSchema()->getJson();
45+
// A batch update must execute the base validators to check the integrity of the object.
46+
// Consequently the schema hook must not add validation code in that places.
47+
if ($batchUpdate || !isset($json['patternProperties'])) {
48+
return '';
49+
}
50+
51+
$matchesAnyPattern = false;
52+
53+
/** @var PatternPropertiesValidator $patternPropertiesValidator */
54+
foreach (array_keys($json['patternProperties']) as $pattern) {
55+
if (preg_match("/{$pattern}/", $property->getName())) {
56+
$matchesAnyPattern = true;
57+
}
58+
}
59+
60+
if (!$matchesAnyPattern) {
61+
return '';
62+
}
63+
64+
// TODO: extract pattern property validation from the base validator into a separate method and
65+
// TODO: call only the pattern property validation at this location to avoid executing unnecessary
66+
// TODO: validators
67+
return sprintf('
68+
$modelData = array_merge($this->_rawModelDataInput, ["%s" => $value]);
69+
$this->executeBaseValidators($modelData);
70+
',
71+
$property->getName()
72+
);
73+
}
74+
}
75+
);
76+
}
77+
78+
/**
79+
* @param Schema $schema
80+
* @param GeneratorConfiguration $generatorConfiguration
81+
*
82+
* @throws SchemaException
83+
*/
84+
protected function transferPatternPropertiesFilterToProperty(
85+
Schema $schema,
86+
GeneratorConfiguration $generatorConfiguration
87+
): void {
88+
$patternPropertiesValidators = array_filter(
89+
$schema->getBaseValidators(),
90+
function (PropertyValidatorInterface $validator): bool {
91+
return $validator instanceof PatternPropertiesValidator;
92+
});
93+
94+
if (empty($patternPropertiesValidators)) {
95+
return;
96+
}
97+
98+
foreach ($schema->getProperties() as $property) {
99+
$propertyHasTransformingFilter = !empty(
100+
array_filter(
101+
$property->getValidators(),
102+
function (Validator $validator): bool {
103+
return $validator->getValidator() instanceof FilterValidator &&
104+
$validator->getValidator()->getFilter() instanceof TransformingFilterInterface;
105+
}
106+
)
107+
);
108+
109+
/** @var PatternPropertiesValidator $patternPropertiesValidator */
110+
foreach ($patternPropertiesValidators as $patternPropertiesValidator) {
111+
if (!preg_match("/{$patternPropertiesValidator->getPattern()}/", $property->getName()) ||
112+
!isset(
113+
$schema->getJsonSchema()->getJson()
114+
['patternProperties']
115+
[$patternPropertiesValidator->getPattern()]
116+
['filter']
117+
)
118+
) {
119+
continue;
120+
}
121+
122+
if ($propertyHasTransformingFilter) {
123+
foreach (
124+
$patternPropertiesValidator->getValidationProperty()->getValidators() as $validator
125+
) {
126+
if ($validator->getValidator() instanceof FilterValidator &&
127+
$validator->getValidator()->getFilter() instanceof TransformingFilterInterface
128+
) {
129+
throw new SchemaException(
130+
sprintf(
131+
'Applying multiple transforming filters for property %s is not supported in file %s',
132+
$property->getName(),
133+
$property->getJsonSchema()->getFile()
134+
)
135+
);
136+
}
137+
}
138+
}
139+
140+
(new FilterProcessor())->process(
141+
$property,
142+
$schema->getJsonSchema()->getJson()
143+
['patternProperties']
144+
[$patternPropertiesValidator->getPattern()]
145+
['filter'],
146+
$generatorConfiguration,
147+
$schema
148+
);
149+
}
150+
}
151+
}
152+
}

0 commit comments

Comments
 (0)