Skip to content

Commit 9747966

Browse files
committed
Add SchemaHook test cases and update docs
1 parent 967ef71 commit 9747966

File tree

3 files changed

+227
-0
lines changed

3 files changed

+227
-0
lines changed

docs/source/generator/postProcessor.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ Now let's have a look at the behaviour of the generated model:
7979

8080
If the **PopulatePostProcessor** is added to your model generator the populate method will be added to the model independently of the `immutable setting <../gettingStarted.html#immutable-classes>`__.
8181

82+
The **PopulatePostProcessor** will also resolve all hooks which are applied to setters. Added code will be executed for all properties changed by a populate call.
83+
8284
AdditionalPropertiesAccessorPostProcessor
8385
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
8486

@@ -163,3 +165,15 @@ A custom post processor which adds a custom trait to the generated model (eg. a
163165
.. hint::
164166

165167
For examples how to implement a custom post processor have a look at the built in post processors located at **src/SchemaProcessor/PostProcessor/**
168+
169+
What can you do inside your custom post processor?
170+
171+
* Add additional traits and interfaces to your models
172+
* Add additional methods and properties to your models
173+
* Hook via SchemaHooks into the generated source code and add your snippets at defined places inside the model:
174+
175+
* Implement the **ConstructorBeforeValidationHookInterface** to add code to the beginning of your constructor
176+
* Implement the **ConstructorAfterValidationHookInterface** to add code to the end of your constructor
177+
* Implement the **GetterHookInterface** to add code to your getter methods
178+
* Implement the **SetterBeforeValidationHookInterface** to add code to the beginning of your setter methods
179+
* Implement the **SetterAfterValidationHookInterface** to add code to the end of your setter methods

tests/Basic/SchemaHookTest.php

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<?php
2+
3+
namespace PHPModelGenerator\Tests\Basic;
4+
5+
use Exception;
6+
use PHPModelGenerator\Exception\Generic\InvalidTypeException;
7+
use PHPModelGenerator\Exception\Number\MinimumException;
8+
use PHPModelGenerator\Model\GeneratorConfiguration;
9+
use PHPModelGenerator\Model\Property\PropertyInterface;
10+
use PHPModelGenerator\Model\Schema;
11+
use PHPModelGenerator\ModelGenerator;
12+
use PHPModelGenerator\SchemaProcessor\Hook\ConstructorAfterValidationHookInterface;
13+
use PHPModelGenerator\SchemaProcessor\Hook\ConstructorBeforeValidationHookInterface;
14+
use PHPModelGenerator\SchemaProcessor\Hook\GetterHookInterface;
15+
use PHPModelGenerator\SchemaProcessor\Hook\SchemaHookInterface;
16+
use PHPModelGenerator\SchemaProcessor\Hook\SetterAfterValidationHookInterface;
17+
use PHPModelGenerator\SchemaProcessor\Hook\SetterBeforeValidationHookInterface;
18+
use PHPModelGenerator\SchemaProcessor\PostProcessor\PostProcessorInterface;
19+
use PHPModelGenerator\Tests\AbstractPHPModelGeneratorTest;
20+
21+
/**
22+
* Class SchemaHookTest
23+
*
24+
* @package PHPModelGenerator\Tests\Basic
25+
*/
26+
class SchemaHookTest extends AbstractPHPModelGeneratorTest
27+
{
28+
public function testConstructorBeforeValidationHookIsResolved(): void
29+
{
30+
$this->addSchemaHook(new class () implements ConstructorBeforeValidationHookInterface {
31+
public function getCode(): string
32+
{
33+
return 'throw new \Exception("ConstructorBeforeValidationHook");';
34+
}
35+
});
36+
37+
$className = $this->generateClassFromFile('BasicSchema.json');
38+
39+
$this->expectException(Exception::class);
40+
$this->expectExceptionMessage("ConstructorBeforeValidationHook");
41+
42+
new $className(['name' => false]);
43+
}
44+
45+
/**
46+
* @dataProvider constructorAfterValidationHookDataProvider
47+
*
48+
* @param $value
49+
* @param string $expectedException
50+
* @param string $expectedExceptionMessage
51+
*/
52+
public function testConstructorAfterValidationHookIsResolved(
53+
$value,
54+
string $expectedException,
55+
string $expectedExceptionMessage
56+
): void {
57+
$this->addSchemaHook(new class () implements ConstructorAfterValidationHookInterface {
58+
public function getCode(): string
59+
{
60+
return 'throw new \Exception("ConstructorAfterValidationHook");';
61+
}
62+
});
63+
64+
$className = $this->generateClassFromFile('BasicSchema.json');
65+
66+
$this->expectException($expectedException);
67+
$this->expectExceptionMessage($expectedExceptionMessage);
68+
69+
new $className(['name' => $value]);
70+
}
71+
72+
public function constructorAfterValidationHookDataProvider(): array
73+
{
74+
return [
75+
'Invalid value' => [
76+
false,
77+
InvalidTypeException::class,
78+
'Invalid type for name. Requires string, got boolean',
79+
],
80+
'Valid value' => [
81+
'Hannes',
82+
Exception::class,
83+
'ConstructorAfterValidationHook',
84+
],
85+
];
86+
}
87+
88+
public function testGetterHookIsResolved(): void
89+
{
90+
$this->addSchemaHook(new class () implements GetterHookInterface {
91+
public function getCode(PropertyInterface $property): string
92+
{
93+
return $property->getName() === 'age' ? 'throw new \Exception("GetterHook");' : '';
94+
}
95+
});
96+
97+
$className = $this->generateClassFromFile('BasicSchema.json');
98+
99+
$object = new $className(['name' => 'Albert', 'age' => 35]);
100+
101+
$this->assertSame('Albert', $object->getName());
102+
103+
$this->expectException(Exception::class);
104+
$this->expectExceptionMessage("GetterHook");
105+
106+
$object->getAge();
107+
}
108+
109+
public function testSetterBeforeValidationHookIsResolved(): void
110+
{
111+
$this->addSchemaHook(new class () implements SetterBeforeValidationHookInterface {
112+
public function getCode(PropertyInterface $property): string
113+
{
114+
return $property->getName() === 'age' ? 'throw new \Exception("SetterBeforeValidationHook");' : '';
115+
}
116+
});
117+
118+
$className = $this->generateClassFromFile(
119+
'BasicSchema.json',
120+
(new GeneratorConfiguration())->setImmutable(false)
121+
);
122+
123+
$object = new $className(['name' => 'Albert', 'age' => 35]);
124+
125+
$this->assertSame('Hannes', $object->setName('Hannes')->getName());
126+
127+
$this->expectException(Exception::class);
128+
$this->expectExceptionMessage("SetterBeforeValidationHook");
129+
130+
$object->setAge(-12);
131+
}
132+
133+
134+
/**
135+
* @dataProvider setterAfterValidationHookDataProvider
136+
*
137+
* @param $value
138+
* @param string $expectedException
139+
* @param string $expectedExceptionMessage
140+
*/
141+
public function testSetterAfterValidationHookIsResolved(
142+
$value,
143+
string $expectedException,
144+
string $expectedExceptionMessage
145+
): void {
146+
$this->addSchemaHook(new class () implements SetterAfterValidationHookInterface {
147+
public function getCode(PropertyInterface $property): string
148+
{
149+
return $property->getName() === 'age' ? 'throw new \Exception("SetterAfterValidationHook");' : '';
150+
}
151+
});
152+
153+
$className = $this->generateClassFromFile(
154+
'BasicSchema.json',
155+
(new GeneratorConfiguration())->setImmutable(false)->setCollectErrors(false)
156+
);
157+
158+
$object = new $className(['name' => 'Albert', 'age' => 35]);
159+
160+
$this->assertSame('Hannes', $object->setName('Hannes')->getName());
161+
162+
$this->expectException($expectedException);
163+
$this->expectExceptionMessage($expectedExceptionMessage);
164+
165+
$object->setAge($value);
166+
}
167+
168+
public function setterAfterValidationHookDataProvider(): array
169+
{
170+
return [
171+
'Invalid value' => [
172+
-12,
173+
MinimumException::class,
174+
'Value for age must not be smaller than 0',
175+
],
176+
'Valid value' => [
177+
12,
178+
Exception::class,
179+
'SetterAfterValidationHook',
180+
],
181+
];
182+
}
183+
184+
protected function addSchemaHook(SchemaHookInterface $schemaHook): void
185+
{
186+
$this->modifyModelGenerator = function (ModelGenerator $modelGenerator) use ($schemaHook): void {
187+
$modelGenerator->addPostProcessor(new class ($schemaHook) implements PostProcessorInterface {
188+
private $schemaHook;
189+
190+
public function __construct(SchemaHookInterface $schemaHook)
191+
{
192+
$this->schemaHook = $schemaHook;
193+
}
194+
public function process(Schema $schema, GeneratorConfiguration $generatorConfiguration): void
195+
{
196+
$schema->addSchemaHook($this->schemaHook);
197+
}
198+
});
199+
};
200+
}
201+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"name": {
5+
"type": "string"
6+
},
7+
"age": {
8+
"type": "integer",
9+
"minimum": 0
10+
}
11+
}
12+
}

0 commit comments

Comments
 (0)