Skip to content

Commit 83acf61

Browse files
kolodkinvalentinKolodkin Valentin
and
Kolodkin Valentin
authored
Added support for non required const-properties (#82)
* Added support for non required const-properties * Replaced interface to abstract class * Added implementation of implicit null for constant properties * Changed the check setRequired * Remove modelData from check conditions * Added call generateValidators() and unit test * Replaced check with three conditions * Ensured the presence of the $modelData variable in the templates, added some unit tests --------- Co-authored-by: Kolodkin Valentin <[email protected]>
1 parent 1bb1374 commit 83acf61

File tree

12 files changed

+283
-22
lines changed

12 files changed

+283
-22
lines changed

src/Model/Validator/ExtractedMethodValidator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function __construct(
5959
public function getCode(): string
6060
{
6161
$renderHelper = new RenderHelper($this->generatorConfiguration);
62-
return "private function {$this->validator->getExtractedMethodName()}(&\$value): void {
62+
return "private function {$this->validator->getExtractedMethodName()}(&\$value, \$modelData): void {
6363
{$this->validator->getValidatorSetUp()}
6464
6565
if ({$this->validator->getCheck()}) {

src/PropertyProcessor/Property/ConstProcessor.php

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010
use PHPModelGenerator\Model\Property\PropertyType;
1111
use PHPModelGenerator\Model\SchemaDefinition\JsonSchema;
1212
use PHPModelGenerator\Model\Validator\PropertyValidator;
13-
use PHPModelGenerator\PropertyProcessor\PropertyProcessorInterface;
13+
use PHPModelGenerator\Utils\RenderHelper;
1414
use PHPModelGenerator\Utils\TypeConverter;
1515

1616
/**
1717
* Class ConstProcessor
1818
*
1919
* @package PHPModelGenerator\PropertyProcessor\Property
2020
*/
21-
class ConstProcessor implements PropertyProcessorInterface
21+
class ConstProcessor extends AbstractPropertyProcessor
2222
{
2323
/**
2424
* @inheritdoc
@@ -34,13 +34,26 @@ public function process(string $propertyName, JsonSchema $propertySchema): Prope
3434
$json['description'] ?? '',
3535
);
3636

37-
return $property
38-
->setRequired(true)
39-
->addValidator(new PropertyValidator(
40-
$property,
41-
'$value !== ' . var_export($json['const'], true),
42-
InvalidConstException::class,
43-
[$json['const']],
44-
));
37+
$property->setRequired($this->propertyMetaDataCollection->isAttributeRequired($propertyName));
38+
39+
$check = match(true) {
40+
$property->isRequired()
41+
=> '$value !== ' . var_export($json['const'], true),
42+
$this->isImplicitNullAllowed($property)
43+
=> '!in_array($value, ' . RenderHelper::varExportArray([$json['const'], null]) . ', true)',
44+
default
45+
=> "array_key_exists('{$property->getName()}', \$modelData) && \$value !== " . var_export($json['const'], true),
46+
};
47+
48+
$property->addValidator(new PropertyValidator(
49+
$property,
50+
$check,
51+
InvalidConstException::class,
52+
[$json['const']],
53+
));
54+
55+
$this->generateValidators($property, $propertySchema);
56+
57+
return $property;
4558
}
4659
}

src/Templates/Validator/AdditionalProperties.phptpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
(function () use ($properties, &$invalidProperties) {
1+
(function () use ($properties, &$invalidProperties, $modelData) {
22
{% if generatorConfiguration.collectErrors() %}
33
$originalErrorRegistry = $this->_errorRegistry;
44
{% endif %}

src/Templates/Validator/ArrayItem.phptpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
is_array($value) && (function (&$items) use (&$invalidItems{{ suffix }}) {
1+
is_array($value) && (function (&$items) use (&$invalidItems{{ suffix }}, $modelData) {
22
{% if generatorConfiguration.collectErrors() %}
33
$originalErrorRegistry = $this->_errorRegistry;
44
{% endif %}

src/Templates/Validator/ArrayTuple.phptpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
is_array($value) && (function (&$items) use (&$invalidTuples) {
1+
is_array($value) && (function (&$items) use (&$invalidTuples, $modelData) {
22
{% if generatorConfiguration.collectErrors() %}
33
$originalErrorRegistry = $this->_errorRegistry;
44
{% endif %}

src/Templates/Validator/PatternProperties.phptpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
(function () use ($properties, &$invalidProperties) {
1+
(function () use ($properties, &$invalidProperties, $modelData) {
22
{% if generatorConfiguration.collectErrors() %}
33
$originalErrorRegistry = $this->_errorRegistry;
44
{% endif %}

src/Utils/RenderHelper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ public function renderValidator(PropertyValidatorInterface $validator, Schema $s
182182
$schema->addMethod($validator->getExtractedMethodName(), $validator->getMethod());
183183
}
184184

185-
return "\$this->{$validator->getExtractedMethodName()}(\$value);";
185+
return "\$this->{$validator->getExtractedMethodName()}(\$value, \$modelData);";
186186
}
187187

188188
public function renderMethods(Schema $schema): string

tests/Objects/ConstPropertyTest.php

Lines changed: 201 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44

55
namespace PHPModelGenerator\Tests\Objects;
66

7+
use PHPModelGenerator\Exception\Arrays\InvalidTupleException;
8+
use PHPModelGenerator\Exception\ComposedValue\OneOfException;
9+
use PHPModelGenerator\Exception\ErrorRegistryException;
710
use PHPModelGenerator\Exception\FileSystemException;
11+
use PHPModelGenerator\Exception\Object\RequiredValueException;
812
use PHPModelGenerator\Exception\ValidationException;
913
use PHPModelGenerator\Exception\RenderException;
1014
use PHPModelGenerator\Exception\SchemaException;
15+
use PHPModelGenerator\Model\GeneratorConfiguration;
1116
use PHPModelGenerator\Tests\AbstractPHPModelGeneratorTestCase;
1217
use stdClass;
1318

@@ -38,14 +43,38 @@ public function testProvidedConstPropertyIsValid(): void
3843
* @throws RenderException
3944
* @throws SchemaException
4045
*/
41-
public function testNotProvidedConstPropertyThrowsAnException(): void
46+
public function testProvidedArrayItemConstPropertyIsValid(): void
4247
{
43-
$this->expectException(ValidationException::class);
44-
$this->expectExceptionMessage('Invalid value for stringProperty declined by const constraint');
48+
$className = $this->generateClassFromFile('ArrayItemConstProperty.json');
4549

46-
$className = $this->generateClassFromFile('ConstProperty.json');
50+
$object = new $className(['property' => ['red', 'red']]);
4751

48-
new $className([]);
52+
$this->assertIsArray($object->getProperty());
53+
$this->assertSame(['red', 'red'], $object->getProperty());
54+
}
55+
56+
/**
57+
* @dataProvider stringIntDataProvider
58+
*
59+
* @throws FileSystemException
60+
* @throws RenderException
61+
* @throws SchemaException
62+
*/
63+
public function testProvidedAnyOfConstPropertyIsValid(string|int $propertyValue): void
64+
{
65+
$className = $this->generateClassFromFile('AnyOfConstProperty.json');
66+
67+
$object = new $className(['property' => $propertyValue]);
68+
69+
$this->assertSame($propertyValue, $object->getProperty());
70+
}
71+
72+
public function stringIntDataProvider(): array
73+
{
74+
return [
75+
['red'],
76+
[1],
77+
];
4978
}
5079

5180
/**
@@ -62,7 +91,7 @@ public function testNotMatchingProvidedDataThrowsAnException($propertyValue): vo
6291
$this->expectException(ValidationException::class);
6392
$this->expectExceptionMessage('Invalid value for stringProperty declined by const constraint');
6493

65-
$className = $this->generateClassFromFile('ConstProperty.json');
94+
$className = $this->generateClassFromFile('ConstProperty.json', null, false, false);
6695

6796
new $className(['stringProperty' => $propertyValue]);
6897
}
@@ -79,4 +108,170 @@ public function invalidPropertyDataProvider(): array
79108
'null' => [null],
80109
];
81110
}
111+
112+
/**
113+
* @throws FileSystemException
114+
* @throws RenderException
115+
* @throws SchemaException
116+
*/
117+
public function testNotMatchingArrayItemConstPropertyThrowsAnException(): void
118+
{
119+
$this->expectException(InvalidTupleException::class);
120+
$this->expectExceptionMessage('Invalid tuple item in array property');
121+
122+
$className = $this->generateClassFromFile('ArrayItemConstProperty.json');
123+
124+
new $className(['property' => ['green']]);
125+
}
126+
127+
/**
128+
* @throws FileSystemException
129+
* @throws RenderException
130+
* @throws SchemaException
131+
*/
132+
public function testNotMatchingArrayItemConstPropertyThrowsAnException1(): void
133+
{
134+
$this->expectException(OneOfException::class);
135+
$this->expectExceptionMessage('Invalid value for property declined by composition constraint');
136+
137+
$className = $this->generateClassFromFile('AnyOfConstProperty.json');
138+
139+
new $className(['property' => 'green']);
140+
}
141+
142+
/**
143+
* @throws FileSystemException
144+
* @throws RenderException
145+
* @throws SchemaException
146+
*/
147+
public function testProvidedConstOnlyRequiredPropertyIsValid(): void
148+
{
149+
$className = $this->generateClassFromFile('RequiredAndOptionalConstProperties.json');
150+
151+
$object = new $className(['requiredProperty' => 'red']);
152+
153+
$this->assertSame('red', $object->getRequiredProperty());
154+
$this->assertNull($object->getOptionalProperty());
155+
}
156+
157+
/**
158+
* @throws FileSystemException
159+
* @throws RenderException
160+
* @throws SchemaException
161+
*/
162+
public function testProvidedNullOptionalPropertyConstPropertyIsValid(): void
163+
{
164+
$className = $this->generateClassFromFile('RequiredAndOptionalConstProperties.json');
165+
166+
$object = new $className(['requiredProperty' => 'red', 'optionalProperty' => null]);
167+
168+
$this->assertSame('red', $object->getRequiredProperty());
169+
$this->assertNull($object->getOptionalProperty());
170+
}
171+
172+
/**
173+
* @dataProvider requiredAndOptionalPropertiesDataProvider
174+
*
175+
* @throws FileSystemException
176+
* @throws RenderException
177+
* @throws SchemaException
178+
*/
179+
public function testProvidedConstPropertiesIsValidWithDifferentImplicitNull(
180+
bool $implicitNull,
181+
string $reqPropertyValue,
182+
string $optPropertyValue
183+
): void
184+
{
185+
$className = $this->generateClassFromFile(
186+
'RequiredAndOptionalConstProperties.json',
187+
new GeneratorConfiguration(),
188+
false,
189+
$implicitNull,
190+
);
191+
192+
$object = new $className(['requiredProperty' => $reqPropertyValue, 'optionalProperty' => $optPropertyValue]);
193+
194+
$this->assertSame($reqPropertyValue, $object->getRequiredProperty());
195+
$this->assertSame($optPropertyValue, $object->getOptionalProperty());
196+
}
197+
198+
public function requiredAndOptionalPropertiesDataProvider(): array
199+
{
200+
return $this->combineDataProvider(
201+
$this->implicitNullDataProvider(),
202+
[
203+
['red', 'green'],
204+
],
205+
);
206+
}
207+
208+
/**
209+
* @throws FileSystemException
210+
* @throws RenderException
211+
* @throws SchemaException
212+
*/
213+
public function testNotProvidedRequiredPropertyThrowsAnException(): void
214+
{
215+
$this->expectException(RequiredValueException::class);
216+
$this->expectExceptionMessage('Missing required value for requiredProperty');
217+
218+
$className = $this->generateClassFromFile('RequiredAndOptionalConstProperties.json');
219+
220+
new $className([]);
221+
}
222+
223+
/**
224+
* @dataProvider invalidRequiredAndOptionalConstPropertiesDataProvider
225+
*
226+
* @throws FileSystemException
227+
* @throws RenderException
228+
* @throws SchemaException
229+
*/
230+
public function testNotMatchingRequiredAndOptionalProvidedDataThrowsAnException(
231+
bool $implicitNull,
232+
string $reqPropertyValue,
233+
?string $optPropertyValue,
234+
string $exceptionMessage
235+
): void
236+
{
237+
$className = $this->generateClassFromFile(
238+
'RequiredAndOptionalConstProperties.json',
239+
new GeneratorConfiguration(),
240+
false,
241+
$implicitNull,
242+
);
243+
244+
$this->expectException(ErrorRegistryException::class);
245+
$this->expectExceptionMessage($exceptionMessage);
246+
247+
new $className(['requiredProperty' => $reqPropertyValue, 'optionalProperty' => $optPropertyValue]);
248+
}
249+
250+
public function invalidRequiredAndOptionalConstPropertiesDataProvider(): array
251+
{
252+
return $this->combineDataProvider(
253+
$this->implicitNullDataProvider(),
254+
[
255+
['blue', 'green', 'Invalid value for requiredProperty declined by const constraint'],
256+
['blue', null, 'Invalid value for requiredProperty declined by const constraint'],
257+
['red', 'blue', 'Invalid value for optionalProperty declined by const constraint'],
258+
['red', '0', 'Invalid value for optionalProperty declined by const constraint'],
259+
['red', '', 'Invalid value for optionalProperty declined by const constraint'],
260+
],
261+
);
262+
}
263+
264+
/**
265+
* @throws FileSystemException
266+
* @throws RenderException
267+
* @throws SchemaException
268+
*/
269+
public function testProvidedNullValueConstPropertyIsValid(): void
270+
{
271+
$className = $this->generateClassFromFile('NullValueConstProperty.json', null, false, false);
272+
273+
$object = new $className(['nullProperty' => null]);
274+
275+
$this->assertNull($object->getNullProperty());
276+
}
82277
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"property": {
5+
"oneOf": [
6+
{
7+
"const": "red"
8+
},
9+
{
10+
"const": 1
11+
}
12+
]
13+
}
14+
}
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"property": {
5+
"type": "array",
6+
"items": [
7+
{
8+
"const": "red"
9+
}
10+
]
11+
}
12+
}
13+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"nullProperty": {
5+
"const": null
6+
}
7+
},
8+
"required": [
9+
"nullProperty"
10+
]
11+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"requiredProperty": {
5+
"const": "red"
6+
},
7+
"optionalProperty": {
8+
"const": "green"
9+
}
10+
},
11+
"required": [
12+
"requiredProperty"
13+
]
14+
}

0 commit comments

Comments
 (0)