Skip to content

Commit 23d47b1

Browse files
committed
Fix error collection for composition properties
1 parent e6500d8 commit 23d47b1

16 files changed

+662
-318
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ As an optional parameter you can set up a *GeneratorConfiguration* object to con
3737
```php
3838
$generator = new Generator(
3939
(new GeneratorConfiguration())
40-
->setNamespacePrefix('\\MyApp\\Model')
40+
->setNamespacePrefix('\MyApp\Model')
4141
->setImmutable(true)
4242
);
4343

src/Exception/ErrorRegistryException.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class ErrorRegistryException extends Exception implements ErrorRegistryException
1818
public function addError(string $message): ErrorRegistryExceptionInterface
1919
{
2020
$this->errors[] = $message;
21+
22+
$this->message = join("\n", $this->errors);
23+
2124
return $this;
2225
}
2326

src/Model/RenderJob.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ protected function renderClass(GeneratorConfiguration $generatorConfiguration):
113113
$class = $render->renderTemplate(
114114
'Model.phptpl',
115115
[
116-
'namespace' => empty($namespace) ? '' : "namespace $namespace;",
116+
'namespace' => $namespace,
117117
'use' => empty($use) ? '' : 'use ' . join(";\nuse ", array_unique($use)) . ';',
118118
'class' => $this->className,
119119
'baseValidators' => $this->schema->getBaseValidators(),

src/PropertyProcessor/ComposedValue/AbstractComposedValueProcessor.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ protected function generateValidators(PropertyInterface $property, array $proper
7373
static::class,
7474
[
7575
'properties' => $properties,
76+
'generatorConfiguration' => $this->schemaProcessor->getGeneratorConfiguration(),
7677
'viewHelper' => new RenderHelper($this->schemaProcessor->getGeneratorConfiguration()),
7778
'availableAmount' => $availableAmount,
7879
'composedValueValidation' => $this->getComposedValueValidation($availableAmount),

src/PropertyProcessor/ComposedValue/IfProcessor.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ protected function generateValidators(PropertyInterface $property, array $proper
7070
'ifProperty' => $properties['if'],
7171
'thenProperty' => $properties['then'],
7272
'elseProperty' => $properties['else'],
73+
'generatorConfiguration' => $this->schemaProcessor->getGeneratorConfiguration(),
7374
'viewHelper' => new RenderHelper($this->schemaProcessor->getGeneratorConfiguration()),
7475
'onlyForDefinedValues' => $propertyData['onlyForDefinedValues'],
7576
]

src/Templates/Model.phptpl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
declare(strict_types = 1);
44

5-
{{ namespace }}
5+
{% if namespace %}
6+
namespace {{ namespace }};
7+
{% endif %}
68

79
{{ use }}
810

@@ -12,7 +14,7 @@ declare(strict_types = 1);
1214
* are re-generated.
1315
*
1416
* Class {{ class }}
15-
* @package {{ namespace }}
17+
{% if namespace %} * @package {{ namespace }} {% endif %}
1618
*/
1719
class {{ class }}
1820
{
@@ -38,7 +40,7 @@ class {{ class }}
3840
* @param array $modelData
3941
{% if generatorConfiguration.collectErrors() %}* @param {{ viewHelper.getSimpleClassName(generatorConfiguration.getErrorRegistryClass()) }} $errorRegistry{% endif %}
4042
*
41-
* @throws Throwable
43+
* @throws {% if generatorConfiguration.collectErrors() %}{{ viewHelper.getSimpleClassName(generatorConfiguration.getErrorRegistryClass()) }}{% else %}{{ viewHelper.getSimpleClassName(generatorConfiguration.getExceptionClass()) }}{% endif %}
4244
*/
4345
public function __construct(
4446
array $modelData
@@ -48,6 +50,8 @@ class {{ class }}
4850
if (!$errorRegistry) {
4951
$this->isInitialClass = true;
5052
$this->errorRegistry = new {{ viewHelper.getSimpleClassName(generatorConfiguration.getErrorRegistryClass()) }}();
53+
} else {
54+
$this->errorRegistry = $errorRegistry;
5155
}
5256
{% endif%}
5357

src/Templates/Validator/ComposedItem.phptpl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,17 @@
55
$succeededCompositionElements = {{ availableAmount }};
66
$originalModelData = $value;
77
$proposedValue = null;
8+
{% if generatorConfiguration.collectErrors() %}
9+
$originalErrorRegistry = $this->errorRegistry;
10+
$this->errorRegistry = new {{ viewHelper.getSimpleClassName(generatorConfiguration.getErrorRegistryClass()) }}();
11+
{% endif %}
812

913
{% foreach properties as property %}
1014
try {
15+
{% if generatorConfiguration.collectErrors() %}
16+
$currentErrors = count($this->errorRegistry->getErrors());
17+
{% endif %}
18+
1119
{% if not postPropose %}
1220
$proposedValue = $proposedValue ?? $value;
1321
{% endif %}
@@ -20,6 +28,14 @@
2028
}
2129
{% endforeach %}
2230

31+
{% if generatorConfiguration.collectErrors() %}
32+
// an error inside the composed validation occurred. Throw an exception to count the validity of the
33+
// composition item
34+
if ($currentErrors < count($this->errorRegistry->getErrors())) {
35+
throw new \Exception();
36+
}
37+
{% endif %}
38+
2339
{% if postPropose %}
2440
$proposedValue = $proposedValue ?? $value;
2541
{% endif %}
@@ -43,5 +59,9 @@
4359
$value = $proposedValue;
4460
{% endif %}
4561

62+
{% if generatorConfiguration.collectErrors() %}
63+
$this->errorRegistry = $originalErrorRegistry;
64+
{% endif %}
65+
4666
return !({{ composedValueValidation }});
4767
})($value)

src/Templates/Validator/ConditionalComposedItem.phptpl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,40 @@
44
(function (&$value) use (&$modelData) {
55
$originalModelData = $value;
66
$ifValid = true;
7+
{% if generatorConfiguration.collectErrors() %}
8+
$originalErrorRegistry = $this->errorRegistry;
9+
$this->errorRegistry = new {{ viewHelper.getSimpleClassName(generatorConfiguration.getErrorRegistryClass()) }}();
10+
{% endif %}
711

812
try {
13+
{% if generatorConfiguration.collectErrors() %}
14+
$currentErrors = count($this->errorRegistry->getErrors());
15+
{% endif %}
16+
917
{{ viewHelper.resolvePropertyDecorator(ifProperty) }}
1018

1119
{% foreach ifProperty.getOrderedValidators() as validator %}
1220
if ({{ validator.getCheck() }}) {
1321
{{ viewHelper.validationError(validator.getExceptionMessage()) }}
1422
}
1523
{% endforeach %}
24+
25+
{% if generatorConfiguration.collectErrors() %}
26+
// an error inside the composed validation occurred. Throw an exception to count the validity of the
27+
// composition item
28+
if ($currentErrors < count($this->errorRegistry->getErrors())) {
29+
throw new \Exception();
30+
}
31+
{% endif %}
1632
} catch (\Exception $e) {
1733
$ifValid = false;
1834
}
1935
$value = $originalModelData;
2036

37+
{% if generatorConfiguration.collectErrors() %}
38+
$this->errorRegistry = $originalErrorRegistry;
39+
{% endif %}
40+
2141
if ($ifValid) {
2242
{% if thenProperty %}
2343
{{ viewHelper.resolvePropertyDecorator(thenProperty) }}

tests/AbstractPHPModelGeneratorTest.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
namespace PHPModelGenerator\Tests;
44

55
use FilesystemIterator;
6+
use PHPModelGenerator\Exception\ErrorRegistryException;
67
use PHPModelGenerator\Exception\FileSystemException;
78
use PHPModelGenerator\Exception\RenderException;
89
use PHPModelGenerator\Exception\SchemaException;
10+
use PHPModelGenerator\Exception\ValidationException;
911
use PHPModelGenerator\ModelGenerator;
1012
use PHPModelGenerator\Model\GeneratorConfiguration;
1113
use PHPUnit\Framework\TestCase;
@@ -230,6 +232,61 @@ protected function combineDataProvider(array $dataProvider1, array $dataProvider
230232
return $result;
231233
}
232234

235+
protected function expectValidationError(GeneratorConfiguration $configuration, $messages): void
236+
{
237+
if (!is_array($messages)) {
238+
$messages = [$messages];
239+
}
240+
241+
if ($configuration->collectErrors()) {
242+
$this->expectExceptionObject($this->getErrorRegistryException($messages));
243+
} else {
244+
$this->expectException(ValidationException::class);
245+
$this->expectExceptionMessage($messages[0]);
246+
}
247+
}
248+
249+
protected function expectValidationErrorRegExp(GeneratorConfiguration $configuration, $messages)
250+
{
251+
if (!is_array($messages)) {
252+
$messages = [$messages];
253+
}
254+
255+
if ($configuration->collectErrors()) {
256+
$exception = $this->getErrorRegistryException($messages);
257+
$this->expectException(get_class($exception));
258+
$this->expectExceptionMessageRegExp($exception->getMessage());
259+
} else {
260+
$this->expectException(ValidationException::class);
261+
$this->expectExceptionMessageRegExp($messages[0]);
262+
}
263+
}
264+
265+
/**
266+
* Set up an ErrorRegistryException containing the given messages
267+
*
268+
* @param array $messages
269+
*
270+
* @return ErrorRegistryException
271+
*/
272+
protected function getErrorRegistryException(array $messages): ErrorRegistryException
273+
{
274+
$errorRegistry = new ErrorRegistryException();
275+
276+
foreach ($messages as $message) {
277+
$errorRegistry->addError($message);
278+
}
279+
280+
return $errorRegistry;
281+
}
282+
283+
public function validationMethodDataProvider(): array {
284+
return [
285+
'Error Collection' => [new GeneratorConfiguration()],
286+
'Direct Exception' => [(new GeneratorConfiguration())->setCollectErrors(false)],
287+
];
288+
}
289+
233290
/**
234291
* Get the annotated type for an object property
235292
*

tests/Basic/ErrorCollectionTest.php

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace PHPModelGenerator\Tests\Basic;
66

7-
use PHPModelGenerator\Exception\ErrorRegistryException;
87
use PHPModelGenerator\Model\GeneratorConfiguration;
98
use PHPModelGenerator\Tests\AbstractPHPModelGeneratorTest;
109
use stdClass;
@@ -85,22 +84,4 @@ public function invalidValuesForSinglePropertyDataProvider(): array
8584
'object' => [new stdClass(), ['invalid type for property']],
8685
];
8786
}
88-
89-
/**
90-
* Set up an ErrorRegistryException containing the given messages
91-
*
92-
* @param array $messages
93-
*
94-
* @return ErrorRegistryException
95-
*/
96-
protected function getErrorRegistryException(array $messages): ErrorRegistryException
97-
{
98-
$errorRegistry = new ErrorRegistryException();
99-
100-
foreach ($messages as $message) {
101-
$errorRegistry->addError($message);
102-
}
103-
104-
return $errorRegistry;
105-
}
10687
}

tests/ComposedValue/ComposedIfTest.php

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
namespace PHPModelGenerator\Tests\ComposedValue;
44

5-
use PHPModelGenerator\Exception\ValidationException;
5+
use PHPModelGenerator\Exception\FileSystemException;
6+
use PHPModelGenerator\Exception\RenderException;
7+
use PHPModelGenerator\Exception\SchemaException;
8+
use PHPModelGenerator\Model\GeneratorConfiguration;
69
use PHPModelGenerator\Tests\AbstractPHPModelGeneratorTest;
710

811
/**
@@ -15,13 +18,18 @@ class ComposedIfTest extends AbstractPHPModelGeneratorTest
1518
/**
1619
* @dataProvider validConditionalObjectPropertyDataProvider
1720
*
21+
* @param GeneratorConfiguration $configuration
1822
* @param string $streetAddress
1923
* @param string $country
2024
* @param string $postalCode
2125
*/
22-
public function testConditionalObjectProperty(?string $streetAddress, ?string $country, ?string $postalCode): void
23-
{
24-
$className = $this->generateClassFromFile('ConditionalObjectProperty.json');
26+
public function testConditionalObjectProperty(
27+
GeneratorConfiguration $configuration,
28+
?string $streetAddress,
29+
?string $country,
30+
?string $postalCode
31+
): void {
32+
$className = $this->generateClassFromFile('ConditionalObjectProperty.json', $configuration);
2533

2634
$object = new $className([
2735
'street_address' => $streetAddress,
@@ -36,29 +44,37 @@ public function testConditionalObjectProperty(?string $streetAddress, ?string $c
3644

3745
public function validConditionalObjectPropertyDataProvider(): array
3846
{
39-
return [
40-
'not provided postal code' => ['1600 Pennsylvania Avenue NW', 'USA', null],
41-
'USA postal code' => ['1600 Pennsylvania Avenue NW', 'USA', '20500'],
42-
'Canada postal code' => ['24 Sussex Drive', 'Canada', 'K1M 1M4'],
43-
];
47+
return $this->combineDataProvider(
48+
$this->validationMethodDataProvider(),
49+
[
50+
'not provided postal code' => ['1600 Pennsylvania Avenue NW', 'USA', null],
51+
'USA postal code' => ['1600 Pennsylvania Avenue NW', 'USA', '20500'],
52+
'Canada postal code' => ['24 Sussex Drive', 'Canada', 'K1M 1M4'],
53+
]
54+
);
4455
}
4556

4657
/**
4758
* @dataProvider invalidConditionalObjectPropertyDataProvider
4859
*
60+
* @param GeneratorConfiguration $configuration
4961
* @param string $streetAddress
5062
* @param string $country
5163
* @param string $postalCode
64+
*
65+
* @throws FileSystemException
66+
* @throws RenderException
67+
* @throws SchemaException
5268
*/
5369
public function testInvalidConditionalObjectPropertyThrowsAnException(
70+
GeneratorConfiguration $configuration,
5471
?string $streetAddress,
5572
?string $country,
5673
?string $postalCode
5774
): void {
58-
$this->expectException(ValidationException::class);
59-
$this->expectExceptionMessageRegExp('/postal_code doesn\'t match pattern .*/');
75+
$this->expectValidationErrorRegExp($configuration, '/postal_code doesn\'t match pattern .*/');
6076

61-
$className = $this->generateClassFromFile('ConditionalObjectProperty.json');
77+
$className = $this->generateClassFromFile('ConditionalObjectProperty.json', $configuration);
6278

6379
new $className([
6480
'street_address' => $streetAddress,
@@ -69,11 +85,14 @@ public function testInvalidConditionalObjectPropertyThrowsAnException(
6985

7086
public function invalidConditionalObjectPropertyDataProvider(): array
7187
{
72-
return [
73-
'empty provided postal code' => ['1600 Pennsylvania Avenue NW', 'USA', ''],
74-
'Canadian postal code for USA' => ['1600 Pennsylvania Avenue NW', 'USA', 'K1M 1M4'],
75-
'USA postal code for Canada' => ['24 Sussex Drive', 'Canada', '20500'],
76-
'Unmatching postal code for both' => ['24 Sussex Drive', 'Canada', 'djqwWDJId8juw9duq9'],
77-
];
88+
return $this->combineDataProvider(
89+
$this->validationMethodDataProvider(),
90+
[
91+
'empty provided postal code' => ['1600 Pennsylvania Avenue NW', 'USA', ''],
92+
'Canadian postal code for USA' => ['1600 Pennsylvania Avenue NW', 'USA', 'K1M 1M4'],
93+
'USA postal code for Canada' => ['24 Sussex Drive', 'Canada', '20500'],
94+
'Unmatching postal code for both' => ['24 Sussex Drive', 'Canada', 'djqwWDJId8juw9duq9'],
95+
]
96+
);
7897
}
7998
}

0 commit comments

Comments
 (0)