Skip to content

Commit 7da2132

Browse files
authored
fix: self-referencing relations (#390)
Self-referencing relations needs to use another name for the inverse join column than the default one.
1 parent 09324b9 commit 7da2132

File tree

12 files changed

+44
-19
lines changed

12 files changed

+44
-19
lines changed

src/AttributeGenerator/DoctrineMongoDBAttributeGenerator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use ApiPlatform\SchemaGenerator\Model\Property;
2020
use ApiPlatform\SchemaGenerator\Model\Use_;
2121
use Nette\PhpGenerator\Literal;
22+
2223
use function Symfony\Component\String\u;
2324

2425
/**

src/AttributeGenerator/DoctrineOrmAttributeGenerator.php

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use ApiPlatform\SchemaGenerator\Model\Property;
2020
use ApiPlatform\SchemaGenerator\Model\Use_;
2121
use Nette\PhpGenerator\Literal;
22+
2223
use function Symfony\Component\String\u;
2324

2425
/**
@@ -125,8 +126,7 @@ public function generatePropertyAttributes(Property $property, string $className
125126
case 'bool':
126127
$type = 'boolean';
127128
break;
128-
// TODO: use more precise types for int (smallint, bigint...)
129-
case 'int':
129+
case 'int': // TODO: use more precise types for int (smallint, bigint...)
130130
$type = 'integer';
131131
break;
132132
case 'string':
@@ -215,7 +215,12 @@ public function generatePropertyAttributes(Property $property, string $className
215215
$attributes[] = new Attribute('ORM\ManyToMany', ['targetEntity' => $relationName]);
216216
}
217217
$attributes[] = new Attribute('ORM\JoinTable', ['name' => $relationTableName]);
218-
$attributes[] = new Attribute('ORM\InverseJoinColumn', ['unique' => true]);
218+
// Self-referencing relation
219+
if ($className === $property->reference->name()) {
220+
$attributes[] = new Attribute('ORM\InverseJoinColumn', ['name' => $this->generateIdentifierName($this->inflector->singularize($property->name())[0].ucfirst($property->reference->name()).'Id', 'inverse_join_column', $this->config)]);
221+
} else {
222+
$attributes[] = new Attribute('ORM\InverseJoinColumn', ['unique' => true]);
223+
}
219224
break;
220225
case CardinalitiesExtractor::CARDINALITY_1_N:
221226
if (null !== $property->mappedBy) {
@@ -224,7 +229,12 @@ public function generatePropertyAttributes(Property $property, string $className
224229
$attributes[] = new Attribute('ORM\ManyToMany', ['targetEntity' => $relationName]);
225230
}
226231
$attributes[] = new Attribute('ORM\JoinTable', ['name' => $relationTableName]);
227-
$attributes[] = new Attribute('ORM\InverseJoinColumn', ['nullable' => false, 'unique' => true]);
232+
// Self-referencing relation
233+
if ($className === $property->reference->name()) {
234+
$attributes[] = new Attribute('ORM\InverseJoinColumn', ['name' => $this->generateIdentifierName($this->inflector->singularize($property->name())[0].ucfirst($property->reference->name()).'Id', 'inverse_join_column', $this->config), 'nullable' => false]);
235+
} else {
236+
$attributes[] = new Attribute('ORM\InverseJoinColumn', ['nullable' => false, 'unique' => true]);
237+
}
228238
break;
229239
case CardinalitiesExtractor::CARDINALITY_N_N:
230240
$attributes[] = new Attribute('ORM\ManyToMany', ['targetEntity' => $relationName]);
@@ -256,13 +266,13 @@ private function generateIdAttributes(): array
256266
switch ($this->config['id']['generationStrategy']) {
257267
case 'uuid':
258268
$type = 'guid';
259-
break;
269+
break;
260270
case 'auto':
261271
$type = 'integer';
262-
break;
272+
break;
263273
default:
264274
$type = 'string';
265-
break;
275+
break;
266276
}
267277

268278
$attributes[] = new Attribute('ORM\Column', ['type' => $type]);

src/OpenApi/ClassGenerator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
use Doctrine\Common\Collections\Collection;
3535
use Psr\Log\LoggerAwareTrait;
3636
use Symfony\Component\String\Inflector\InflectorInterface;
37+
3738
use function Symfony\Component\String\u;
3839

3940
final class ClassGenerator

src/OpenApi/ClassMutator/EnumClassMutator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use ApiPlatform\SchemaGenerator\Model\Class_;
1818
use ApiPlatform\SchemaGenerator\OpenApi\Model\Constant as OpenApiConstant;
1919
use ApiPlatform\SchemaGenerator\PhpTypeConverterInterface;
20+
2021
use function Symfony\Component\String\u;
2122

2223
final class EnumClassMutator extends BaseEnumClassMutator

src/OpenApi/PropertyGenerator/PropertyGenerator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use ApiPlatform\SchemaGenerator\PropertyGenerator\PropertyGenerator as CommonPropertyGenerator;
2020
use ApiPlatform\SchemaGenerator\PropertyGenerator\PropertyGeneratorInterface;
2121
use cebe\openapi\spec\Schema;
22+
2223
use function Symfony\Component\String\u;
2324

2425
final class PropertyGenerator implements PropertyGeneratorInterface

tests/AttributeGenerator/ApiPlatformCoreAttributeGeneratorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public function provideGenerateClassAttributesCases(): \Generator
8787
$class = new SchemaClass('Enum', $resource);
8888
yield 'enum' => [$class, []];
8989

90-
yield 'with short name' => [(new SchemaClass('WithShortName', new RdfResource('https://schema.org/DifferentLocalName', new RdfGraph()))), [new Attribute('ApiResource', ['shortName' => 'DifferentLocalName', 'types' => ['https://schema.org/DifferentLocalName']])]];
90+
yield 'with short name' => [new SchemaClass('WithShortName', new RdfResource('https://schema.org/DifferentLocalName', new RdfGraph())), [new Attribute('ApiResource', ['shortName' => 'DifferentLocalName', 'types' => ['https://schema.org/DifferentLocalName']])]];
9191
}
9292

9393
/**

tests/AttributeGenerator/DoctrineOrmAttributeGeneratorTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ protected function setUp(): void
130130
$relation1NProperty->cardinality = CardinalitiesExtractor::CARDINALITY_1_N;
131131
$relation1NProperty->isArray = true;
132132
$vehicle->addProperty($relation1NProperty);
133+
$relation1NSelfReferencingProperty = new Property('relation1_N_self_referencing');
134+
$relation1NSelfReferencingProperty->rangeName = 'Vehicle';
135+
$relation1NSelfReferencingProperty->range = new RdfResource('https://schema.org/Vehicle');
136+
$relation1NSelfReferencingProperty->reference = new SchemaClass('Vehicle', new RdfResource('htts://schema.org/Vehicle', $graph));
137+
$relation1NSelfReferencingProperty->cardinality = CardinalitiesExtractor::CARDINALITY_1_N;
138+
$relation1NSelfReferencingProperty->isArray = true;
139+
$vehicle->addProperty($relation1NSelfReferencingProperty);
133140
$relationNNProperty = new Property('relationN_N');
134141
$relationNNProperty->rangeName = 'QuantitativeValue';
135142
$relationNNProperty->range = new RdfResource('https://schema.org/QuantitativeValue');
@@ -230,6 +237,10 @@ public function testGeneratePropertyAttributes(): void
230237
[new Attribute('ORM\ManyToMany', ['targetEntity' => 'App\Entity\QuantitativeValue']), new Attribute('ORM\JoinTable', ['name' => 'vehicle_quantitative_value_relation1_n']), new Attribute('ORM\InverseJoinColumn', ['nullable' => false, 'unique' => true])],
231238
$this->generator->generatePropertyAttributes($this->classMap['Vehicle']->getPropertyByName('relation1_N'), 'Vehicle')
232239
);
240+
$this->assertEquals(
241+
[new Attribute('ORM\ManyToMany', ['targetEntity' => 'App\Entity\Vehicle']), new Attribute('ORM\JoinTable', ['name' => 'vehicle_vehicle_relation1_n_self_referencing']), new Attribute('ORM\InverseJoinColumn', ['name' => 'relation1_n_self_referencing_vehicle_id', 'nullable' => false])],
242+
$this->generator->generatePropertyAttributes($this->classMap['Vehicle']->getPropertyByName('relation1_N_self_referencing'), 'Vehicle')
243+
);
233244
$this->assertEquals(
234245
[new Attribute('ORM\ManyToMany', ['targetEntity' => 'App\Entity\QuantitativeValue']), new Attribute('ORM\JoinTable', ['name' => 'vehicle_quantitative_value_relation_nn'])],
235246
$this->generator->generatePropertyAttributes($this->classMap['Vehicle']->getPropertyByName('relationN_N'), 'Vehicle')

tests/Command/DumpConfigurationTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ interface: null
273273

274274

275275
YAML
276-
,
276+
,
277277
$commandTester->getDisplay()
278278
);
279279
}

tests/Command/ExtractCardinalitiesCommandTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1476,6 +1476,6 @@ public function testExtractCardinalities(): void
14761476
}
14771477

14781478
JSON
1479-
, $commandTester->getDisplay());
1479+
, $commandTester->getDisplay());
14801480
}
14811481
}

tests/Command/GenerateCommandTest.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public function getFriends(): Collection
7272
return $this->friends;
7373
}
7474
PHP
75-
, $person);
75+
, $person);
7676
}
7777

7878
public function testCustomAttributes(): void
@@ -106,17 +106,17 @@ public function testCustomAttributes(): void
106106
class Book
107107
{
108108
PHP
109-
, $book);
109+
, $book);
110110

111111
// Attributes given as unordered map.
112112
$this->assertStringContainsString(<<<'PHP'
113113
#[ORM\OneToMany(targetEntity: 'App\Entity\Review', mappedBy: 'book', cascade: ['persist', 'remove'])]
114114
PHP
115-
, $book);
115+
, $book);
116116
$this->assertStringContainsString(<<<'PHP'
117117
#[ORM\OrderBy(name: 'ASC')]
118118
PHP
119-
, $book);
119+
, $book);
120120
}
121121

122122
public function testFluentMutators(): void
@@ -136,7 +136,7 @@ public function setUrl(?string $url): self
136136
return $this;
137137
}
138138
PHP
139-
, $person);
139+
, $person);
140140

141141
$this->assertStringContainsString(<<<'PHP'
142142
public function addFriend(Person $friend): self
@@ -255,7 +255,7 @@ public function getId(): ?int
255255
return $this->id;
256256
}
257257
PHP
258-
, $person);
258+
, $person);
259259

260260
$this->assertStringNotContainsString('setId(', $person);
261261
}
@@ -363,7 +363,7 @@ public function setId(string $id): void
363363
}
364364

365365
PHP
366-
, $person);
366+
, $person);
367367
}
368368

369369
public function testDoNotGenerateId(): void

0 commit comments

Comments
 (0)