Skip to content

Commit 4bfeb36

Browse files
xico42tPl0ch
andauthored
Improve schema generation with annotations (#42)
* Add support for maps with complex values * Add support for arrays with complex items Fixes #40 Co-authored-by: Thomas Ploch <[email protected]>
1 parent 9a87c35 commit 4bfeb36

File tree

14 files changed

+288
-81
lines changed

14 files changed

+288
-81
lines changed

src/Objects/Schema/Generation/Annotations/AvroAliases.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
namespace FlixTech\AvroSerializer\Objects\Schema\Generation\Annotations;
66

77
use FlixTech\AvroSerializer\Objects\Schema\AttributeName;
8-
use FlixTech\AvroSerializer\Objects\Schema\Generation\SchemaAttribute;
98
use FlixTech\AvroSerializer\Objects\Schema\Generation\SchemaAttributes;
9+
use FlixTech\AvroSerializer\Objects\Schema\Generation\VariadicAttribute;
1010

1111
/**
1212
* @Annotation
1313
*/
14-
final class AvroAliases implements SchemaAttribute
14+
final class AvroAliases implements VariadicAttribute
1515
{
1616
/**
1717
* @var array<string>

src/Objects/Schema/Generation/Annotations/AvroItems.php

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,14 @@
55
namespace FlixTech\AvroSerializer\Objects\Schema\Generation\Annotations;
66

77
use FlixTech\AvroSerializer\Objects\Schema\AttributeName;
8-
use FlixTech\AvroSerializer\Objects\Schema\Generation\SchemaAttribute;
9-
use FlixTech\AvroSerializer\Objects\Schema\Generation\SchemaAttributes;
8+
use FlixTech\AvroSerializer\Objects\Schema\Generation\TypeOnlyAttribute;
109

1110
/**
1211
* @Annotation
1312
*/
14-
final class AvroItems implements SchemaAttribute
13+
final class AvroItems implements TypeOnlyAttribute
1514
{
16-
/**
17-
* @var string
18-
*/
19-
public $value;
15+
use ContainsOnlyTypes;
2016

2117
/**
2218
* {@inheritdoc}
@@ -25,20 +21,4 @@ public function name(): string
2521
{
2622
return AttributeName::ITEMS;
2723
}
28-
29-
/**
30-
* {@inheritdoc}
31-
*/
32-
public function value(): string
33-
{
34-
return $this->value;
35-
}
36-
37-
/**
38-
* {@inheritdoc}
39-
*/
40-
public function attributes(): SchemaAttributes
41-
{
42-
return new SchemaAttributes();
43-
}
4424
}

src/Objects/Schema/Generation/Annotations/AvroSymbols.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
namespace FlixTech\AvroSerializer\Objects\Schema\Generation\Annotations;
66

77
use FlixTech\AvroSerializer\Objects\Schema\AttributeName;
8-
use FlixTech\AvroSerializer\Objects\Schema\Generation\SchemaAttribute;
98
use FlixTech\AvroSerializer\Objects\Schema\Generation\SchemaAttributes;
9+
use FlixTech\AvroSerializer\Objects\Schema\Generation\VariadicAttribute;
1010

1111
/**
1212
* @Annotation
1313
*/
14-
final class AvroSymbols implements SchemaAttribute
14+
final class AvroSymbols implements VariadicAttribute
1515
{
1616
/**
1717
* @var array<string>

src/Objects/Schema/Generation/Annotations/AvroType.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ final class AvroType implements SchemaAttribute
2323
*/
2424
public $attributes = [];
2525

26+
public static function create(string $typeName, SchemaAttribute ...$attributes): self
27+
{
28+
$avroType = new self();
29+
30+
$avroType->value = $typeName;
31+
$avroType->attributes = $attributes;
32+
33+
return $avroType;
34+
}
35+
2636
/**
2737
* {@inheritdoc}
2838
*/

src/Objects/Schema/Generation/Annotations/AvroValues.php

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,14 @@
55
namespace FlixTech\AvroSerializer\Objects\Schema\Generation\Annotations;
66

77
use FlixTech\AvroSerializer\Objects\Schema\AttributeName;
8-
use FlixTech\AvroSerializer\Objects\Schema\Generation\SchemaAttribute;
9-
use FlixTech\AvroSerializer\Objects\Schema\Generation\SchemaAttributes;
8+
use FlixTech\AvroSerializer\Objects\Schema\Generation\TypeOnlyAttribute;
109

1110
/**
1211
* @Annotation
1312
*/
14-
final class AvroValues implements SchemaAttribute
13+
final class AvroValues implements TypeOnlyAttribute
1514
{
16-
/**
17-
* @var string
18-
*/
19-
public $value;
15+
use ContainsOnlyTypes;
2016

2117
/**
2218
* {@inheritdoc}
@@ -25,20 +21,4 @@ public function name(): string
2521
{
2622
return AttributeName::VALUES;
2723
}
28-
29-
/**
30-
* {@inheritdoc}
31-
*/
32-
public function value(): string
33-
{
34-
return $this->value;
35-
}
36-
37-
/**
38-
* {@inheritdoc}
39-
*/
40-
public function attributes(): SchemaAttributes
41-
{
42-
return new SchemaAttributes();
43-
}
4424
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace FlixTech\AvroSerializer\Objects\Schema\Generation\Annotations;
6+
7+
use FlixTech\AvroSerializer\Objects\Schema\Generation\SchemaAttribute;
8+
use FlixTech\AvroSerializer\Objects\Schema\Generation\SchemaAttributes;
9+
10+
trait ContainsOnlyTypes
11+
{
12+
/**
13+
* @phpstan-var string|AvroType|array<string|AvroType>
14+
*/
15+
public $value;
16+
17+
/**
18+
* {@inheritdoc}
19+
*
20+
* @return array<SchemaAttribute>
21+
*/
22+
public function value(): array
23+
{
24+
if (!\is_array($this->value)) {
25+
return [$this->valueToType($this->value)];
26+
}
27+
28+
return \array_map([$this, 'valueToType'], $this->value);
29+
}
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
public function attributes(): SchemaAttributes
35+
{
36+
return new SchemaAttributes(...$this->value());
37+
}
38+
39+
/**
40+
* @param string|AvroType $value
41+
*/
42+
private function valueToType($value): AvroType
43+
{
44+
if ($value instanceof AvroType) {
45+
return $value;
46+
}
47+
48+
return AvroType::create($value);
49+
}
50+
}

src/Objects/Schema/Generation/SchemaAttributes.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,16 @@ public function __construct(SchemaAttribute ...$attributes)
2626
}
2727

2828
/**
29-
* @return array<SchemaAttribute>
29+
* @return array<Type>
3030
*/
3131
public function types(): array
3232
{
33-
return $this->attributes[AttributeName::TYPE] ?? [];
33+
return \array_map(function (SchemaAttribute $schemaAttribute) {
34+
return new Type(
35+
$schemaAttribute->value(),
36+
$schemaAttribute->attributes()
37+
);
38+
}, $this->attributes[AttributeName::TYPE] ?? []);
3439
}
3540

3641
/**

src/Objects/Schema/Generation/SchemaGenerator.php

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,15 @@ public function generate(string $className): Schema
3131
$class = new ReflectionClass($className);
3232
$attributes = $this->reader->readClassAttributes($class);
3333

34-
return $this->generateFromClass($class, $attributes);
34+
return $this->generateFromClass($class, new Type(TypeName::RECORD, $attributes));
3535
}
3636

3737
/**
3838
* @param ReflectionClass<object> $class
3939
*/
40-
private function generateFromClass(ReflectionClass $class, SchemaAttributes $attributes): Schema
40+
private function generateFromClass(ReflectionClass $class, Type $type): Schema
4141
{
42-
$type = $attributes->types()[0];
43-
$schema = $this->schemaFromType($type->value(), $attributes);
42+
$schema = $this->schemaFromTypes($type);
4443

4544
if (!$schema instanceof Schema\RecordType) {
4645
return $schema;
@@ -54,9 +53,20 @@ private function generateFromClass(ReflectionClass $class, SchemaAttributes $att
5453
return $schema;
5554
}
5655

57-
private function schemaFromType(string $type, SchemaAttributes $attributes): Schema
56+
private function schemaFromTypes(Type ...$types): Schema
5857
{
59-
switch ($type) {
58+
if (\count($types) > 1) {
59+
$unionSchemas = \array_map(function (Type $type) {
60+
return $this->schemaFromTypes($type);
61+
}, $types);
62+
63+
return Schema::union(...$unionSchemas);
64+
}
65+
66+
$type = $types[0];
67+
$attributes = $type->getAttributes();
68+
69+
switch ($type->getTypeName()) {
6070
case TypeName::RECORD:
6171
if ($attributes->has(AttributeName::TARGET_CLASS)) {
6272
return $this->generate($attributes->get(AttributeName::TARGET_CLASS));
@@ -125,16 +135,7 @@ private function parseField(ReflectionProperty $property, Schema\RecordType $roo
125135
return $rootSchema;
126136
}
127137

128-
$mainType = $attributes->types()[0];
129-
$fieldSchema = $this->schemaFromType($mainType->value(), $mainType->attributes());
130-
131-
if (1 < \count($attributes->types())) {
132-
$unionSchemas = \array_map(function (SchemaAttribute $type) {
133-
return $this->schemaFromType($type->value(), $type->attributes());
134-
}, $attributes->types());
135-
136-
$fieldSchema = Schema::union(...$unionSchemas);
137-
}
138+
$fieldSchema = $this->schemaFromTypes(...$attributes->types());
138139

139140
$fieldArgs = [
140141
$attributes->has(AttributeName::NAME) ? $attributes->get(AttributeName::NAME) : $property->getName(),
@@ -165,25 +166,16 @@ private function parseField(ReflectionProperty $property, Schema\RecordType $roo
165166

166167
private function applyAttributes(Schema $schema, SchemaAttributes $attributes): Schema
167168
{
168-
$variadicValues = [
169-
AttributeName::SYMBOLS,
170-
AttributeName::ALIASES,
171-
];
172-
173-
$schemaValues = [
174-
AttributeName::ITEMS,
175-
AttributeName::VALUES,
176-
];
177-
178169
foreach ($attributes->options() as $attribute) {
179-
if (\in_array($attribute->name(), $variadicValues) && \is_array($attribute->value())) {
170+
if ($attribute instanceof VariadicAttribute) {
180171
$schema = $schema->{$attribute->name()}(...$attribute->value());
181172

182173
continue;
183174
}
184175

185-
if (\in_array($attribute->name(), $schemaValues) && \is_string($attribute->value())) {
186-
$schema = $schema->{$attribute->name()}($this->schemaFromType($attribute->value(), new SchemaAttributes()));
176+
if ($attribute instanceof TypeOnlyAttribute) {
177+
$types = $attribute->attributes()->types();
178+
$schema = $schema->{$attribute->name()}($this->schemaFromTypes(...$types));
187179

188180
continue;
189181
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace FlixTech\AvroSerializer\Objects\Schema\Generation;
6+
7+
class Type
8+
{
9+
/**
10+
* @var string
11+
*/
12+
private $typeName;
13+
14+
/**
15+
* @var SchemaAttributes
16+
*/
17+
private $attributes;
18+
19+
public function __construct(string $typeName, SchemaAttributes $attributes = null)
20+
{
21+
$this->typeName = $typeName;
22+
$this->attributes = $attributes ?? new SchemaAttributes();
23+
}
24+
25+
public function getTypeName(): string
26+
{
27+
return $this->typeName;
28+
}
29+
30+
public function getAttributes(): SchemaAttributes
31+
{
32+
return $this->attributes;
33+
}
34+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace FlixTech\AvroSerializer\Objects\Schema\Generation;
6+
7+
/**
8+
* Marker interface to indicate that the attribute contains only type attributes
9+
*/
10+
interface TypeOnlyAttribute extends SchemaAttribute
11+
{
12+
}

0 commit comments

Comments
 (0)