Skip to content

Commit a16ac6c

Browse files
committed
Merge #26 remote-tracking branch 'php-openapi/25-generate-inverse-relations'
* php-openapi/25-generate-inverse-relations: Complete the test Fix other failing tests Fix test - GeneratorTest and more Fix failing test - RelationsInFakerTest::testIndex Refactor Avoid inverse relation for self reference Handle multiple relations and add more tests Implement Fix style Fix failing tests Refactor again for more code readability Refactor Fix style Cleanup 2 Cleanup and add test OpenAPI spec Cleanup Initial commit to create PR
2 parents 92e2b97 + 1a7302e commit a16ac6c

File tree

48 files changed

+1036
-198
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1036
-198
lines changed

src/generator/default/dbmodel.php

+9-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public function rules()
128128
public function get<?= $relation->getCamelName() ?>()
129129
{
130130
return $this-><?= $relation->getMethod() ?>(\<?= trim($relationNamespace, '\\') ?>\<?= $relation->getClassName() ?>::class, <?php
131-
echo $relation->linkToString()?>);
131+
echo $relation->linkToString()?>)<?= $relation->getInverse() ? '->inverseOf(\''.$relation->getInverse().'\')' : '' ?>;
132132
}
133133
<?php endforeach; ?>
134134
<?php foreach ($model->many2many as $relation): ?>
@@ -144,4 +144,12 @@ public function get<?= $relation->getCamelName() ?>()
144144
<?php endif;?>
145145
}
146146
<?php endforeach; ?>
147+
<?php $i = 1; foreach ($model->inverseRelations as $relationName => $relation): ?>
148+
149+
public function get<?= $relation->getCamelName().($i===1 ? '' : $i) ?>()
150+
{
151+
return $this-><?= $relation->getMethod() ?>(\<?= trim($relationNamespace, '\\') ?>\<?= $relation->getClassName() ?>::class, <?php
152+
echo $relation->linkToString() ?>)->inverseOf('<?= $relation->getInverse() ?>');
153+
}
154+
<?php $i++; endforeach; ?>
147155
}

src/lib/AttributeResolver.php

+94-79
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use cebe\yii2openapi\lib\openapi\ComponentSchema;
1919
use cebe\yii2openapi\lib\openapi\PropertySchema;
2020
use Yii;
21+
use yii\base\InvalidConfigException;
2122
use yii\helpers\Inflector;
2223
use yii\helpers\StringHelper;
2324
use function explode;
@@ -29,49 +30,40 @@ class AttributeResolver
2930
/**
3031
* @var Attribute[]|array
3132
*/
32-
private $attributes = [];
33+
private array $attributes = [];
3334

3435
/**
3536
* @var AttributeRelation[]|array
3637
*/
37-
private $relations = [];
38+
public array $relations = [];
39+
3840
/**
3941
* @var NonDbRelation[]|array
4042
*/
41-
private $nonDbRelations = [];
43+
private array $nonDbRelations = [];
4244
/**
4345
* @var ManyToManyRelation[]|array
4446
*/
45-
private $many2many = [];
47+
private array $many2many = [];
4648

47-
/**
48-
* @var string
49-
*/
50-
private $schemaName;
49+
private string $schemaName;
5150

52-
/**
53-
* @var string
54-
*/
55-
private $tableName;
51+
private string $tableName;
5652

57-
/**
58-
* @var ComponentSchema
59-
*/
60-
private $componentSchema;
53+
private ComponentSchema $componentSchema;
6154

62-
/**
63-
* @var \cebe\yii2openapi\lib\items\JunctionSchemas
64-
*/
65-
private $junctions;
55+
private JunctionSchemas $junctions;
6656

67-
/** @var bool */
68-
private $isJunctionSchema;
57+
private bool $isJunctionSchema;
6958

70-
/** @var bool */
71-
private $hasMany2Many;
59+
private bool $hasMany2Many;
7260

73-
/** @var Config */
74-
private $config;
61+
private ?Config $config;
62+
63+
/**
64+
* @var AttributeRelation[]|array
65+
*/
66+
public array $inverseRelations = [];
7567

7668
public function __construct(string $schemaName, ComponentSchema $schema, JunctionSchemas $junctions, ?Config $config = null)
7769
{
@@ -85,14 +77,14 @@ public function __construct(string $schemaName, ComponentSchema $schema, Junctio
8577
}
8678

8779
/**
88-
* @return \cebe\yii2openapi\lib\items\DbModel
89-
* @throws \cebe\yii2openapi\lib\exceptions\InvalidDefinitionException
90-
* @throws \yii\base\InvalidConfigException
80+
* @return DbModel
81+
* @throws InvalidDefinitionException
82+
* @throws InvalidConfigException
9183
*/
92-
public function resolve():DbModel
84+
public function resolve(): DbModel
9385
{
9486
foreach ($this->componentSchema->getProperties() as $property) {
95-
/** @var $property \cebe\yii2openapi\lib\openapi\PropertySchema */
87+
/** @var $property PropertySchema */
9688

9789
$isRequired = $this->componentSchema->isRequiredProperty($property->getName());
9890
$nullableValue = $property->getProperty()->getSerializableData()->nullable ?? null;
@@ -108,6 +100,7 @@ public function resolve():DbModel
108100
$this->resolveProperty($property, $isRequired, $nullableValue);
109101
}
110102
}
103+
111104
return Yii::createObject(DbModel::class, [
112105
[
113106
/** @see \cebe\openapi\spec\Schema */
@@ -129,24 +122,24 @@ public function resolve():DbModel
129122
}
130123

131124
/**
132-
* @param \cebe\yii2openapi\lib\openapi\PropertySchema $property
133-
* @param bool $isRequired
134-
* @throws \cebe\yii2openapi\lib\exceptions\InvalidDefinitionException
135-
* @throws \yii\base\InvalidConfigException
125+
* @param PropertySchema $property
126+
* @param bool $isRequired
127+
* @throws InvalidDefinitionException
128+
* @throws InvalidConfigException
136129
*/
137-
protected function resolveJunctionTableProperty(PropertySchema $property, bool $isRequired):void
130+
protected function resolveJunctionTableProperty(PropertySchema $property, bool $isRequired): void
138131
{
139132
if ($this->junctions->isJunctionProperty($this->schemaName, $property->getName())) {
140133
$junkAttribute = $this->junctions->byJunctionSchema($this->schemaName)[$property->getName()];
141134
$attribute = Yii::createObject(Attribute::class, [$property->getName()]);
142135
$attribute->setRequired($isRequired)
143-
->setDescription($property->getAttr('description', ''))
144-
->setReadOnly($property->isReadonly())
145-
->setIsPrimary($property->isPrimaryKey())
146-
->asReference($junkAttribute['relatedClassName'])
147-
->setPhpType($junkAttribute['phpType'])
148-
->setDbType($junkAttribute['dbType'])
149-
->setForeignKeyColumnName($property->fkColName);
136+
->setDescription($property->getAttr('description', ''))
137+
->setReadOnly($property->isReadonly())
138+
->setIsPrimary($property->isPrimaryKey())
139+
->asReference($junkAttribute['relatedClassName'])
140+
->setPhpType($junkAttribute['phpType'])
141+
->setDbType($junkAttribute['dbType'])
142+
->setForeignKeyColumnName($property->fkColName);
150143
$relation = Yii::createObject(AttributeRelation::class, [
151144
$property->getName(),
152145
$junkAttribute['relatedTableName'],
@@ -161,12 +154,12 @@ protected function resolveJunctionTableProperty(PropertySchema $property, bool $
161154
}
162155

163156
/**
164-
* @param \cebe\yii2openapi\lib\openapi\PropertySchema $property
165-
* @param bool $isRequired
166-
* @throws \cebe\yii2openapi\lib\exceptions\InvalidDefinitionException
167-
* @throws \yii\base\InvalidConfigException
157+
* @param PropertySchema $property
158+
* @param bool $isRequired
159+
* @throws InvalidDefinitionException
160+
* @throws InvalidConfigException
168161
*/
169-
protected function resolveHasMany2ManyTableProperty(PropertySchema $property, bool $isRequired):void
162+
protected function resolveHasMany2ManyTableProperty(PropertySchema $property, bool $isRequired): void
170163
{
171164
if ($this->junctions->isManyToManyProperty($this->schemaName, $property->getName())) {
172165
return;
@@ -196,25 +189,25 @@ protected function resolveHasMany2ManyTableProperty(PropertySchema $property, bo
196189

197190
$this->relations[Inflector::pluralize($junkRef)] =
198191
Yii::createObject(AttributeRelation::class, [$junkRef, $junkAttribute['junctionTable'], $viaModel])
199-
->asHasMany([$junkAttribute['pairProperty'] . '_id' => $this->componentSchema->getPkName()]);
192+
->asHasMany([$junkAttribute['pairProperty'] . '_id' => $this->componentSchema->getPkName()]);
200193
return;
201194
}
202195

203196
$this->resolveProperty($property, $isRequired);
204197
}
205198

206199
/**
207-
* @param \cebe\yii2openapi\lib\openapi\PropertySchema $property
208-
* @param bool $isRequired
209-
* @param bool|null|string $nullableValue if string then its value will be only constant `ARG_ABSENT`. Default `null` is avoided because it can be in passed value in method call
210-
* @throws \cebe\yii2openapi\lib\exceptions\InvalidDefinitionException
211-
* @throws \yii\base\InvalidConfigException
200+
* @param PropertySchema $property
201+
* @param bool $isRequired
202+
* @param bool|null|string $nullableValue if string then its value will be only constant `ARG_ABSENT`. Default `null` is avoided because it can be in passed value in method call
203+
* @throws InvalidDefinitionException
204+
* @throws InvalidConfigException
212205
*/
213206
protected function resolveProperty(
214207
PropertySchema $property,
215208
bool $isRequired,
216209
$nullableValue = 'ARG_ABSENT'
217-
):void {
210+
): void {
218211
if ($nullableValue === 'ARG_ABSENT') {
219212
$nullableValue = $property->getProperty()->getSerializableData()->nullable ?? null;
220213
}
@@ -239,7 +232,7 @@ protected function resolveProperty(
239232
if ($property->isVirtual()) {
240233
throw new InvalidDefinitionException('References not supported for virtual attributes');
241234
}
242-
235+
243236
if ($property->isNonDbReference()) {
244237
$attribute->asNonDbReference($property->getRefClassName());
245238
$relation = Yii::createObject(
@@ -275,21 +268,24 @@ protected function resolveProperty(
275268
AttributeRelation::class,
276269
[static::relationName($property->getName(), $property->fkColName), $relatedTableName, $relatedClassName]
277270
)
278-
->asHasOne([$fkProperty->getName() => $attribute->columnName]);
271+
->asHasOne([$fkProperty->getName() => $attribute->columnName]);
279272
$relation->onUpdateFkConstraint = $property->onUpdateFkConstraint;
280273
$relation->onDeleteFkConstraint = $property->onDeleteFkConstraint;
281274
if ($property->isRefPointerToSelf()) {
282275
$relation->asSelfReference();
283276
}
284277
$this->relations[$property->getName()] = $relation;
278+
if (!$property->isRefPointerToSelf()) {
279+
$this->addInverseRelation($relatedClassName, $attribute, $property, $fkProperty);
280+
}
285281
}
286282
if (!$property->isReference() && !$property->hasRefItems()) {
287283
[$min, $max] = $property->guessMinMax();
288284
$attribute->setIsVirtual($property->isVirtual())
289-
->setPhpType($property->guessPhpType())
290-
->setDbType($property->guessDbType())
291-
->setSize($property->getMaxLength())
292-
->setLimits($min, $max, $property->getMinLength());
285+
->setPhpType($property->guessPhpType())
286+
->setDbType($property->guessDbType())
287+
->setSize($property->getMaxLength())
288+
->setLimits($min, $max, $property->getMinLength());
293289
if ($property->hasEnum()) {
294290
$attribute->setEnumValues($property->getAttr('enum'));
295291
}
@@ -326,7 +322,7 @@ protected function resolveProperty(
326322
AttributeRelation::class,
327323
[static::relationName($property->getName(), $property->fkColName), $relatedTableName, $relatedClassName]
328324
)
329-
->asHasMany([$fkProperty->getName() => $fkProperty->getName()])->asSelfReference();
325+
->asHasMany([$fkProperty->getName() => $fkProperty->getName()])->asSelfReference();
330326
return;
331327
}
332328
$foreignPk = Inflector::camel2id($fkProperty->getName(), '_') . '_id';
@@ -335,7 +331,7 @@ protected function resolveProperty(
335331
AttributeRelation::class,
336332
[static::relationName($property->getName(), $property->fkColName), $relatedTableName, $relatedClassName]
337333
)
338-
->asHasMany([$foreignPk => $this->componentSchema->getPkName()]);
334+
->asHasMany([$foreignPk => $this->componentSchema->getPkName()]);
339335
return;
340336
}
341337
$relatedClassName = $property->getRefClassName();
@@ -354,7 +350,8 @@ protected function resolveProperty(
354350
AttributeRelation::class,
355351
[static::relationName($property->getName(), $property->fkColName), $relatedTableName, $relatedClassName]
356352
)
357-
->asHasMany([Inflector::camel2id($this->schemaName, '_') . '_id' => $this->componentSchema->getPkName()]);
353+
->asHasMany([Inflector::camel2id($this->schemaName, '_') . '_id' => $this->componentSchema->getPkName()])
354+
->setInverse(Inflector::variablize($this->schemaName));
358355
return;
359356
}
360357
if ($this->componentSchema->isNonDb() && $attribute->isReference()) {
@@ -374,14 +371,14 @@ protected function resolveProperty(
374371
* @param string $relatedTableName
375372
* @param ComponentSchema $refSchema
376373
* @return bool
377-
* @throws \yii\base\InvalidConfigException
374+
* @throws InvalidConfigException|InvalidDefinitionException
378375
*/
379376
protected function catchManyToMany(
380377
string $propertyName,
381378
string $relatedSchemaName,
382379
string $relatedTableName,
383380
ComponentSchema $refSchema
384-
):bool {
381+
): bool {
385382
if (strtolower(Inflector::id2camel($propertyName, '_'))
386383
!== strtolower(Inflector::pluralize($relatedSchemaName))) {
387384
return false;
@@ -413,9 +410,9 @@ protected function catchManyToMany(
413410
}
414411

415412
/**
416-
* @throws \yii\base\InvalidConfigException
413+
* @throws InvalidConfigException
417414
*/
418-
protected function guessFakerStub(Attribute $attribute, PropertySchema $property):?string
415+
protected function guessFakerStub(Attribute $attribute, PropertySchema $property): ?string
419416
{
420417
$resolver = Yii::createObject(['class' => FakerStubResolver::class], [$attribute, $property, $this->config]);
421418
return $resolver->resolve();
@@ -424,9 +421,9 @@ protected function guessFakerStub(Attribute $attribute, PropertySchema $property
424421
/**
425422
* @param array $indexes
426423
* @return array|DbIndex[]
427-
* @throws \cebe\yii2openapi\lib\exceptions\InvalidDefinitionException
424+
* @throws InvalidDefinitionException
428425
*/
429-
protected function prepareIndexes(array $indexes):array
426+
protected function prepareIndexes(array $indexes): array
430427
{
431428
$dbIndexes = [];
432429
foreach ($indexes as $index) {
@@ -481,12 +478,12 @@ protected function prepareIndexes(array $indexes):array
481478
}
482479

483480
/**
484-
* @param \cebe\yii2openapi\lib\openapi\PropertySchema $property
485-
* @param \cebe\yii2openapi\lib\items\Attribute $attribute
481+
* @param PropertySchema $property
482+
* @param Attribute $attribute
486483
* @return void
487-
* @throws \yii\base\InvalidConfigException
484+
* @throws InvalidConfigException|InvalidDefinitionException
488485
*/
489-
protected function resolvePropertyRef(PropertySchema $property, Attribute $attribute):void
486+
protected function resolvePropertyRef(PropertySchema $property, Attribute $attribute): void
490487
{
491488
$fkProperty = new PropertySchema(
492489
$property->getRefSchema()->getSchema(),
@@ -495,11 +492,11 @@ protected function resolvePropertyRef(PropertySchema $property, Attribute $attri
495492
);
496493
[$min, $max] = $fkProperty->guessMinMax();
497494
$attribute->setPhpType($fkProperty->guessPhpType())
498-
->setDbType($fkProperty->guessDbType(true))
499-
->setSize($fkProperty->getMaxLength())
500-
->setDescription($fkProperty->getAttr('description'))
501-
->setDefault($fkProperty->guessDefault())
502-
->setLimits($min, $max, $fkProperty->getMinLength());
495+
->setDbType($fkProperty->guessDbType(true))
496+
->setSize($fkProperty->getMaxLength())
497+
->setDescription($fkProperty->getAttr('description'))
498+
->setDefault($fkProperty->guessDefault())
499+
->setLimits($min, $max, $fkProperty->getMinLength());
503500
$this->attributes[$property->getName()] =
504501
$attribute->setFakerStub($this->guessFakerStub($attribute, $fkProperty));
505502
}
@@ -513,4 +510,22 @@ public static function relationName(string $propertyName, ?string $fkColumnName)
513510
}
514511
return $relationName;
515512
}
513+
514+
/**
515+
* @throws InvalidConfigException
516+
*/
517+
public function addInverseRelation(
518+
string $relatedClassName,
519+
Attribute $attribute,
520+
PropertySchema $property,
521+
PropertySchema $fkProperty
522+
): void {
523+
$inverseRelation = Yii::createObject(
524+
AttributeRelation::class,
525+
[$this->schemaName, $this->tableName, $this->schemaName]
526+
)
527+
->asHasOne([$attribute->columnName => $fkProperty->getName()]);
528+
$inverseRelation->setInverse($property->getName());
529+
$this->inverseRelations[$relatedClassName][] = $inverseRelation;
530+
}
516531
}

0 commit comments

Comments
 (0)