Skip to content

Commit 322fcd6

Browse files
authored
Allow referencing previously defined named types (#51)
Once a named type is defined, in order to reuse that same type it is needed to reference that type by the name (as in the primitive types). Add support for NamedTypes in both the schema builder and the schema generator, which is a way of referencing previously defined named types. Add support for adding extra attributes for records in field's definition.
1 parent 89504da commit 322fcd6

File tree

6 files changed

+141
-69
lines changed

6 files changed

+141
-69
lines changed

src/Objects/Schema.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use FlixTech\AvroSerializer\Objects\Schema\LocalTimestampMillisType;
2020
use FlixTech\AvroSerializer\Objects\Schema\LongType;
2121
use FlixTech\AvroSerializer\Objects\Schema\MapType;
22+
use FlixTech\AvroSerializer\Objects\Schema\NamedType;
2223
use FlixTech\AvroSerializer\Objects\Schema\NullType;
2324
use FlixTech\AvroSerializer\Objects\Schema\RecordType;
2425
use FlixTech\AvroSerializer\Objects\Schema\StringType;
@@ -71,6 +72,11 @@ public static function string(): StringType
7172
return new StringType();
7273
}
7374

75+
public static function named(string $name): NamedType
76+
{
77+
return new NamedType($name);
78+
}
79+
7480
public static function record(): RecordType
7581
{
7682
return new RecordType();

src/Objects/Schema/Generation/SchemaGenerator.php

Lines changed: 16 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,15 @@ class SchemaGenerator
1818
*/
1919
private $reader;
2020

21+
/**
22+
* @var TypeMapper
23+
*/
24+
private $typeMapper;
25+
2126
public function __construct(SchemaAttributeReader $reader)
2227
{
2328
$this->reader = $reader;
29+
$this->typeMapper = new TypeMapper($this);
2430
}
2531

2632
/**
@@ -39,7 +45,7 @@ public function generate(string $className): Schema
3945
*/
4046
private function generateFromClass(ReflectionClass $class, Type $type): Schema
4147
{
42-
$schema = $this->schemaFromTypes($type);
48+
$schema = $this->schemaFromType($type);
4349

4450
if (!$schema instanceof Schema\RecordType) {
4551
return $schema;
@@ -56,75 +62,20 @@ private function generateFromClass(ReflectionClass $class, Type $type): Schema
5662
private function schemaFromTypes(Type ...$types): Schema
5763
{
5864
if (\count($types) > 1) {
59-
$unionSchemas = \array_map(function (Type $type) {
60-
return $this->schemaFromTypes($type);
61-
}, $types);
65+
$unionSchemas = \array_map([$this, 'schemaFromType'], $types);
6266

6367
return Schema::union(...$unionSchemas);
6468
}
6569

66-
$type = $types[0];
67-
$attributes = $type->getAttributes();
68-
69-
switch ($type->getTypeName()) {
70-
case TypeName::RECORD:
71-
if ($attributes->has(AttributeName::TARGET_CLASS)) {
72-
return $this->generate($attributes->get(AttributeName::TARGET_CLASS));
73-
}
74-
$schema = Schema::record();
75-
76-
return $this->applyAttributes($schema, $attributes);
77-
case TypeName::NULL:
78-
$schema = Schema::null();
79-
80-
return $this->applyAttributes($schema, $attributes);
81-
case TypeName::BOOLEAN:
82-
$schema = Schema::boolean();
83-
84-
return $this->applyAttributes($schema, $attributes);
85-
case TypeName::INT:
86-
$schema = Schema::int();
87-
88-
return $this->applyAttributes($schema, $attributes);
89-
case TypeName::LONG:
90-
$schema = Schema::long();
91-
92-
return $this->applyAttributes($schema, $attributes);
93-
case TypeName::FLOAT:
94-
$schema = Schema::float();
95-
96-
return $this->applyAttributes($schema, $attributes);
97-
case TypeName::DOUBLE:
98-
$schema = Schema::double();
99-
100-
return $this->applyAttributes($schema, $attributes);
101-
case TypeName::BYTES:
102-
$schema = Schema::bytes();
103-
104-
return $this->applyAttributes($schema, $attributes);
105-
case TypeName::STRING:
106-
$schema = Schema::string();
107-
108-
return $this->applyAttributes($schema, $attributes);
109-
case TypeName::ARRAY:
110-
$schema = Schema::array();
111-
112-
return $this->applyAttributes($schema, $attributes);
113-
case TypeName::MAP:
114-
$schema = Schema::map();
115-
116-
return $this->applyAttributes($schema, $attributes);
117-
case TypeName::ENUM:
118-
$schema = Schema::enum();
119-
120-
return $this->applyAttributes($schema, $attributes);
121-
case TypeName::FIXED:
122-
$schema = Schema::fixed();
70+
return $this->schemaFromType($types[0]);
71+
}
12372

124-
return $this->applyAttributes($schema, $attributes);
125-
default:
126-
throw new \InvalidArgumentException('$type is not a valid avro type');
127-
}
73+
private function schemaFromType(Type $type): Schema
74+
{
75+
return $this->applyAttributes(
76+
$this->typeMapper->toSchema($type),
77+
$type->getAttributes()
78+
);
12879
}
12980

13081
private function parseField(ReflectionProperty $property, Schema\RecordType $rootSchema): Schema
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace FlixTech\AvroSerializer\Objects\Schema\Generation;
6+
7+
use FlixTech\AvroSerializer\Objects\Schema;
8+
use FlixTech\AvroSerializer\Objects\Schema\AttributeName;
9+
use FlixTech\AvroSerializer\Objects\Schema\TypeName;
10+
11+
final class TypeMapper
12+
{
13+
/**
14+
* @var array<string, callable>
15+
*/
16+
private $mappers;
17+
18+
public function __construct(SchemaGenerator $generator)
19+
{
20+
$this->mappers = [
21+
TypeName::RECORD => $this->recordType($generator),
22+
TypeName::NULL => $this->simpleType(Schema::null()),
23+
TypeName::BOOLEAN => $this->simpleType(Schema::boolean()),
24+
TypeName::INT => $this->simpleType(Schema::int()),
25+
TypeName::LONG => $this->simpleType(Schema::long()),
26+
TypeName::FLOAT => $this->simpleType(Schema::float()),
27+
TypeName::DOUBLE => $this->simpleType(Schema::double()),
28+
TypeName::BYTES => $this->simpleType(Schema::bytes()),
29+
TypeName::STRING => $this->simpleType(Schema::string()),
30+
TypeName::ARRAY => $this->simpleType(Schema::array()),
31+
TypeName::MAP => $this->simpleType(Schema::map()),
32+
TypeName::ENUM => $this->simpleType(Schema::enum()),
33+
TypeName::FIXED => $this->simpleType(Schema::fixed()),
34+
];
35+
}
36+
37+
public function toSchema(Type $type): Schema
38+
{
39+
$mapper = $this->mappers[$type->getTypeName()] ?? $this->namedType();
40+
41+
return $mapper($type);
42+
}
43+
44+
private function simpleType(Schema $schema): callable
45+
{
46+
return function () use ($schema): Schema {
47+
return $schema;
48+
};
49+
}
50+
51+
private function recordType(SchemaGenerator $generator): callable
52+
{
53+
return function (Type $type) use ($generator): Schema {
54+
$attributes = $type->getAttributes();
55+
56+
if ($attributes->has(AttributeName::TARGET_CLASS)) {
57+
return $generator->generate($attributes->get(AttributeName::TARGET_CLASS));
58+
}
59+
60+
return Schema::record();
61+
};
62+
}
63+
64+
private function namedType(): callable
65+
{
66+
return function (Type $type): Schema {
67+
return Schema::named($type->getTypeName());
68+
};
69+
}
70+
}

src/Objects/Schema/NamedType.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace FlixTech\AvroSerializer\Objects\Schema;
6+
7+
use FlixTech\AvroSerializer\Objects\Schema;
8+
9+
class NamedType extends Schema
10+
{
11+
/**
12+
* @var string
13+
*/
14+
private $name;
15+
16+
public function __construct(string $name)
17+
{
18+
$this->name = $name;
19+
}
20+
21+
public function serialize(): string
22+
{
23+
return $this->name;
24+
}
25+
}

test/Objects/Schema/Generation/Fixture/RecordWithRecordType.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,18 @@
1313
class RecordWithRecordType
1414
{
1515
/**
16-
* @SerDe\AvroName("simple")
16+
* @SerDe\AvroName("simpleField")
1717
* @SerDe\AvroType("record", attributes={
18-
* @SerDe\AvroTargetClass("\FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture\SimpleRecord")
18+
* @SerDe\AvroTargetClass("\FlixTech\AvroSerializer\Test\Objects\Schema\Generation\Fixture\SimpleRecord"),
19+
* @SerDe\AvroDoc("This a simple record for testing purposes")
1920
* })
2021
*/
2122
private $simpleRecord;
23+
24+
/**
25+
* @SerDe\AvroName("unionField")
26+
* @SerDe\AvroType("null")
27+
* @SerDe\AvroType("org.acme.SimpleRecord")
28+
*/
29+
private $unionRecord;
2230
}

test/Objects/Schema/Generation/SchemaGeneratorTest.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,23 @@ public function it_should_generate_records_containing_records()
151151
$expected = Schema::record()
152152
->name('RecordWithRecordType')
153153
->field(
154-
'simple',
154+
'simpleField',
155155
Schema::record()
156156
->name('SimpleRecord')
157157
->namespace('org.acme')
158-
->field('intType', Schema::int(), Schema\Record\FieldOption::default(42))
158+
->doc('This a simple record for testing purposes')
159+
->field(
160+
'intType',
161+
Schema::int(),
162+
Schema\Record\FieldOption::default(42)
163+
),
164+
)
165+
->field(
166+
'unionField',
167+
Schema::union(
168+
Schema::null(),
169+
Schema::named('org.acme.SimpleRecord')
170+
)
159171
);
160172

161173
$this->assertEquals($expected, $schema);

0 commit comments

Comments
 (0)