Skip to content

Commit ac2ae33

Browse files
authored
Optimize exceptions for nested objects (#20)
Optimize exceptions for nested objects to be represented by a dedicated exception instead of being merged into top level messages
1 parent 9f1d46a commit ac2ae33

16 files changed

+83
-38
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
],
1313
"require": {
1414
"symplify/easy-coding-standard": "^7.2.3",
15-
"wol-soft/php-json-schema-model-generator-production": "^0.13.0",
15+
"wol-soft/php-json-schema-model-generator-production": "0.14.0",
1616
"wol-soft/php-micro-template": "^1.3.2",
1717

1818
"php": ">=7.2",

docs/source/complexTypes/object.rst

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,12 @@ Generated interface:
4646
4747
Possible exceptions:
4848

49-
* Invalid type for car. Requires object, got __TYPE__
49+
.. code-block:: none
50+
51+
* Invalid type for car. Requires object, got __TYPE__
5052
51-
The nested object will be validated in the nested class Car which may throw additional exceptions if invalid data is provided.
53+
* Invalid nested object for property car:
54+
- Invalid type for model. Requires string, got __TYPE__
5255
5356
The thrown exception will be a *PHPModelGenerator\\Exception\\Generic\\InvalidTypeException* which provides the following methods to get further error details:
5457

@@ -61,6 +64,23 @@ The thrown exception will be a *PHPModelGenerator\\Exception\\Generic\\InvalidTy
6164
// get the value provided to the property
6265
public function getProvidedValue()
6366
67+
The nested object will be validated in the nested class Car which may throw additional exceptions if invalid data is provided. If the internal validation of a nested object fails a *PHPModelGenerator\\Exception\\Generic\\NestedObjectException* will be thrown which provides the following methods to get further error details:
68+
69+
.. code-block:: php
70+
71+
// Returns the exception which was thrown in the nested object
72+
public function getNestedException()
73+
// get the name of the property which contains the nested object
74+
public function getPropertyName(): string
75+
// get the value provided to the property
76+
public function getProvidedValue()
77+
78+
If `error collection <../gettingStarted.html#collect-errors-vs-early-return>`__ is enabled the nested exception returned by `getNestedException` will be an **ErrorRegistryException** containing all validation errors of the nested object. Otherwise it will contain the first validation error which occurred during the validation of the nested object.
79+
80+
.. hint::
81+
82+
If the class created for a nested object is instantiated manually you will either get a collection exception or a specific exception based on your error collection configuration if invalid data is provided.
83+
6484
Namespaces
6585
----------
6686

docs/source/gettingStarted.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ All collected exceptions from an ErrorRegistryException are accessible via the *
193193
(new GeneratorConfiguration())
194194
->setCollectErrors(false);
195195
196+
.. hint::
197+
198+
All builtin exceptions provide serialization methods (compare `serialization <#serialization-methods>`_). By default sensitive data (file and line) of the exception will not be serialized. The serialization methods provide another parameter `$stripSensitiveData`. When this parameter is set to false file and line information will be included.
199+
196200
Custom exception classes
197201
^^^^^^^^^^^^^^^^^^^^^^^^
198202

src/Model/Property/Property.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,10 @@ public function addDecorator(PropertyDecoratorInterface $decorator): PropertyInt
215215
/**
216216
* @inheritdoc
217217
*/
218-
public function resolveDecorator(string $input): string
218+
public function resolveDecorator(string $input, bool $nestedProperty): string
219219
{
220220
foreach ($this->decorators as $decorator) {
221-
$input = $decorator->decorate($input, $this);
221+
$input = $decorator->decorate($input, $this, $nestedProperty);
222222
}
223223

224224
return $input;

src/Model/Property/PropertyInterface.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,11 @@ public function addDecorator(PropertyDecoratorInterface $decorator): PropertyInt
119119
* Resolve all decorators of the property
120120
*
121121
* @param string $input
122+
* @param bool $nestedProperty
122123
*
123124
* @return string
124125
*/
125-
public function resolveDecorator(string $input): string;
126+
public function resolveDecorator(string $input, bool $nestedProperty): string;
126127

127128
/**
128129
* @return bool

src/Model/Property/PropertyProxy.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,9 @@ public function addDecorator(PropertyDecoratorInterface $decorator): PropertyInt
144144
/**
145145
* @inheritdoc
146146
*/
147-
public function resolveDecorator(string $input): string
147+
public function resolveDecorator(string $input, bool $nestedProperty): string
148148
{
149-
return $this->getProperty()->resolveDecorator($input);
149+
return $this->getProperty()->resolveDecorator($input, $nestedProperty);
150150
}
151151

152152
/**

src/PropertyProcessor/Decorator/Property/IntToFloatCastDecorator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class IntToFloatCastDecorator implements PropertyDecoratorInterface
1616
/**
1717
* @inheritdoc
1818
*/
19-
public function decorate(string $input, PropertyInterface $property): string
19+
public function decorate(string $input, PropertyInterface $property, bool $nestedProperty): string
2020
{
2121
return "is_int($input) ? (float) $input : $input";
2222
}

src/PropertyProcessor/Decorator/Property/ObjectInstantiationDecorator.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
namespace PHPModelGenerator\PropertyProcessor\Decorator\Property;
66

77
use PHPMicroTemplate\Render;
8+
use PHPModelGenerator\Exception\Object\NestedObjectException;
89
use PHPModelGenerator\Model\GeneratorConfiguration;
910
use PHPModelGenerator\Model\Property\PropertyInterface;
11+
use PHPModelGenerator\Model\Validator\PropertyValidator;
12+
use PHPModelGenerator\Utils\RenderHelper;
1013

1114
/**
1215
* Class ObjectInstantiationDecorator
@@ -43,17 +46,21 @@ public function __construct(string $className, GeneratorConfiguration $generator
4346
/**
4447
* @inheritdoc
4548
*/
46-
public function decorate(string $input, PropertyInterface $property): string
49+
public function decorate(string $input, PropertyInterface $property, bool $nestedProperty): string
4750
{
48-
$template = $this->generatorConfiguration->collectErrors()
49-
? 'ObjectInstantiationDecoratorErrorRegistry.phptpl'
50-
: 'ObjectInstantiationDecoratorDirectException.phptpl';
51-
5251
return static::$renderer->renderTemplate(
53-
DIRECTORY_SEPARATOR . 'Decorator' . DIRECTORY_SEPARATOR . $template,
52+
DIRECTORY_SEPARATOR . 'Decorator' . DIRECTORY_SEPARATOR . 'ObjectInstantiationDecorator.phptpl',
5453
[
5554
'input' => $input,
5655
'className' => $this->className,
56+
'nestedProperty' => $nestedProperty,
57+
'viewHelper' => new RenderHelper($this->generatorConfiguration),
58+
'generatorConfiguration' => $this->generatorConfiguration,
59+
'nestedValidator' => new PropertyValidator(
60+
'',
61+
NestedObjectException::class,
62+
[$property->getName(), '&$instantiationException']
63+
),
5764
]
5865
);
5966
}

src/PropertyProcessor/Decorator/Property/PropertyDecoratorInterface.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ interface PropertyDecoratorInterface
1616
/**
1717
* Decorate a given string
1818
*
19-
* @param string $input
19+
* @param string $input
2020
* @param PropertyInterface $property The property getting decorated
21+
* @param bool $nestedProperty
2122
*
2223
* @return string
2324
*/
24-
public function decorate(string $input, PropertyInterface $property): string;
25+
public function decorate(string $input, PropertyInterface $property, bool $nestedProperty): string;
2526
}

src/PropertyProcessor/Decorator/Property/PropertyTransferDecorator.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ public function __construct(PropertyInterface $property)
3232
/**
3333
* @inheritdoc
3434
*/
35-
public function decorate(string $input, PropertyInterface $property): string
35+
public function decorate(string $input, PropertyInterface $property, bool $nestedProperty): string
3636
{
37-
return $this->property->resolveDecorator($input);
37+
return $this->property->resolveDecorator($input, $nestedProperty);
3838
}
3939
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
(function ($value) {
2+
try {
3+
return is_array($value) ? new {{ className }}($value) : $value;
4+
} catch (\Exception $instantiationException) {
5+
{% if nestedProperty %}
6+
{{ viewHelper.validationError(nestedValidator) }}
7+
{% else %}
8+
{% if generatorConfiguration.collectErrors() %}
9+
foreach($instantiationException->getErrors() as $nestedValidationError) {
10+
$this->errorRegistry->addError($nestedValidationError);
11+
}
12+
{% else %}
13+
throw $instantiationException;
14+
{% endif %}
15+
{% endif %}
16+
17+
{% if generatorConfiguration.collectErrors() %}
18+
return $instantiationException;
19+
{% endif %}
20+
}
21+
})({{ input }})

src/Templates/Decorator/ObjectInstantiationDecoratorDirectException.phptpl

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/Templates/Decorator/ObjectInstantiationDecoratorErrorRegistry.phptpl

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/Templates/Model.phptpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ class {{ class }} {% if schema.getInterfaces() %}implements {{ viewHelper.joinCl
163163

164164
$value = $modelData['{{ property.getName() }}'] ?? $this->{{ property.getAttribute() }};
165165

166-
{{ viewHelper.resolvePropertyDecorator(property) }}
166+
{{ viewHelper.resolvePropertyDecorator(property, true) }}
167167

168168
$this->{{ property.getAttribute() }} = $this->validate{{ viewHelper.ucfirst(property.getAttribute()) }}($value, $modelData);
169169
}

src/Utils/RenderHelper.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,19 @@ public function joinClassNames(array $fqcns): string
7474
* Resolve all associated decorators of a property
7575
*
7676
* @param PropertyInterface $property
77+
* @param bool $nestedProperty
7778
*
7879
* @return string
7980
*/
80-
public function resolvePropertyDecorator(PropertyInterface $property): string
81+
public function resolvePropertyDecorator(PropertyInterface $property, bool $nestedProperty = false): string
8182
{
8283
if (!$property->hasDecorators()) {
8384
return '';
8485
}
8586

8687
return $property->isRequired()
87-
? '$value = ' . $property->resolveDecorator('$value') . ';'
88-
: 'if ($value !== null) { $value = ' . $property->resolveDecorator('$value') . '; }';
88+
? '$value = ' . $property->resolveDecorator('$value', $nestedProperty) . ';'
89+
: 'if ($value !== null) { $value = ' . $property->resolveDecorator('$value', $nestedProperty) . '; }';
8990
}
9091

9192
/**

tests/Basic/SchemaDependencyTest.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,9 @@ public function invalidSchemaDependencyNestedObjectDataProvider(): array
353353
Invalid schema which is dependant on credit_card:
354354
- Missing required value for billing_address
355355
- Invalid type for billing_address. Requires string, got NULL
356-
- Missing required value for name
357-
- Invalid type for name. Requires string, got NULL
356+
- Invalid nested object for property owner:
357+
- Missing required value for name
358+
- Invalid type for name. Requires string, got NULL
358359
ERROR
359360
],
360361
'invalid data type' => [
@@ -368,7 +369,8 @@ public function invalidSchemaDependencyNestedObjectDataProvider(): array
368369
],
369370
<<<ERROR
370371
Invalid schema which is dependant on credit_card:
371-
- Invalid type for name. Requires string, got boolean
372+
- Invalid nested object for property owner:
373+
- Invalid type for name. Requires string, got boolean
372374
ERROR
373375
],
374376
];

0 commit comments

Comments
 (0)