From 03a68bf261a15c936b463e46bd50d027a5e6fd23 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sun, 18 Aug 2024 20:00:15 +0530 Subject: [PATCH 01/17] Initial commit to create PR --- TODO.taskpaper | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 TODO.taskpaper diff --git a/TODO.taskpaper b/TODO.taskpaper new file mode 100644 index 00000000..07c175cc --- /dev/null +++ b/TODO.taskpaper @@ -0,0 +1,9 @@ +TODO.taskpaper + +### Generate inverse relations #25 + ☐ create failing test + ☐ implement the solution + ☐ fix failing tests if any + ☐ resolve TODOs if any + ☐ review PR + ☐ delete this file and submit PR From 59be8e59224f4a5bb656d63901e2045cee88b470 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sun, 18 Aug 2024 20:13:41 +0530 Subject: [PATCH 02/17] Cleanup --- src/lib/SchemaToDatabase.php | 47 +++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/lib/SchemaToDatabase.php b/src/lib/SchemaToDatabase.php index 0e93ff29..8217513d 100644 --- a/src/lib/SchemaToDatabase.php +++ b/src/lib/SchemaToDatabase.php @@ -7,10 +7,16 @@ namespace cebe\yii2openapi\lib; +use cebe\openapi\exceptions\IOException; +use cebe\openapi\exceptions\TypeErrorException; +use cebe\openapi\exceptions\UnresolvableReferenceException; +use cebe\yii2openapi\lib\exceptions\InvalidDefinitionException; +use cebe\yii2openapi\lib\items\DbModel; use cebe\yii2openapi\lib\items\JunctionSchemas; use cebe\yii2openapi\lib\openapi\ComponentSchema; use Yii; use yii\base\Exception; +use yii\base\InvalidConfigException; use yii\helpers\StringHelper; use function count; @@ -55,10 +61,7 @@ */ class SchemaToDatabase { - /** - * @var \cebe\yii2openapi\lib\Config - */ - protected $config; + protected Config $config; public function __construct(Config $config) { @@ -66,15 +69,15 @@ public function __construct(Config $config) } /** - * @return array|\cebe\yii2openapi\lib\items\DbModel[] - * @throws \cebe\openapi\exceptions\IOException - * @throws \cebe\openapi\exceptions\TypeErrorException - * @throws \cebe\openapi\exceptions\UnresolvableReferenceException - * @throws \cebe\yii2openapi\lib\exceptions\InvalidDefinitionException - * @throws \yii\base\Exception - * @throws \yii\base\InvalidConfigException + * @return array|DbModel[] + * @throws IOException + * @throws TypeErrorException + * @throws UnresolvableReferenceException + * @throws InvalidDefinitionException + * @throws Exception + * @throws InvalidConfigException */ - public function prepareModels():array + public function prepareModels(): array { $models = []; $openApi = $this->config->getOpenApi(); @@ -88,11 +91,11 @@ public function prepareModels():array if ($junctions->isJunctionSchema($schemaName)) { $schemaName = $junctions->trimPrefix($schemaName); } - /**@var \cebe\yii2openapi\lib\AttributeResolver $resolver */ + /**@var AttributeResolver $resolver */ $resolver = Yii::createObject(AttributeResolver::class, [$schemaName, $schema, $junctions, $this->config]); $models[$schemaName] = $resolver->resolve(); } - foreach ($models as $model) { + foreach ($models as $model) { foreach ($model->many2many as $relation) { if (isset($models[$relation->viaModelName])) { $relation->hasViaModel = true; @@ -108,14 +111,14 @@ public function prepareModels():array } /** - * @return \cebe\yii2openapi\lib\items\JunctionSchemas - * @throws \cebe\openapi\exceptions\IOException - * @throws \cebe\openapi\exceptions\TypeErrorException - * @throws \cebe\openapi\exceptions\UnresolvableReferenceException - * @throws \yii\base\Exception - * @throws \yii\base\InvalidConfigException + * @return JunctionSchemas + * @throws IOException + * @throws TypeErrorException + * @throws UnresolvableReferenceException + * @throws Exception + * @throws InvalidConfigException|InvalidDefinitionException */ - public function findJunctionSchemas():JunctionSchemas + public function findJunctionSchemas(): JunctionSchemas { $junctions = []; $openApi = $this->config->getOpenApi(); @@ -195,7 +198,7 @@ public function findJunctionSchemas():JunctionSchemas return Yii::createObject(JunctionSchemas::class, [$junctions]); } - private function canGenerateModel(string $schemaName, ComponentSchema $schema):bool + private function canGenerateModel(string $schemaName, ComponentSchema $schema): bool { // only generate tables for schemas of type object and those who have defined properties if ($schema->isObjectSchema() && !$schema->hasProperties()) { From a0de22f37ac693e97bfab5abeab63dfe8c731b0f Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sun, 18 Aug 2024 20:29:34 +0530 Subject: [PATCH 03/17] Cleanup and add test OpenAPI spec --- src/lib/AttributeResolver.php | 177 ++++++++---------- .../25_generate_inverse_relations/index.php | 14 ++ .../25_generate_inverse_relations/index.yaml | 83 ++++++++ tests/unit/IssueFixTest.php | 15 ++ 4 files changed, 193 insertions(+), 96 deletions(-) create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/index.php create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/index.yaml diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index 0063d8df..d4340d20 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -7,8 +7,6 @@ namespace cebe\yii2openapi\lib; -use cebe\yii2openapi\lib\Config; -use cebe\yii2openapi\lib\CustomSpecAttr; use cebe\yii2openapi\lib\exceptions\InvalidDefinitionException; use cebe\yii2openapi\lib\items\Attribute; use cebe\yii2openapi\lib\items\AttributeRelation; @@ -20,9 +18,9 @@ use cebe\yii2openapi\lib\openapi\ComponentSchema; use cebe\yii2openapi\lib\openapi\PropertySchema; use Yii; +use yii\base\InvalidConfigException; use yii\helpers\Inflector; use yii\helpers\StringHelper; -use yii\helpers\VarDumper; use function explode; use function strpos; use function strtolower; @@ -32,49 +30,34 @@ class AttributeResolver /** * @var Attribute[]|array */ - private $attributes = []; + private array $attributes = []; /** * @var AttributeRelation[]|array */ - private $relations = []; + private array $relations = []; /** * @var NonDbRelation[]|array */ - private $nonDbRelations = []; + private array $nonDbRelations = []; /** * @var ManyToManyRelation[]|array */ - private $many2many = []; + private array $many2many = []; - /** - * @var string - */ - private $schemaName; + private string $schemaName; - /** - * @var string - */ - private $tableName; + private string $tableName; - /** - * @var ComponentSchema - */ - private $schema; + private ComponentSchema $schema; - /** - * @var \cebe\yii2openapi\lib\items\JunctionSchemas - */ - private $junctions; + private JunctionSchemas $junctions; - /** @var bool */ - private $isJunctionSchema; + private bool $isJunctionSchema; - /** @var bool */ - private $hasMany2Many; + private bool $hasMany2Many; - /** @var Config */ - private $config; + private ?Config $config; public function __construct(string $schemaName, ComponentSchema $schema, JunctionSchemas $junctions, ?Config $config = null) { @@ -88,14 +71,14 @@ public function __construct(string $schemaName, ComponentSchema $schema, Junctio } /** - * @return \cebe\yii2openapi\lib\items\DbModel - * @throws \cebe\yii2openapi\lib\exceptions\InvalidDefinitionException - * @throws \yii\base\InvalidConfigException + * @return DbModel + * @throws InvalidDefinitionException + * @throws InvalidConfigException */ - public function resolve():DbModel + public function resolve(): DbModel { foreach ($this->schema->getProperties() as $property) { - /** @var $property \cebe\yii2openapi\lib\openapi\PropertySchema */ + /** @var $property PropertySchema */ $isRequired = $this->schema->isRequiredProperty($property->getName()); $nullableValue = $property->getProperty()->getSerializableData()->nullable ?? null; @@ -130,24 +113,24 @@ public function resolve():DbModel } /** - * @param \cebe\yii2openapi\lib\openapi\PropertySchema $property - * @param bool $isRequired - * @throws \cebe\yii2openapi\lib\exceptions\InvalidDefinitionException - * @throws \yii\base\InvalidConfigException + * @param PropertySchema $property + * @param bool $isRequired + * @throws InvalidDefinitionException + * @throws InvalidConfigException */ - protected function resolveJunctionTableProperty(PropertySchema $property, bool $isRequired):void + protected function resolveJunctionTableProperty(PropertySchema $property, bool $isRequired): void { if ($this->junctions->isJunctionProperty($this->schemaName, $property->getName())) { $junkAttribute = $this->junctions->byJunctionSchema($this->schemaName)[$property->getName()]; $attribute = Yii::createObject(Attribute::class, [$property->getName()]); $attribute->setRequired($isRequired) - ->setDescription($property->getAttr('description', '')) - ->setReadOnly($property->isReadonly()) - ->setIsPrimary($property->isPrimaryKey()) - ->asReference($junkAttribute['relatedClassName']) - ->setPhpType($junkAttribute['phpType']) - ->setDbType($junkAttribute['dbType']) - ->setForeignKeyColumnName($property->fkColName); + ->setDescription($property->getAttr('description', '')) + ->setReadOnly($property->isReadonly()) + ->setIsPrimary($property->isPrimaryKey()) + ->asReference($junkAttribute['relatedClassName']) + ->setPhpType($junkAttribute['phpType']) + ->setDbType($junkAttribute['dbType']) + ->setForeignKeyColumnName($property->fkColName); $relation = Yii::createObject(AttributeRelation::class, [ $property->getName(), $junkAttribute['relatedTableName'], @@ -162,12 +145,12 @@ protected function resolveJunctionTableProperty(PropertySchema $property, bool $ } /** - * @param \cebe\yii2openapi\lib\openapi\PropertySchema $property - * @param bool $isRequired - * @throws \cebe\yii2openapi\lib\exceptions\InvalidDefinitionException - * @throws \yii\base\InvalidConfigException + * @param PropertySchema $property + * @param bool $isRequired + * @throws InvalidDefinitionException + * @throws InvalidConfigException */ - protected function resolveHasMany2ManyTableProperty(PropertySchema $property, bool $isRequired):void + protected function resolveHasMany2ManyTableProperty(PropertySchema $property, bool $isRequired): void { if ($this->junctions->isManyToManyProperty($this->schemaName, $property->getName())) { return; @@ -197,7 +180,7 @@ protected function resolveHasMany2ManyTableProperty(PropertySchema $property, bo $this->relations[Inflector::pluralize($junkRef)] = Yii::createObject(AttributeRelation::class, [$junkRef, $junkAttribute['junctionTable'], $viaModel]) - ->asHasMany([$junkAttribute['pairProperty'] . '_id' => $this->schema->getPkName()]); + ->asHasMany([$junkAttribute['pairProperty'] . '_id' => $this->schema->getPkName()]); return; } @@ -205,35 +188,36 @@ protected function resolveHasMany2ManyTableProperty(PropertySchema $property, bo } /** - * @param \cebe\yii2openapi\lib\openapi\PropertySchema $property - * @param bool $isRequired - * @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 - * @throws \cebe\yii2openapi\lib\exceptions\InvalidDefinitionException - * @throws \yii\base\InvalidConfigException + * @param PropertySchema $property + * @param bool $isRequired + * @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 + * @throws InvalidDefinitionException + * @throws InvalidConfigException */ protected function resolveProperty( PropertySchema $property, bool $isRequired, - $nullableValue = 'ARG_ABSENT' - ):void { + $nullableValue = 'ARG_ABSENT' + ): void + { if ($nullableValue === 'ARG_ABSENT') { $nullableValue = $property->getProperty()->getSerializableData()->nullable ?? null; } $attribute = Yii::createObject(Attribute::class, [$property->getName()]); $attribute->setRequired($isRequired) - ->setDescription($property->getAttr('description', '')) - ->setReadOnly($property->isReadonly()) - ->setDefault($property->guessDefault()) - ->setXDbType($property->getAttr(CustomSpecAttr::DB_TYPE)) - ->setXDbDefaultExpression($property->getAttr(CustomSpecAttr::DB_DEFAULT_EXPRESSION)) - ->setNullable($nullableValue) - ->setIsPrimary($property->isPrimaryKey()) - ->setForeignKeyColumnName($property->fkColName); + ->setDescription($property->getAttr('description', '')) + ->setReadOnly($property->isReadonly()) + ->setDefault($property->guessDefault()) + ->setXDbType($property->getAttr(CustomSpecAttr::DB_TYPE)) + ->setXDbDefaultExpression($property->getAttr(CustomSpecAttr::DB_DEFAULT_EXPRESSION)) + ->setNullable($nullableValue) + ->setIsPrimary($property->isPrimaryKey()) + ->setForeignKeyColumnName($property->fkColName); if ($property->isReference()) { if ($property->isVirtual()) { throw new InvalidDefinitionException('References not supported for virtual attributes'); } - + if ($property->isNonDbReference()) { $attribute->asNonDbReference($property->getRefClassName()); $relation = Yii::createObject( @@ -258,17 +242,17 @@ protected function resolveProperty( [$min, $max] = $fkProperty->guessMinMax(); $attribute->asReference($relatedClassName); $attribute->setPhpType($fkProperty->guessPhpType()) - ->setDbType($fkProperty->guessDbType(true)) - ->setSize($fkProperty->getMaxLength()) - ->setDescription($property->getRefSchema()->getDescription()) - ->setDefault($fkProperty->guessDefault()) - ->setLimits($min, $max, $fkProperty->getMinLength()); + ->setDbType($fkProperty->guessDbType(true)) + ->setSize($fkProperty->getMaxLength()) + ->setDescription($property->getRefSchema()->getDescription()) + ->setDefault($fkProperty->guessDefault()) + ->setLimits($min, $max, $fkProperty->getMinLength()); $relation = Yii::createObject( AttributeRelation::class, [$property->getName(), $relatedTableName, $relatedClassName] ) - ->asHasOne([$fkProperty->getName() => $attribute->columnName]); + ->asHasOne([$fkProperty->getName() => $attribute->columnName]); $relation->onUpdateFkConstraint = $property->onUpdateFkConstraint; $relation->onDeleteFkConstraint = $property->onDeleteFkConstraint; if ($property->isRefPointerToSelf()) { @@ -279,10 +263,10 @@ protected function resolveProperty( if (!$property->isReference() && !$property->hasRefItems()) { [$min, $max] = $property->guessMinMax(); $attribute->setIsVirtual($property->isVirtual()) - ->setPhpType($property->guessPhpType()) - ->setDbType($property->guessDbType()) - ->setSize($property->getMaxLength()) - ->setLimits($min, $max, $property->getMinLength()); + ->setPhpType($property->guessPhpType()) + ->setDbType($property->guessDbType()) + ->setSize($property->getMaxLength()) + ->setLimits($min, $max, $property->getMinLength()); if ($property->hasEnum()) { $attribute->setEnumValues($property->getAttr('enum')); } @@ -319,7 +303,7 @@ protected function resolveProperty( AttributeRelation::class, [$property->getName(), $relatedTableName, $relatedClassName] ) - ->asHasMany([$fkProperty->getName() => $fkProperty->getName()])->asSelfReference(); + ->asHasMany([$fkProperty->getName() => $fkProperty->getName()])->asSelfReference(); return; } $foreignPk = Inflector::camel2id($fkProperty->getName(), '_') . '_id'; @@ -328,7 +312,7 @@ protected function resolveProperty( AttributeRelation::class, [$property->getName(), $relatedTableName, $relatedClassName] ) - ->asHasMany([$foreignPk => $this->schema->getPkName()]); + ->asHasMany([$foreignPk => $this->schema->getPkName()]); return; } $relatedClassName = $property->getRefClassName(); @@ -347,7 +331,7 @@ protected function resolveProperty( AttributeRelation::class, [$property->getName(), $relatedTableName, $relatedClassName] ) - ->asHasMany([Inflector::camel2id($this->schemaName, '_') . '_id' => $this->schema->getPkName()]); + ->asHasMany([Inflector::camel2id($this->schemaName, '_') . '_id' => $this->schema->getPkName()]); return; } if ($this->schema->isNonDb() && $attribute->isReference()) { @@ -367,14 +351,15 @@ protected function resolveProperty( * @param string $relatedTableName * @param ComponentSchema $refSchema * @return bool - * @throws \yii\base\InvalidConfigException + * @throws InvalidConfigException|InvalidDefinitionException */ protected function catchManyToMany( string $propertyName, string $relatedSchemaName, string $relatedTableName, ComponentSchema $refSchema - ):bool { + ): bool + { if (strtolower(Inflector::id2camel($propertyName, '_')) !== strtolower(Inflector::pluralize($relatedSchemaName))) { return false; @@ -406,9 +391,9 @@ protected function catchManyToMany( } /** - * @throws \yii\base\InvalidConfigException + * @throws InvalidConfigException */ - protected function guessFakerStub(Attribute $attribute, PropertySchema $property):?string + protected function guessFakerStub(Attribute $attribute, PropertySchema $property): ?string { $resolver = Yii::createObject(['class' => FakerStubResolver::class], [$attribute, $property, $this->config]); return $resolver->resolve(); @@ -417,9 +402,9 @@ protected function guessFakerStub(Attribute $attribute, PropertySchema $property /** * @param array $indexes * @return array|DbIndex[] - * @throws \cebe\yii2openapi\lib\exceptions\InvalidDefinitionException + * @throws InvalidDefinitionException */ - protected function prepareIndexes(array $indexes):array + protected function prepareIndexes(array $indexes): array { $dbIndexes = []; foreach ($indexes as $index) { @@ -470,12 +455,12 @@ protected function prepareIndexes(array $indexes):array } /** - * @param \cebe\yii2openapi\lib\openapi\PropertySchema $property - * @param \cebe\yii2openapi\lib\items\Attribute $attribute + * @param PropertySchema $property + * @param Attribute $attribute * @return void - * @throws \yii\base\InvalidConfigException + * @throws InvalidConfigException|InvalidDefinitionException */ - protected function resolvePropertyRef(PropertySchema $property, Attribute $attribute):void + protected function resolvePropertyRef(PropertySchema $property, Attribute $attribute): void { $fkProperty = new PropertySchema( $property->getRefSchema()->getSchema(), @@ -484,11 +469,11 @@ protected function resolvePropertyRef(PropertySchema $property, Attribute $attri ); [$min, $max] = $fkProperty->guessMinMax(); $attribute->setPhpType($fkProperty->guessPhpType()) - ->setDbType($fkProperty->guessDbType(true)) - ->setSize($fkProperty->getMaxLength()) - ->setDescription($fkProperty->getAttr('description')) - ->setDefault($fkProperty->guessDefault()) - ->setLimits($min, $max, $fkProperty->getMinLength()); + ->setDbType($fkProperty->guessDbType(true)) + ->setSize($fkProperty->getMaxLength()) + ->setDescription($fkProperty->getAttr('description')) + ->setDefault($fkProperty->guessDefault()) + ->setLimits($min, $max, $fkProperty->getMinLength()); $this->attributes[$property->getName()] = $attribute->setFakerStub($this->guessFakerStub($attribute, $fkProperty)); } diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/index.php b/tests/specs/issue_fix/25_generate_inverse_relations/index.php new file mode 100644 index 00000000..535787af --- /dev/null +++ b/tests/specs/issue_fix/25_generate_inverse_relations/index.php @@ -0,0 +1,14 @@ + '@specs/issue_fix/25_generate_inverse_relations/index.yaml', + 'generateUrls' => false, + 'generateModels' => true, + 'excludeModels' => [ + 'Error', + ], + 'generateControllers' => false, + 'generateMigrations' => true, + 'generateModelFaker' => true, // `generateModels` must be `true` in order to use `generateModelFaker` as `true` +]; + diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml b/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml new file mode 100644 index 00000000..7a780944 --- /dev/null +++ b/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml @@ -0,0 +1,83 @@ + +openapi: 3.0.3 + +info: + title: 'Generate inverse relations #25' + version: 1.0.0 + +components: + schemas: + User: + type: object + required: + - id + - name + properties: + id: + type: integer + readOnly: true + name: + type: string + maxLength: 128 + + Account: + description: Account + type: object + required: + - id + - name + properties: + id: + type: integer + readOnly: true + name: + description: account name + type: string + maxLength: 128 + paymentMethodName: + type: string + user: + $ref: '#/components/schemas/User' + + Contact: + description: Contact + type: object + required: + - id + - account + properties: + id: + type: integer + readOnly: true + account: + $ref: '#/components/schemas/Account' + active: + type: boolean + default: false + nickname: + type: string + + PaymentMethod: + type: object + description: PaymentMethod + x-indexes: + - 'unique:name' + required: + - id + - name + properties: + id: + type: integer + readOnly: true + name: + type: string + example: Bank transfer within 14 days + maxLength: 150 + x-faker: false + +paths: + '/account': + get: + responses: + '200': + description: Account with id = "\" was not found. diff --git a/tests/unit/IssueFixTest.php b/tests/unit/IssueFixTest.php index b6c7abdb..dd20604a 100644 --- a/tests/unit/IssueFixTest.php +++ b/tests/unit/IssueFixTest.php @@ -360,4 +360,19 @@ public function test158BugGiiapiGeneratedRulesEnumWithTrim() ]); $this->checkFiles($actualFiles, $expectedFiles); } + + // https://github.com/php-openapi/yii2-openapi/issues/25 + public function test25GenerateInverseRelations() + { + // 25_generate_inverse_relations + $testFile = Yii::getAlias("@specs/issue_fix/25_generate_inverse_relations/index.php"); + $this->runGenerator($testFile); +// $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ +// 'recursive' => true, +// ]); +// $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/25_generate_inverse_relations/mysql"), [ +// 'recursive' => true, +// ]); +// $this->checkFiles($actualFiles, $expectedFiles); + } } From df272319cb49cb09a005ca9be183ae641c9e023e Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sun, 18 Aug 2024 20:34:32 +0530 Subject: [PATCH 04/17] Cleanup 2 --- .../25_generate_inverse_relations/index.yaml | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml b/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml index 7a780944..f1e9f59e 100644 --- a/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml +++ b/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml @@ -39,42 +39,6 @@ components: user: $ref: '#/components/schemas/User' - Contact: - description: Contact - type: object - required: - - id - - account - properties: - id: - type: integer - readOnly: true - account: - $ref: '#/components/schemas/Account' - active: - type: boolean - default: false - nickname: - type: string - - PaymentMethod: - type: object - description: PaymentMethod - x-indexes: - - 'unique:name' - required: - - id - - name - properties: - id: - type: integer - readOnly: true - name: - type: string - example: Bank transfer within 14 days - maxLength: 150 - x-faker: false - paths: '/account': get: From 489f0f5a251072a1c25e3be5518c61f858dbd996 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sun, 18 Aug 2024 20:41:07 +0530 Subject: [PATCH 05/17] Fix style --- src/lib/AttributeResolver.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index d4340d20..74ef5e79 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -197,9 +197,8 @@ protected function resolveHasMany2ManyTableProperty(PropertySchema $property, bo protected function resolveProperty( PropertySchema $property, bool $isRequired, - $nullableValue = 'ARG_ABSENT' - ): void - { + $nullableValue = 'ARG_ABSENT' + ): void { if ($nullableValue === 'ARG_ABSENT') { $nullableValue = $property->getProperty()->getSerializableData()->nullable ?? null; } @@ -358,8 +357,7 @@ protected function catchManyToMany( string $relatedSchemaName, string $relatedTableName, ComponentSchema $refSchema - ): bool - { + ): bool { if (strtolower(Inflector::id2camel($propertyName, '_')) !== strtolower(Inflector::pluralize($relatedSchemaName))) { return false; From e25b36c5fd7552e14e19c09aa91cd9373779ddf2 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Tue, 20 Aug 2024 17:04:31 +0530 Subject: [PATCH 06/17] Refactor --- src/lib/items/DbModel.php | 87 ++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 4f174370..ea1c2911 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -10,9 +10,9 @@ use cebe\yii2openapi\lib\ValidationRulesBuilder; use Yii; use yii\base\BaseObject; +use yii\base\InvalidConfigException; use yii\db\ColumnSchema; use yii\helpers\Inflector; -use yii\helpers\StringHelper; use yii\helpers\VarDumper; use function array_filter; use function array_map; @@ -20,73 +20,68 @@ use const PHP_EOL; /** - * @property-read string $tableAlias - * @property-read array $uniqueColumnsList - * @property-read array[]|array $attributesByType - * @property-read array|\cebe\yii2openapi\lib\items\AttributeRelation[] $hasOneRelations + * @property-read string $tableAlias + * @property-read array $uniqueColumnsList + * @property-read array[]|array $attributesByType + * @property-read array|AttributeRelation[] $hasOneRelations */ class DbModel extends BaseObject { - /** - * @var string primary key attribute name - */ - public $pkName; + // primary key attribute name + public string $pkName; - /** - * @var string model name. - */ - public $name; + // model name + public string $name; - /** - * @var string table name. (without brackets and db prefix) - */ - public $tableName; + // table name. (without brackets and db prefix) + public string $tableName; - /** - * @var string description from the schema. - */ - public $description; + // description from the schema. + public string $description; /** - * @var array|\cebe\yii2openapi\lib\items\Attribute[] model attributes. + * @var array|Attribute[] model attributes. */ - public $attributes = []; + public array $attributes = []; /** - * @var array|\cebe\yii2openapi\lib\items\AttributeRelation[] database relations. + * @var array|AttributeRelation[] database relations. */ - public $relations = []; + public array $relations = []; /*** - * @var array|\cebe\yii2openapi\lib\items\NonDbRelation[] non-db relations + * @var array|NonDbRelation[] non-db relations */ - public $nonDbRelations = []; + public array $nonDbRelations = []; /** - * @var array|\cebe\yii2openapi\lib\items\ManyToManyRelation[] many to many relations. + * @var array|ManyToManyRelation[] many-to-many relations. */ - public $many2many = []; + public array $many2many = []; - public $junctionCols = []; + public array $junctionCols = []; /** - * @var \cebe\yii2openapi\lib\items\DbIndex[]|array + * @var DbIndex[]|array */ - public $indexes = []; + public array $indexes = []; - public $isNotDb = false; + public bool $isNotDb = false; - public function getTableAlias():string + public function getTableAlias(): string { return '{{%' . $this->tableName . '}}'; } - public function getClassName():string + public function getClassName(): string { return Inflector::id2camel($this->name, '_'); } - public function getValidationRules():string + /** + * @throws InvalidConfigException + */ + public function getValidationRules(): string { $rules = Yii::createObject(ValidationRulesBuilder::class, [$this])->build(); $rules = array_map('strval', $rules); @@ -105,9 +100,9 @@ public function getValidationRules():string } /** - * @return \cebe\yii2openapi\lib\items\AttributeRelation[]|array + * @return AttributeRelation[]|array */ - public function getHasOneRelations():array + public function getHasOneRelations(): array { return array_filter( $this->relations, @@ -117,7 +112,7 @@ static function (AttributeRelation $relation) { ); } - public function getPkAttribute():Attribute + public function getPkAttribute(): Attribute { return $this->attributes[$this->pkName]; } @@ -125,7 +120,7 @@ public function getPkAttribute():Attribute /** * @return ColumnSchema[] */ - public function attributesToColumnSchema():array + public function attributesToColumnSchema(): array { return $this->isNotDb ? [] @@ -142,9 +137,9 @@ static function ($acc, Attribute $attribute) { } /** - * @return array|\cebe\yii2openapi\lib\items\Attribute[] + * @return array|Attribute[] */ - public function getEnumAttributes():array + public function getEnumAttributes(): array { return array_filter( $this->attributes, @@ -155,9 +150,9 @@ static function (Attribute $attribute) { } /** - * @return array|\cebe\yii2openapi\lib\items\Attribute[] + * @return array|Attribute[] */ - public function virtualAttributes():array + public function virtualAttributes(): array { return array_filter($this->attributes, static function (Attribute $attribute) { return $attribute->isVirtual; @@ -165,9 +160,9 @@ public function virtualAttributes():array } /** - * @return array|\cebe\yii2openapi\lib\items\Attribute[] + * @return array|Attribute[] */ - public function dbAttributes():array + public function dbAttributes(): array { return array_filter($this->attributes, static function (Attribute $attribute) { return !$attribute->isVirtual; From dcd49d115ba55bd401f238f2d6f5681d9f7c8da3 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Tue, 20 Aug 2024 17:10:27 +0530 Subject: [PATCH 07/17] Refactor again for more code readability --- src/lib/items/AttributeRelation.php | 74 ++++++++++-------------- src/lib/items/DbModel.php | 5 ++ src/lib/traits/ForeignKeyConstraints.php | 6 +- 3 files changed, 38 insertions(+), 47 deletions(-) diff --git a/src/lib/items/AttributeRelation.php b/src/lib/items/AttributeRelation.php index 7b21fa76..3cf2cb6f 100644 --- a/src/lib/items/AttributeRelation.php +++ b/src/lib/items/AttributeRelation.php @@ -7,10 +7,10 @@ namespace cebe\yii2openapi\lib\items; +use cebe\yii2openapi\lib\traits\ForeignKeyConstraints; use yii\helpers\Inflector; use yii\helpers\VarDumper; use function reset; -use cebe\yii2openapi\lib\traits\ForeignKeyConstraints; class AttributeRelation { @@ -19,41 +19,29 @@ class AttributeRelation public const HAS_ONE = 'hasOne'; public const HAS_MANY = 'hasMany'; - /** - * @var string $name - **/ - private $name; + private string $name; - /** - * @var string $tableName - **/ - private $tableName; + private ?string $tableName; - /** - * @var string $className - **/ - private $className; + private ?string $className; /** - * @var string $method (hasOne/hasMany) + * hasOne/hasMany **/ - private $method; + private ?string $method; - /** - * @var array - **/ - private $link = []; + private array $link; - /**@var bool */ - private $selfReference = false; + private bool $selfReference = false; public function __construct( - string $name, + string $name, ?string $tableName = null, ?string $className = null, ?string $method = null, - array $link = [] - ) { + array $link = [] + ) + { $this->name = $name; $this->tableName = $tableName; $this->className = $className; @@ -65,7 +53,7 @@ public function __construct( * @param string $name * @return AttributeRelation */ - public function setName(string $name):AttributeRelation + public function setName(string $name): AttributeRelation { $this->name = $name; return $this; @@ -75,7 +63,7 @@ public function setName(string $name):AttributeRelation * @param string $tableName * @return AttributeRelation */ - public function setTableName(string $tableName):AttributeRelation + public function setTableName(string $tableName): AttributeRelation { $this->tableName = $tableName; return $this; @@ -85,38 +73,38 @@ public function setTableName(string $tableName):AttributeRelation * @param string $className * @return AttributeRelation */ - public function setClassName(string $className):AttributeRelation + public function setClassName(string $className): AttributeRelation { $this->className = $className; return $this; } - public function asSelfReference():AttributeRelation + public function asSelfReference(): AttributeRelation { $this->selfReference = true; return $this; } - public function asHasOne(array $link):AttributeRelation + public function asHasOne(array $link): AttributeRelation { $this->method = self::HAS_ONE; $this->link = $link; return $this; } - public function asHasMany(array $link):AttributeRelation + public function asHasMany(array $link): AttributeRelation { $this->method = self::HAS_MANY; $this->link = $link; return $this; } - public function isHasOne():bool + public function isHasOne(): bool { return $this->method === self::HAS_ONE; } - public function isSelfReferenced():bool + public function isSelfReferenced(): bool { return $this->selfReference; } @@ -124,7 +112,7 @@ public function isSelfReferenced():bool /** * @return string */ - public function getName():string + public function getName(): string { return $this->name; } @@ -132,12 +120,12 @@ public function getName():string /** * @return string */ - public function getTableName():string + public function getTableName(): string { return $this->tableName; } - public function getTableAlias():string + public function getTableAlias(): string { return "{{%$this->tableName}}"; } @@ -145,12 +133,12 @@ public function getTableAlias():string /** * @return string */ - public function getClassName():string + public function getClassName(): string { return $this->className; } - public function getClassKey():string + public function getClassKey(): string { return Inflector::camel2id($this->getClassName()); } @@ -158,7 +146,7 @@ public function getClassKey():string /** * @return string */ - public function getMethod():string + public function getMethod(): string { return $this->method; } @@ -166,27 +154,27 @@ public function getMethod():string /** * @return array */ - public function getLink():array + public function getLink(): array { return $this->link; } - public function getCamelName():string + public function getCamelName(): string { return Inflector::camelize($this->name); } - public function getColumnName():string + public function getColumnName(): string { return reset($this->link); } - public function getForeignName():string + public function getForeignName(): string { return key($this->link); } - public function linkToString():string + public function linkToString(): string { return str_replace( [',', '=>', ', ]'], diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index ea1c2911..962fae91 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -59,6 +59,11 @@ class DbModel extends BaseObject */ public array $many2many = []; + /** + * @var array|AttributeRelation[] inverse relations # TODO + */ + public array $inverseRelations = []; + public array $junctionCols = []; /** diff --git a/src/lib/traits/ForeignKeyConstraints.php b/src/lib/traits/ForeignKeyConstraints.php index a3af3895..215abb42 100644 --- a/src/lib/traits/ForeignKeyConstraints.php +++ b/src/lib/traits/ForeignKeyConstraints.php @@ -10,18 +10,16 @@ trait ForeignKeyConstraints { /** - * @var string * Contains foreign key constraint * @example 'SET NULL' * @example 'CASCADE' */ - public $onDeleteFkConstraint; + public string $onDeleteFkConstraint; /** - * @var string * Contains foreign key constraint * @example 'SET NULL' * @example 'CASCADE' */ - public $onUpdateFkConstraint; + public string $onUpdateFkConstraint; } From 3f4111671b82ffaec1d0eba5dd339f7bc769869e Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Tue, 20 Aug 2024 17:15:11 +0530 Subject: [PATCH 08/17] Fix failing tests --- src/lib/items/DbModel.php | 6 ++++-- src/lib/traits/ForeignKeyConstraints.php | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 962fae91..58691c22 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -27,8 +27,10 @@ */ class DbModel extends BaseObject { - // primary key attribute name - public string $pkName; + /** + * @var string primary key attribute name + */ + public $pkName; // model name public string $name; diff --git a/src/lib/traits/ForeignKeyConstraints.php b/src/lib/traits/ForeignKeyConstraints.php index 215abb42..a3af3895 100644 --- a/src/lib/traits/ForeignKeyConstraints.php +++ b/src/lib/traits/ForeignKeyConstraints.php @@ -10,16 +10,18 @@ trait ForeignKeyConstraints { /** + * @var string * Contains foreign key constraint * @example 'SET NULL' * @example 'CASCADE' */ - public string $onDeleteFkConstraint; + public $onDeleteFkConstraint; /** + * @var string * Contains foreign key constraint * @example 'SET NULL' * @example 'CASCADE' */ - public string $onUpdateFkConstraint; + public $onUpdateFkConstraint; } From 71637c8573b54ce86effa92b4f6376715f6c1c58 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Tue, 20 Aug 2024 17:16:08 +0530 Subject: [PATCH 09/17] Fix style --- src/lib/items/AttributeRelation.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/items/AttributeRelation.php b/src/lib/items/AttributeRelation.php index 3cf2cb6f..885abfb5 100644 --- a/src/lib/items/AttributeRelation.php +++ b/src/lib/items/AttributeRelation.php @@ -40,8 +40,7 @@ public function __construct( ?string $className = null, ?string $method = null, array $link = [] - ) - { + ) { $this->name = $name; $this->tableName = $tableName; $this->className = $className; From e499530c80e7d58c306cb637a6873601e721397a Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Wed, 21 Aug 2024 21:34:06 +0530 Subject: [PATCH 10/17] Implement --- src/generator/default/dbmodel.php | 8 ++++ src/lib/AttributeResolver.php | 57 ++++++++++++++++++++--------- src/lib/SchemaToDatabase.php | 20 +++++++++- src/lib/items/AttributeRelation.php | 15 +++++++- src/lib/items/DbModel.php | 2 +- 5 files changed, 81 insertions(+), 21 deletions(-) diff --git a/src/generator/default/dbmodel.php b/src/generator/default/dbmodel.php index d9c60ebc..24b7d98d 100644 --- a/src/generator/default/dbmodel.php +++ b/src/generator/default/dbmodel.php @@ -103,4 +103,12 @@ public function getgetCamelName() ?>() } +inverseRelations as $relationName => $relation): ?> + + public function getgetCamelName() ?>() + { + return $this->getMethod() ?>(\\getClassName() ?>::class, linkToString() ?>)->inverseOf('getClassName()) ?>'); + } + } diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index 74ef5e79..faa6029f 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -35,7 +35,8 @@ class AttributeResolver /** * @var AttributeRelation[]|array */ - private array $relations = []; + public array $relations = []; + /** * @var NonDbRelation[]|array */ @@ -59,6 +60,11 @@ class AttributeResolver private ?Config $config; + /** + * @var AttributeRelation[]|array + */ + public array $inverseRelations = []; + public function __construct(string $schemaName, ComponentSchema $schema, JunctionSchemas $junctions, ?Config $config = null) { $this->schemaName = $schemaName; @@ -94,22 +100,7 @@ public function resolve(): DbModel $this->resolveProperty($property, $isRequired, $nullableValue); } } - return Yii::createObject(DbModel::class, [ - [ - 'pkName' => $this->schema->getPkName(), - 'name' => $this->schemaName, - 'tableName' => $this->tableName, - 'description' => $this->schema->getDescription(), - 'attributes' => $this->attributes, - 'relations' => $this->relations, - 'nonDbRelations' => $this->nonDbRelations, - 'many2many' => $this->many2many, - 'indexes' => $this->prepareIndexes($this->schema->getIndexes()), - //For valid primary keys for junction tables - 'junctionCols' => $this->isJunctionSchema ? $this->junctions->junctionCols($this->schemaName) : [], - 'isNotDb' => $this->schema->isNonDb(), - ], - ]); + return $this->createDbModel(); } /** @@ -258,6 +249,14 @@ protected function resolveProperty( $relation->asSelfReference(); } $this->relations[$property->getName()] = $relation; + + $inverseRelation = Yii::createObject( + AttributeRelation::class, + [$this->schemaName, $this->tableName, $this->schemaName] + ) + ->asHasOne([$attribute->columnName => $fkProperty->getName()]); + $inverseRelation->setInverse(true); + $this->inverseRelations[$relatedClassName] = $inverseRelation; } if (!$property->isReference() && !$property->hasRefItems()) { [$min, $max] = $property->guessMinMax(); @@ -475,4 +474,28 @@ protected function resolvePropertyRef(PropertySchema $property, Attribute $attri $this->attributes[$property->getName()] = $attribute->setFakerStub($this->guessFakerStub($attribute, $fkProperty)); } + + /** + * @throws InvalidDefinitionException + * @throws InvalidConfigException + */ + public function createDbModel(): DbModel + { + return Yii::createObject(DbModel::class, [ + [ + 'pkName' => $this->schema->getPkName(), + 'name' => $this->schemaName, + 'tableName' => $this->tableName, + 'description' => $this->schema->getDescription(), + 'attributes' => $this->attributes, + 'relations' => $this->relations, + 'nonDbRelations' => $this->nonDbRelations, + 'many2many' => $this->many2many, + 'indexes' => $this->prepareIndexes($this->schema->getIndexes()), + //For valid primary keys for junction tables + 'junctionCols' => $this->isJunctionSchema ? $this->junctions->junctionCols($this->schemaName) : [], + 'isNotDb' => $this->schema->isNonDb(), + ], + ]); + } } diff --git a/src/lib/SchemaToDatabase.php b/src/lib/SchemaToDatabase.php index 8217513d..c13108a7 100644 --- a/src/lib/SchemaToDatabase.php +++ b/src/lib/SchemaToDatabase.php @@ -11,6 +11,7 @@ use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\exceptions\UnresolvableReferenceException; use cebe\yii2openapi\lib\exceptions\InvalidDefinitionException; +use cebe\yii2openapi\lib\items\AttributeRelation; use cebe\yii2openapi\lib\items\DbModel; use cebe\yii2openapi\lib\items\JunctionSchemas; use cebe\yii2openapi\lib\openapi\ComponentSchema; @@ -79,7 +80,7 @@ public function __construct(Config $config) */ public function prepareModels(): array { - $models = []; + $models = $resolvers = []; $openApi = $this->config->getOpenApi(); $junctions = $this->findJunctionSchemas(); foreach ($openApi->components->schemas as $schemaName => $openApiSchema) { @@ -93,8 +94,23 @@ public function prepareModels(): array } /**@var AttributeResolver $resolver */ $resolver = Yii::createObject(AttributeResolver::class, [$schemaName, $schema, $junctions, $this->config]); - $models[$schemaName] = $resolver->resolve(); + + // $models[$schemaName] = $resolver->resolve(); + $resolvers[$schemaName] = $resolver; + $models[$schemaName] = $resolvers[$schemaName]->resolve(); + } + + // handle inverse relation + foreach ($resolvers as $aResolver) { + /** @var AttributeResolver $aResolver */ + if ($aResolver->inverseRelations) { + foreach ($aResolver->inverseRelations as $name => $aRelation) { + /** @var AttributeRelation $aRelation */ + $models[$name]->inverseRelations[] = $aRelation; + } + } } + foreach ($models as $model) { foreach ($model->many2many as $relation) { if (isset($models[$relation->viaModelName])) { diff --git a/src/lib/items/AttributeRelation.php b/src/lib/items/AttributeRelation.php index 885abfb5..78fbd904 100644 --- a/src/lib/items/AttributeRelation.php +++ b/src/lib/items/AttributeRelation.php @@ -27,13 +27,15 @@ class AttributeRelation /** * hasOne/hasMany - **/ + */ private ?string $method; private array $link; private bool $selfReference = false; + private bool $inverse = false; + public function __construct( string $name, ?string $tableName = null, @@ -181,4 +183,15 @@ public function linkToString(): string preg_replace('~\s+~', '', VarDumper::export($this->getLink())) ); } + + public function setInverse(bool $inverse): AttributeRelation + { + $this->inverse = $inverse; + return $this; + } + + public function getInverse(): bool + { + return $this->inverse; + } } diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 58691c22..394637fe 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -51,7 +51,7 @@ class DbModel extends BaseObject */ public array $relations = []; - /*** + /** * @var array|NonDbRelation[] non-db relations */ public array $nonDbRelations = []; From 5dda2371eabe21c69e37b73c165af92207f9e7ea Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Fri, 23 Aug 2024 20:31:56 +0530 Subject: [PATCH 11/17] Handle multiple relations and add more tests --- src/generator/default/dbmodel.php | 12 ++++---- src/lib/AttributeResolver.php | 29 +++++++++++++------ src/lib/SchemaToDatabase.php | 8 +++-- src/lib/items/AttributeRelation.php | 6 ++-- .../25_generate_inverse_relations/index.yaml | 10 +++++++ 5 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/generator/default/dbmodel.php b/src/generator/default/dbmodel.php index 24b7d98d..bf04a385 100644 --- a/src/generator/default/dbmodel.php +++ b/src/generator/default/dbmodel.php @@ -87,7 +87,7 @@ public function rules() public function getgetCamelName() ?>() { return $this->getMethod() ?>(\\getClassName() ?>::class, linkToString()?>); + echo $relation->linkToString()?>)getInverse() ? '->inverseOf(\''.$relation->getInverse().'\')' : '' ?>; } many2many as $relation): ?> @@ -103,12 +103,12 @@ public function getgetCamelName() ?>() } -inverseRelations as $relationName => $relation): ?> +inverseRelations as $relationName => $relation): ?> - public function getgetCamelName() ?>() + public function getgetCamelName().($i===1 ? '' : $i) ?>() { - return $this->getMethod() ?>(\\getClassName() ?>::class, linkToString() ?>)->inverseOf('getClassName()) ?>'); + return $this->getMethod() ?>(\\getClassName() ?>::class, linkToString() ?>)->inverseOf('getInverse() ?>'); } - + } diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index faa6029f..27b305ea 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -249,14 +249,7 @@ protected function resolveProperty( $relation->asSelfReference(); } $this->relations[$property->getName()] = $relation; - - $inverseRelation = Yii::createObject( - AttributeRelation::class, - [$this->schemaName, $this->tableName, $this->schemaName] - ) - ->asHasOne([$attribute->columnName => $fkProperty->getName()]); - $inverseRelation->setInverse(true); - $this->inverseRelations[$relatedClassName] = $inverseRelation; + $this->addInverseRelation($relatedClassName, $attribute, $property, $fkProperty); } if (!$property->isReference() && !$property->hasRefItems()) { [$min, $max] = $property->guessMinMax(); @@ -329,7 +322,8 @@ protected function resolveProperty( AttributeRelation::class, [$property->getName(), $relatedTableName, $relatedClassName] ) - ->asHasMany([Inflector::camel2id($this->schemaName, '_') . '_id' => $this->schema->getPkName()]); + ->asHasMany([Inflector::camel2id($this->schemaName, '_') . '_id' => $this->schema->getPkName()]) + ->setInverse(Inflector::variablize($this->schemaName)); return; } if ($this->schema->isNonDb() && $attribute->isReference()) { @@ -498,4 +492,21 @@ public function createDbModel(): DbModel ], ]); } + + public function addInverseRelation( + string $relatedClassName, + Attribute $attribute, + PropertySchema $property, + PropertySchema $fkProperty + ): void + { + $inverseRelation = Yii::createObject( + AttributeRelation::class, + [$this->schemaName, $this->tableName, $this->schemaName] + ) + ->asHasOne([$attribute->columnName => $fkProperty->getName()]); + $inverseRelation->setInverse($property->getName()); + $this->inverseRelations[$relatedClassName][] = $inverseRelation; + + } } diff --git a/src/lib/SchemaToDatabase.php b/src/lib/SchemaToDatabase.php index c13108a7..4e5737eb 100644 --- a/src/lib/SchemaToDatabase.php +++ b/src/lib/SchemaToDatabase.php @@ -104,9 +104,11 @@ public function prepareModels(): array foreach ($resolvers as $aResolver) { /** @var AttributeResolver $aResolver */ if ($aResolver->inverseRelations) { - foreach ($aResolver->inverseRelations as $name => $aRelation) { - /** @var AttributeRelation $aRelation */ - $models[$name]->inverseRelations[] = $aRelation; + foreach ($aResolver->inverseRelations as $name => $relations) { + foreach ($relations as $relation) { + /** @var AttributeRelation $relation */ + $models[$name]->inverseRelations[] = $relation; + } } } } diff --git a/src/lib/items/AttributeRelation.php b/src/lib/items/AttributeRelation.php index 78fbd904..31acbd2c 100644 --- a/src/lib/items/AttributeRelation.php +++ b/src/lib/items/AttributeRelation.php @@ -34,7 +34,7 @@ class AttributeRelation private bool $selfReference = false; - private bool $inverse = false; + private ?string $inverse = null; public function __construct( string $name, @@ -184,13 +184,13 @@ public function linkToString(): string ); } - public function setInverse(bool $inverse): AttributeRelation + public function setInverse(string $inverse): AttributeRelation { $this->inverse = $inverse; return $this; } - public function getInverse(): bool + public function getInverse(): ?string { return $this->inverse; } diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml b/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml index f1e9f59e..1d7c79fe 100644 --- a/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml +++ b/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml @@ -19,6 +19,10 @@ components: name: type: string maxLength: 128 + accounts: + type: array + items: + $ref: '#/components/schemas/Account' Account: description: Account @@ -38,6 +42,12 @@ components: type: string user: $ref: '#/components/schemas/User' + user2: # copy of user (not one to many) + $ref: '#/components/schemas/User' + user3: # copy of user (not one to many) + allOf: + - $ref: '#/components/schemas/User' + - x-fk-column-name: user3 paths: '/account': From a168e07cdb7d4c8a1b9201ce61210f1493517280 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sun, 25 Aug 2024 15:23:31 +0530 Subject: [PATCH 12/17] Avoid inverse relation for self reference --- src/lib/AttributeResolver.php | 8 ++++---- .../25_generate_inverse_relations/index.yaml | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index 27b305ea..b68b52c9 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -249,7 +249,9 @@ protected function resolveProperty( $relation->asSelfReference(); } $this->relations[$property->getName()] = $relation; - $this->addInverseRelation($relatedClassName, $attribute, $property, $fkProperty); + if (!$property->isRefPointerToSelf()) { + $this->addInverseRelation($relatedClassName, $attribute, $property, $fkProperty); + } } if (!$property->isReference() && !$property->hasRefItems()) { [$min, $max] = $property->guessMinMax(); @@ -498,8 +500,7 @@ public function addInverseRelation( Attribute $attribute, PropertySchema $property, PropertySchema $fkProperty - ): void - { + ): void { $inverseRelation = Yii::createObject( AttributeRelation::class, [$this->schemaName, $this->tableName, $this->schemaName] @@ -507,6 +508,5 @@ public function addInverseRelation( ->asHasOne([$attribute->columnName => $fkProperty->getName()]); $inverseRelation->setInverse($property->getName()); $this->inverseRelations[$relatedClassName][] = $inverseRelation; - } } diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml b/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml index 1d7c79fe..b6f5ded3 100644 --- a/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml +++ b/tests/specs/issue_fix/25_generate_inverse_relations/index.yaml @@ -49,6 +49,26 @@ components: - $ref: '#/components/schemas/User' - x-fk-column-name: user3 + Menu: + required: + - id + - name + properties: + id: + type: integer + format: int64 + readOnly: True + name: + type: string + maxLength: 100 + minLength: 3 + parent: + $ref: '#/components/schemas/Menu/properties/id' + childes: + type: array + items: + $ref: '#/components/schemas/Menu/properties/parent' + paths: '/account': get: From 51043121da1c7a81be94b2b05293c7214d7d99ea Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sun, 25 Aug 2024 15:34:01 +0530 Subject: [PATCH 13/17] Refactor --- TODO.taskpaper | 4 ++-- src/lib/AttributeResolver.php | 39 +++++++++++++++-------------------- src/lib/SchemaToDatabase.php | 18 +++++++++------- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/TODO.taskpaper b/TODO.taskpaper index 07c175cc..3d082f94 100644 --- a/TODO.taskpaper +++ b/TODO.taskpaper @@ -1,8 +1,8 @@ TODO.taskpaper ### Generate inverse relations #25 - ☐ create failing test - ☐ implement the solution + ✔ create failing test @done (24-08-25 15:25) + ✔ implement the solution @done (24-08-25 15:25) ☐ fix failing tests if any ☐ resolve TODOs if any ☐ review PR diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index b68b52c9..d25a28c2 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -100,7 +100,23 @@ public function resolve(): DbModel $this->resolveProperty($property, $isRequired, $nullableValue); } } - return $this->createDbModel(); + + return Yii::createObject(DbModel::class, [ + [ + 'pkName' => $this->schema->getPkName(), + 'name' => $this->schemaName, + 'tableName' => $this->tableName, + 'description' => $this->schema->getDescription(), + 'attributes' => $this->attributes, + 'relations' => $this->relations, + 'nonDbRelations' => $this->nonDbRelations, + 'many2many' => $this->many2many, + 'indexes' => $this->prepareIndexes($this->schema->getIndexes()), + //For valid primary keys for junction tables + 'junctionCols' => $this->isJunctionSchema ? $this->junctions->junctionCols($this->schemaName) : [], + 'isNotDb' => $this->schema->isNonDb(), + ], + ]); } /** @@ -472,29 +488,8 @@ protected function resolvePropertyRef(PropertySchema $property, Attribute $attri } /** - * @throws InvalidDefinitionException * @throws InvalidConfigException */ - public function createDbModel(): DbModel - { - return Yii::createObject(DbModel::class, [ - [ - 'pkName' => $this->schema->getPkName(), - 'name' => $this->schemaName, - 'tableName' => $this->tableName, - 'description' => $this->schema->getDescription(), - 'attributes' => $this->attributes, - 'relations' => $this->relations, - 'nonDbRelations' => $this->nonDbRelations, - 'many2many' => $this->many2many, - 'indexes' => $this->prepareIndexes($this->schema->getIndexes()), - //For valid primary keys for junction tables - 'junctionCols' => $this->isJunctionSchema ? $this->junctions->junctionCols($this->schemaName) : [], - 'isNotDb' => $this->schema->isNonDb(), - ], - ]); - } - public function addInverseRelation( string $relatedClassName, Attribute $attribute, diff --git a/src/lib/SchemaToDatabase.php b/src/lib/SchemaToDatabase.php index 4e5737eb..dae10450 100644 --- a/src/lib/SchemaToDatabase.php +++ b/src/lib/SchemaToDatabase.php @@ -80,7 +80,12 @@ public function __construct(Config $config) */ public function prepareModels(): array { - $models = $resolvers = []; + /** @var DbModel[] $models */ + $models = []; + + /** @var AttributeResolver[] $resolvers */ + $resolvers = []; + $openApi = $this->config->getOpenApi(); $junctions = $this->findJunctionSchemas(); foreach ($openApi->components->schemas as $schemaName => $openApiSchema) { @@ -102,13 +107,10 @@ public function prepareModels(): array // handle inverse relation foreach ($resolvers as $aResolver) { - /** @var AttributeResolver $aResolver */ - if ($aResolver->inverseRelations) { - foreach ($aResolver->inverseRelations as $name => $relations) { - foreach ($relations as $relation) { - /** @var AttributeRelation $relation */ - $models[$name]->inverseRelations[] = $relation; - } + foreach ($aResolver->inverseRelations as $name => $relations) { + foreach ($relations as $relation) { + /** @var AttributeRelation $relation */ + $models[$name]->inverseRelations[] = $relation; } } } From 6f2d85f51cd02bcaad0a787ddf20ba9645d604a1 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 26 Aug 2024 13:52:38 +0530 Subject: [PATCH 14/17] Fix failing test - RelationsInFakerTest::testIndex --- src/lib/SchemaToDatabase.php | 2 -- src/lib/items/DbModel.php | 2 +- .../specs/relations_in_faker/app/models/base/A123.php | 5 +++++ .../relations_in_faker/app/models/base/Account.php | 5 +++++ .../specs/relations_in_faker/app/models/base/B123.php | 10 ++++++++++ .../specs/relations_in_faker/app/models/base/C123.php | 5 +++++ .../specs/relations_in_faker/app/models/base/D123.php | 5 +++++ .../relations_in_faker/app/models/base/Domain.php | 7 ++++++- .../relations_in_faker/app/models/base/Routing.php | 2 +- tests/specs/relations_in_faker/relations_in_faker.yaml | 2 +- 10 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/lib/SchemaToDatabase.php b/src/lib/SchemaToDatabase.php index dae10450..af2521b2 100644 --- a/src/lib/SchemaToDatabase.php +++ b/src/lib/SchemaToDatabase.php @@ -125,8 +125,6 @@ public function prepareModels(): array } } - // TODO generate inverse relations - return $models; } diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 394637fe..5f3c8c4e 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -62,7 +62,7 @@ class DbModel extends BaseObject public array $many2many = []; /** - * @var array|AttributeRelation[] inverse relations # TODO + * @var array|AttributeRelation[] inverse relations */ public array $inverseRelations = []; diff --git a/tests/specs/relations_in_faker/app/models/base/A123.php b/tests/specs/relations_in_faker/app/models/base/A123.php index 9836dc3b..7b604bad 100644 --- a/tests/specs/relations_in_faker/app/models/base/A123.php +++ b/tests/specs/relations_in_faker/app/models/base/A123.php @@ -32,4 +32,9 @@ public function getB123() { return $this->hasOne(\app\models\B123::class, ['id' => 'b123_id']); } + + public function getRouting() + { + return $this->hasOne(\app\models\Routing::class, ['a123_id' => 'id'])->inverseOf('a123'); + } } diff --git a/tests/specs/relations_in_faker/app/models/base/Account.php b/tests/specs/relations_in_faker/app/models/base/Account.php index 0ee4adc0..07d561cd 100644 --- a/tests/specs/relations_in_faker/app/models/base/Account.php +++ b/tests/specs/relations_in_faker/app/models/base/Account.php @@ -24,4 +24,9 @@ public function rules() 'name_string' => [['name'], 'string', 'max' => 40], ]; } + + public function getDomain() + { + return $this->hasOne(\app\models\Domain::class, ['account_id' => 'id'])->inverseOf('account'); + } } diff --git a/tests/specs/relations_in_faker/app/models/base/B123.php b/tests/specs/relations_in_faker/app/models/base/B123.php index f05fe320..2fdde13d 100644 --- a/tests/specs/relations_in_faker/app/models/base/B123.php +++ b/tests/specs/relations_in_faker/app/models/base/B123.php @@ -32,4 +32,14 @@ public function getC123() { return $this->hasOne(\app\models\C123::class, ['id' => 'c123_id']); } + + public function getA123() + { + return $this->hasOne(\app\models\A123::class, ['b123_id' => 'id'])->inverseOf('b123'); + } + + public function getE1232() + { + return $this->hasOne(\app\models\E123::class, ['b123_id' => 'id'])->inverseOf('b123'); + } } diff --git a/tests/specs/relations_in_faker/app/models/base/C123.php b/tests/specs/relations_in_faker/app/models/base/C123.php index faa3f1e5..7e074ea9 100644 --- a/tests/specs/relations_in_faker/app/models/base/C123.php +++ b/tests/specs/relations_in_faker/app/models/base/C123.php @@ -23,4 +23,9 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + public function getB123() + { + return $this->hasOne(\app\models\B123::class, ['c123_id' => 'id'])->inverseOf('c123'); + } } diff --git a/tests/specs/relations_in_faker/app/models/base/D123.php b/tests/specs/relations_in_faker/app/models/base/D123.php index a6050a8a..7cad2f34 100644 --- a/tests/specs/relations_in_faker/app/models/base/D123.php +++ b/tests/specs/relations_in_faker/app/models/base/D123.php @@ -23,4 +23,9 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + public function getRouting() + { + return $this->hasOne(\app\models\Routing::class, ['d123_id' => 'id'])->inverseOf('d123'); + } } diff --git a/tests/specs/relations_in_faker/app/models/base/Domain.php b/tests/specs/relations_in_faker/app/models/base/Domain.php index 3f861f9e..b7a807e1 100644 --- a/tests/specs/relations_in_faker/app/models/base/Domain.php +++ b/tests/specs/relations_in_faker/app/models/base/Domain.php @@ -38,6 +38,11 @@ public function getAccount() public function getRoutings() { - return $this->hasMany(\app\models\Routing::class, ['domain_id' => 'id']); + return $this->hasMany(\app\models\Routing::class, ['domain_id' => 'id'])->inverseOf('domain'); + } + + public function getRouting() + { + return $this->hasOne(\app\models\Routing::class, ['domain_id' => 'id'])->inverseOf('domain'); } } diff --git a/tests/specs/relations_in_faker/app/models/base/Routing.php b/tests/specs/relations_in_faker/app/models/base/Routing.php index 71a31b81..2aa9f710 100644 --- a/tests/specs/relations_in_faker/app/models/base/Routing.php +++ b/tests/specs/relations_in_faker/app/models/base/Routing.php @@ -3,7 +3,7 @@ namespace app\models\base; /** - * rounting specification + * routing specification * * @property int $id * @property int $domain_id domain diff --git a/tests/specs/relations_in_faker/relations_in_faker.yaml b/tests/specs/relations_in_faker/relations_in_faker.yaml index 65853bbd..546e9b1a 100644 --- a/tests/specs/relations_in_faker/relations_in_faker.yaml +++ b/tests/specs/relations_in_faker/relations_in_faker.yaml @@ -64,7 +64,7 @@ components: nullable: false Routing: - description: rounting specification + description: routing specification type: object required: - id From 65c9ef85e8cb8846265e68c43fa60bf80acace74 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 26 Aug 2024 15:06:09 +0530 Subject: [PATCH 15/17] Fix test - GeneratorTest and more --- tests/fixtures/blog.php | 4 ++-- tests/fixtures/non-db.php | 2 +- tests/specs/blog/models/base/Category.php | 7 ++++++- tests/specs/blog/models/base/Post.php | 7 ++++++- tests/specs/blog/models/base/User.php | 10 ++++++++++ tests/specs/blog_v2/models/base/Category.php | 7 ++++++- tests/specs/blog_v2/models/base/Post.php | 7 ++++++- tests/specs/blog_v2/models/base/User.php | 10 ++++++++++ tests/specs/fk_col_name/app/models/base/Delivery.php | 5 +++++ tests/specs/fk_col_name/app/models/base/User.php | 5 +++++ .../fk_col_name_index/app/models/base/Delivery.php | 10 ++++++++++ tests/specs/fk_col_name_index/app/models/base/User.php | 5 +++++ tests/specs/petstore/models/base/Store.php | 5 +++++ tests/specs/petstore_jsonapi/models/base/Pet.php | 5 +++++ tests/specs/petstore_namespace/mymodels/base/Store.php | 5 +++++ 15 files changed, 87 insertions(+), 7 deletions(-) diff --git a/tests/fixtures/blog.php b/tests/fixtures/blog.php index 74b04000..5d6d0b49 100644 --- a/tests/fixtures/blog.php +++ b/tests/fixtures/blog.php @@ -48,7 +48,7 @@ ->setRequired()->setDefault(false)->setFakerStub('$faker->boolean'), ], 'relations' => [ - 'posts' => new AttributeRelation('posts', 'blog_posts', 'Post', 'hasMany', ['category_id' => 'id']), + 'posts' => (new AttributeRelation('posts', 'blog_posts', 'Post', 'hasMany', ['category_id' => 'id']))->setInverse('category'), ], 'indexes' => [ 'categories_active_index' => DbIndex::make('categories', ['active']), @@ -88,7 +88,7 @@ 'hasOne', ['id' => 'category_id']), 'created_by' => new AttributeRelation('created_by', 'users', 'User', 'hasOne', ['id' => 'created_by_id']), - 'comments' => new AttributeRelation('comments', 'post_comments', 'Comment', 'hasMany', ['post_id' => 'uid']), + 'comments' => (new AttributeRelation('comments', 'post_comments', 'Comment', 'hasMany', ['post_id' => 'uid']))->setInverse('post'), ], 'indexes' => [ 'blog_posts_title_key' => DbIndex::make('blog_posts', ['title'], null, true), diff --git a/tests/fixtures/non-db.php b/tests/fixtures/non-db.php index 220a486d..0e4fb69d 100644 --- a/tests/fixtures/non-db.php +++ b/tests/fixtures/non-db.php @@ -25,7 +25,7 @@ ], 'relations' => [ 'parentPet' => new AttributeRelation('parentPet', 'pets', 'Pet', 'hasOne', ['id' => 'parentPet_id']), - 'favoritePets' => new AttributeRelation('favoritePets', 'pets', 'Pet', 'hasMany', ['pet_statistic_id' => 'id']), + 'favoritePets' => (new AttributeRelation('favoritePets', 'pets', 'Pet', 'hasMany', ['pet_statistic_id' => 'id']))->setInverse('petStatistic'), ], 'nonDbRelations' => [ 'topDoctors' => new NonDbRelation('topDoctors', 'Doctor', 'hasMany'), diff --git a/tests/specs/blog/models/base/Category.php b/tests/specs/blog/models/base/Category.php index f22e0014..fc11c09b 100644 --- a/tests/specs/blog/models/base/Category.php +++ b/tests/specs/blog/models/base/Category.php @@ -32,6 +32,11 @@ public function rules() public function getPosts() { - return $this->hasMany(\app\models\Post::class, ['category_id' => 'id']); + return $this->hasMany(\app\models\Post::class, ['category_id' => 'id'])->inverseOf('category'); + } + + public function getPost() + { + return $this->hasOne(\app\models\Post::class, ['category_id' => 'id'])->inverseOf('category'); } } diff --git a/tests/specs/blog/models/base/Post.php b/tests/specs/blog/models/base/Post.php index 457978b5..abe61623 100644 --- a/tests/specs/blog/models/base/Post.php +++ b/tests/specs/blog/models/base/Post.php @@ -55,6 +55,11 @@ public function getCreatedBy() public function getComments() { - return $this->hasMany(\app\models\Comment::class, ['post_id' => 'uid']); + return $this->hasMany(\app\models\Comment::class, ['post_id' => 'uid'])->inverseOf('post'); + } + + public function getComment() + { + return $this->hasOne(\app\models\Comment::class, ['post_id' => 'uid'])->inverseOf('post'); } } diff --git a/tests/specs/blog/models/base/User.php b/tests/specs/blog/models/base/User.php index f0e484e8..dc94110c 100644 --- a/tests/specs/blog/models/base/User.php +++ b/tests/specs/blog/models/base/User.php @@ -39,4 +39,14 @@ public function rules() 'created_at_datetime' => [['created_at'], 'datetime', 'format' => 'php:Y-m-d H:i:s'], ]; } + + public function getPost() + { + return $this->hasOne(\app\models\Post::class, ['created_by_id' => 'id'])->inverseOf('created_by'); + } + + public function getComment2() + { + return $this->hasOne(\app\models\Comment::class, ['author_id' => 'id'])->inverseOf('author'); + } } diff --git a/tests/specs/blog_v2/models/base/Category.php b/tests/specs/blog_v2/models/base/Category.php index a70be3ff..23ed92ea 100644 --- a/tests/specs/blog_v2/models/base/Category.php +++ b/tests/specs/blog_v2/models/base/Category.php @@ -32,6 +32,11 @@ public function rules() public function getPosts() { - return $this->hasMany(\app\models\Post::class, ['category_id' => 'id']); + return $this->hasMany(\app\models\Post::class, ['category_id' => 'id'])->inverseOf('category'); + } + + public function getPost() + { + return $this->hasOne(\app\models\Post::class, ['category_id' => 'id'])->inverseOf('category'); } } diff --git a/tests/specs/blog_v2/models/base/Post.php b/tests/specs/blog_v2/models/base/Post.php index 44fe4275..83316fab 100644 --- a/tests/specs/blog_v2/models/base/Post.php +++ b/tests/specs/blog_v2/models/base/Post.php @@ -61,7 +61,7 @@ public function getCreatedBy() public function getComments() { - return $this->hasMany(\app\models\Comment::class, ['post_id' => 'id']); + return $this->hasMany(\app\models\Comment::class, ['post_id' => 'id'])->inverseOf('post'); } public function getTags() @@ -69,4 +69,9 @@ public function getTags() return $this->hasMany(\app\models\Tag::class, ['id' => 'tag_id']) ->viaTable('posts2tags', ['post_id' => 'id']); } + + public function getComment() + { + return $this->hasOne(\app\models\Comment::class, ['post_id' => 'id'])->inverseOf('post'); + } } diff --git a/tests/specs/blog_v2/models/base/User.php b/tests/specs/blog_v2/models/base/User.php index 195b5b5e..7274ae0a 100644 --- a/tests/specs/blog_v2/models/base/User.php +++ b/tests/specs/blog_v2/models/base/User.php @@ -43,4 +43,14 @@ public function rules() 'created_at_datetime' => [['created_at'], 'datetime', 'format' => 'php:Y-m-d H:i:s'], ]; } + + public function getPost() + { + return $this->hasOne(\app\models\Post::class, ['created_by_id' => 'id'])->inverseOf('created_by'); + } + + public function getComment2() + { + return $this->hasOne(\app\models\Comment::class, ['user_id' => 'id'])->inverseOf('user'); + } } diff --git a/tests/specs/fk_col_name/app/models/base/Delivery.php b/tests/specs/fk_col_name/app/models/base/Delivery.php index ae7cbd83..0380af0e 100644 --- a/tests/specs/fk_col_name/app/models/base/Delivery.php +++ b/tests/specs/fk_col_name/app/models/base/Delivery.php @@ -23,4 +23,9 @@ public function rules() 'title_string' => [['title'], 'string'], ]; } + + public function getWebhook() + { + return $this->hasOne(\app\models\Webhook::class, ['redelivery_of' => 'id'])->inverseOf('redelivery_of'); + } } diff --git a/tests/specs/fk_col_name/app/models/base/User.php b/tests/specs/fk_col_name/app/models/base/User.php index d76c3f4d..2c9c1c06 100644 --- a/tests/specs/fk_col_name/app/models/base/User.php +++ b/tests/specs/fk_col_name/app/models/base/User.php @@ -24,4 +24,9 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + public function getWebhook() + { + return $this->hasOne(\app\models\Webhook::class, ['user_id' => 'id'])->inverseOf('user'); + } } diff --git a/tests/specs/fk_col_name_index/app/models/base/Delivery.php b/tests/specs/fk_col_name_index/app/models/base/Delivery.php index ae7cbd83..b1f57fc1 100644 --- a/tests/specs/fk_col_name_index/app/models/base/Delivery.php +++ b/tests/specs/fk_col_name_index/app/models/base/Delivery.php @@ -23,4 +23,14 @@ public function rules() 'title_string' => [['title'], 'string'], ]; } + + public function getWebhook() + { + return $this->hasOne(\app\models\Webhook::class, ['redelivery_of' => 'id'])->inverseOf('redelivery_of'); + } + + public function getWebhook2() + { + return $this->hasOne(\app\models\Webhook::class, ['rd_abc_2' => 'id'])->inverseOf('rd2'); + } } diff --git a/tests/specs/fk_col_name_index/app/models/base/User.php b/tests/specs/fk_col_name_index/app/models/base/User.php index d76c3f4d..2c9c1c06 100644 --- a/tests/specs/fk_col_name_index/app/models/base/User.php +++ b/tests/specs/fk_col_name_index/app/models/base/User.php @@ -24,4 +24,9 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + public function getWebhook() + { + return $this->hasOne(\app\models\Webhook::class, ['user_id' => 'id'])->inverseOf('user'); + } } diff --git a/tests/specs/petstore/models/base/Store.php b/tests/specs/petstore/models/base/Store.php index b1f6bc0f..5e6696af 100644 --- a/tests/specs/petstore/models/base/Store.php +++ b/tests/specs/petstore/models/base/Store.php @@ -24,4 +24,9 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + public function getPet() + { + return $this->hasOne(\app\models\Pet::class, ['store_id' => 'id'])->inverseOf('store'); + } } diff --git a/tests/specs/petstore_jsonapi/models/base/Pet.php b/tests/specs/petstore_jsonapi/models/base/Pet.php index f0e3138a..251a39a4 100644 --- a/tests/specs/petstore_jsonapi/models/base/Pet.php +++ b/tests/specs/petstore_jsonapi/models/base/Pet.php @@ -59,4 +59,9 @@ public function getDuplicates() { return $this->hasMany(\app\models\Pet::class, ['tag' => 'tag']); } + + public function getPetStatistic() + { + return $this->hasOne(\app\models\PetStatistic::class, ['parentPet_id' => 'id'])->inverseOf('parentPet'); + } } diff --git a/tests/specs/petstore_namespace/mymodels/base/Store.php b/tests/specs/petstore_namespace/mymodels/base/Store.php index 6ecc3e47..fa304857 100644 --- a/tests/specs/petstore_namespace/mymodels/base/Store.php +++ b/tests/specs/petstore_namespace/mymodels/base/Store.php @@ -24,4 +24,9 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + public function getPet() + { + return $this->hasOne(\app\mymodels\Pet::class, ['store_id' => 'id'])->inverseOf('store'); + } } From 12a2a426d66bc74f860e19ca1badd8ca987c2945 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 26 Aug 2024 15:24:39 +0530 Subject: [PATCH 16/17] Fix other failing tests --- .../maria/models/base/Mailing.php | 5 +++++ .../app/models/base/Invoice.php | 5 +++++ .../pgsql/models/base/Account.php | 5 +++++ .../app/models/base/Account.php | 15 +++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/tests/specs/issue_fix/159_bug_giiapi_generated_rules_emailid/maria/models/base/Mailing.php b/tests/specs/issue_fix/159_bug_giiapi_generated_rules_emailid/maria/models/base/Mailing.php index 77049b23..a7d5017a 100644 --- a/tests/specs/issue_fix/159_bug_giiapi_generated_rules_emailid/maria/models/base/Mailing.php +++ b/tests/specs/issue_fix/159_bug_giiapi_generated_rules_emailid/maria/models/base/Mailing.php @@ -26,4 +26,9 @@ public function rules() 'paymentMethodName_string' => [['paymentMethodName'], 'string'], ]; } + + public function getContact() + { + return $this->hasOne(\app\models\Contact::class, ['mailing_id' => 'id'])->inverseOf('mailing'); + } } diff --git a/tests/specs/issue_fix/162_bug_dollarref_with_x_faker/app/models/base/Invoice.php b/tests/specs/issue_fix/162_bug_dollarref_with_x_faker/app/models/base/Invoice.php index 061e3508..4236c8eb 100644 --- a/tests/specs/issue_fix/162_bug_dollarref_with_x_faker/app/models/base/Invoice.php +++ b/tests/specs/issue_fix/162_bug_dollarref_with_x_faker/app/models/base/Invoice.php @@ -19,4 +19,9 @@ public function rules() { return []; } + + public function getOrder() + { + return $this->hasOne(\app\models\Order::class, ['invoice_id' => 'id'])->inverseOf('invoice'); + } } diff --git a/tests/specs/issue_fix/175_bug_allof_with_multiple_dollarrefs/pgsql/models/base/Account.php b/tests/specs/issue_fix/175_bug_allof_with_multiple_dollarrefs/pgsql/models/base/Account.php index f3dae5db..3c6d28b7 100644 --- a/tests/specs/issue_fix/175_bug_allof_with_multiple_dollarrefs/pgsql/models/base/Account.php +++ b/tests/specs/issue_fix/175_bug_allof_with_multiple_dollarrefs/pgsql/models/base/Account.php @@ -26,4 +26,9 @@ public function rules() 'paymentMethodName_string' => [['paymentMethodName'], 'string'], ]; } + + public function getContact() + { + return $this->hasOne(\app\models\Contact::class, ['account_id' => 'id'])->inverseOf('account'); + } } diff --git a/tests/specs/issue_fix/model_name_more_than_once_in_faker_148/app/models/base/Account.php b/tests/specs/issue_fix/model_name_more_than_once_in_faker_148/app/models/base/Account.php index 0ee4adc0..c3125556 100644 --- a/tests/specs/issue_fix/model_name_more_than_once_in_faker_148/app/models/base/Account.php +++ b/tests/specs/issue_fix/model_name_more_than_once_in_faker_148/app/models/base/Account.php @@ -24,4 +24,19 @@ public function rules() 'name_string' => [['name'], 'string', 'max' => 40], ]; } + + public function getE123() + { + return $this->hasOne(\app\models\E123::class, ['account_id' => 'id'])->inverseOf('account'); + } + + public function getE1232() + { + return $this->hasOne(\app\models\E123::class, ['account_2_id' => 'id'])->inverseOf('account_2'); + } + + public function getE1233() + { + return $this->hasOne(\app\models\E123::class, ['account_3_id' => 'id'])->inverseOf('account_3'); + } } From 1a7302e1af0ecc1259b9a015ab466ecf6adb6d80 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 26 Aug 2024 15:32:06 +0530 Subject: [PATCH 17/17] Complete the test --- TODO.taskpaper | 9 -- .../m200000_000000_create_table_users.php | 20 +++ .../m200000_000001_create_table_accounts.php | 30 ++++ .../m200000_000002_create_table_menus.php | 23 +++ .../mysql/models/Account.php | 10 ++ .../mysql/models/AccountFaker.php | 54 +++++++ .../mysql/models/BaseModelFaker.php | 144 ++++++++++++++++++ .../mysql/models/Menu.php | 10 ++ .../mysql/models/MenuFaker.php | 51 +++++++ .../mysql/models/User.php | 10 ++ .../mysql/models/UserFaker.php | 41 +++++ .../mysql/models/base/Account.php | 56 +++++++ .../mysql/models/base/Menu.php | 42 +++++ .../mysql/models/base/User.php | 48 ++++++ tests/unit/IssueFixTest.php | 16 +- 15 files changed, 547 insertions(+), 17 deletions(-) delete mode 100644 TODO.taskpaper create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/mysql/migrations_mysql_db/m200000_000000_create_table_users.php create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/mysql/migrations_mysql_db/m200000_000001_create_table_accounts.php create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/mysql/migrations_mysql_db/m200000_000002_create_table_menus.php create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/Account.php create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/AccountFaker.php create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/BaseModelFaker.php create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/Menu.php create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/MenuFaker.php create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/User.php create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/UserFaker.php create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/base/Account.php create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/base/Menu.php create mode 100644 tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/base/User.php diff --git a/TODO.taskpaper b/TODO.taskpaper deleted file mode 100644 index 3d082f94..00000000 --- a/TODO.taskpaper +++ /dev/null @@ -1,9 +0,0 @@ -TODO.taskpaper - -### Generate inverse relations #25 - ✔ create failing test @done (24-08-25 15:25) - ✔ implement the solution @done (24-08-25 15:25) - ☐ fix failing tests if any - ☐ resolve TODOs if any - ☐ review PR - ☐ delete this file and submit PR diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/mysql/migrations_mysql_db/m200000_000000_create_table_users.php b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/migrations_mysql_db/m200000_000000_create_table_users.php new file mode 100644 index 00000000..d151ed34 --- /dev/null +++ b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/migrations_mysql_db/m200000_000000_create_table_users.php @@ -0,0 +1,20 @@ +createTable('{{%users}}', [ + 'id' => $this->primaryKey(), + 'name' => $this->string(128)->notNull(), + ]); + } + + public function down() + { + $this->dropTable('{{%users}}'); + } +} diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/mysql/migrations_mysql_db/m200000_000001_create_table_accounts.php b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/migrations_mysql_db/m200000_000001_create_table_accounts.php new file mode 100644 index 00000000..f2bc7b90 --- /dev/null +++ b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/migrations_mysql_db/m200000_000001_create_table_accounts.php @@ -0,0 +1,30 @@ +createTable('{{%accounts}}', [ + 'id' => $this->primaryKey(), + 'name' => $this->string(128)->notNull(), + 'paymentMethodName' => $this->text()->null(), + 'user_id' => $this->integer()->null()->defaultValue(null), + 'user2_id' => $this->integer()->null()->defaultValue(null), + 'user3' => $this->integer()->null()->defaultValue(null), + ]); + $this->addForeignKey('fk_accounts_user_id_users_id', '{{%accounts}}', 'user_id', '{{%users}}', 'id'); + $this->addForeignKey('fk_accounts_user2_id_users_id', '{{%accounts}}', 'user2_id', '{{%users}}', 'id'); + $this->addForeignKey('fk_accounts_user3_users_id', '{{%accounts}}', 'user3', '{{%users}}', 'id'); + } + + public function down() + { + $this->dropForeignKey('fk_accounts_user3_users_id', '{{%accounts}}'); + $this->dropForeignKey('fk_accounts_user2_id_users_id', '{{%accounts}}'); + $this->dropForeignKey('fk_accounts_user_id_users_id', '{{%accounts}}'); + $this->dropTable('{{%accounts}}'); + } +} diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/mysql/migrations_mysql_db/m200000_000002_create_table_menus.php b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/migrations_mysql_db/m200000_000002_create_table_menus.php new file mode 100644 index 00000000..78011c88 --- /dev/null +++ b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/migrations_mysql_db/m200000_000002_create_table_menus.php @@ -0,0 +1,23 @@ +createTable('{{%menus}}', [ + 'id' => $this->bigPrimaryKey(), + 'name' => $this->string(100)->notNull(), + 'parent_id' => $this->bigInteger()->null()->defaultValue(null), + ]); + $this->addForeignKey('fk_menus_parent_id_menus_id', '{{%menus}}', 'parent_id', '{{%menus}}', 'id'); + } + + public function down() + { + $this->dropForeignKey('fk_menus_parent_id_menus_id', '{{%menus}}'); + $this->dropTable('{{%menus}}'); + } +} diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/Account.php b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/Account.php new file mode 100644 index 00000000..2d25d7fc --- /dev/null +++ b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/Account.php @@ -0,0 +1,10 @@ +generateModels(['author_id' => 1]); + * $model = (new PostFaker())->generateModels(function($model, $faker, $uniqueFaker) { + * $model->scenario = 'create'; + * $model->author_id = 1; + * return $model; + * }); + **/ + public function generateModel($attributes = []) + { + $faker = $this->faker; + $uniqueFaker = $this->uniqueFaker; + $model = new Account(); + //$model->id = $uniqueFaker->numberBetween(0, 1000000); + $model->name = substr($faker->text(128), 0, 128); + $model->paymentMethodName = $faker->sentence; + $model->user_id = $faker->randomElement(\app\models\User::find()->select("id")->column()); + $model->user2_id = $faker->randomElement(\app\models\User::find()->select("id")->column()); + $model->user3 = $faker->randomElement(\app\models\User::find()->select("id")->column()); + if (!is_callable($attributes)) { + $model->setAttributes($attributes, false); + } else { + $model = $attributes($model, $faker, $uniqueFaker); + } + return $model; + } + + public static function dependentOn() + { + return [ + // just model class names + 'User', + + ]; + } +} diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/BaseModelFaker.php b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/BaseModelFaker.php new file mode 100644 index 00000000..c367fbb4 --- /dev/null +++ b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/BaseModelFaker.php @@ -0,0 +1,144 @@ +faker = FakerFactory::create(str_replace('-', '_', \Yii::$app->language)); + $this->uniqueFaker = new UniqueGenerator($this->faker); + } + + abstract public function generateModel($attributes = []); + + public function getFaker():Generator + { + return $this->faker; + } + + public function getUniqueFaker():UniqueGenerator + { + return $this->uniqueFaker; + } + + public function setFaker(Generator $faker):void + { + $this->faker = $faker; + } + + public function setUniqueFaker(UniqueGenerator $faker):void + { + $this->uniqueFaker = $faker; + } + + /** + * Generate and return model + * @param array|callable $attributes + * @param UniqueGenerator|null $uniqueFaker + * @return \yii\db\ActiveRecord + * @example MyFaker::makeOne(['user_id' => 1, 'title' => 'foo']); + * @example MyFaker::makeOne( function($model, $faker) { + * $model->scenario = 'create'; + * $model->setAttributes(['user_id' => 1, 'title' => $faker->sentence]); + * return $model; + * }); + */ + public static function makeOne($attributes = [], ?UniqueGenerator $uniqueFaker = null) + { + $fakeBuilder = new static(); + if ($uniqueFaker !== null) { + $fakeBuilder->setUniqueFaker($uniqueFaker); + } + $model = $fakeBuilder->generateModel($attributes); + return $model; + } + + /** + * Generate, save and return model + * @param array|callable $attributes + * @param UniqueGenerator|null $uniqueFaker + * @return \yii\db\ActiveRecord + * @example MyFaker::saveOne(['user_id' => 1, 'title' => 'foo']); + * @example MyFaker::saveOne( function($model, $faker) { + * $model->scenario = 'create'; + * $model->setAttributes(['user_id' => 1, 'title' => $faker->sentence]); + * return $model; + * }); + */ + public static function saveOne($attributes = [], ?UniqueGenerator $uniqueFaker = null) + { + $model = static::makeOne($attributes, $uniqueFaker); + $model->save(); + return $model; + } + + /** + * Generate and return multiple models + * @param int $number + * @param array|callable $commonAttributes + * @return \yii\db\ActiveRecord[]|array + * @example TaskFaker::make(5, ['project_id'=>1, 'user_id' => 2]); + * @example TaskFaker::make(5, function($model, $faker, $uniqueFaker) { + * $model->setAttributes(['name' => $uniqueFaker->username, 'state'=>$faker->boolean(20)]); + * return $model; + * }); + */ + public static function make(int $number, $commonAttributes = [], ?UniqueGenerator $uniqueFaker = null):array + { + if ($number < 1) { + return []; + } + $fakeBuilder = new static(); + if ($uniqueFaker !== null) { + $fakeBuilder->setUniqueFaker($uniqueFaker); + } + return array_map(function () use ($commonAttributes, $fakeBuilder) { + $model = $fakeBuilder->generateModel($commonAttributes); + return $model; + }, range(0, $number -1)); + } + + /** + * Generate, save and return multiple models + * @param int $number + * @param array|callable $commonAttributes + * @return \yii\db\ActiveRecord[]|array + * @example TaskFaker::save(5, ['project_id'=>1, 'user_id' => 2]); + * @example TaskFaker::save(5, function($model, $faker, $uniqueFaker) { + * $model->setAttributes(['name' => $uniqueFaker->username, 'state'=>$faker->boolean(20)]); + * return $model; + * }); + */ + public static function save(int $number, $commonAttributes = [], ?UniqueGenerator $uniqueFaker = null):array + { + if ($number < 1) { + return []; + } + $fakeBuilder = new static(); + if ($uniqueFaker !== null) { + $fakeBuilder->setUniqueFaker($uniqueFaker); + } + return array_map(function () use ($commonAttributes, $fakeBuilder) { + $model = $fakeBuilder->generateModel($commonAttributes); + $model->save(); + return $model; + }, range(0, $number -1)); + } +} diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/Menu.php b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/Menu.php new file mode 100644 index 00000000..2b5867b4 --- /dev/null +++ b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/Menu.php @@ -0,0 +1,10 @@ +generateModels(['author_id' => 1]); + * $model = (new PostFaker())->generateModels(function($model, $faker, $uniqueFaker) { + * $model->scenario = 'create'; + * $model->author_id = 1; + * return $model; + * }); + **/ + public function generateModel($attributes = []) + { + $faker = $this->faker; + $uniqueFaker = $this->uniqueFaker; + $model = new Menu(); + //$model->id = $uniqueFaker->numberBetween(0, 1000000); + $model->name = substr($faker->text(100), 0, 100); + $model->parent_id = $faker->randomElement(\app\models\Menu::find()->select("id")->column()); + if (!is_callable($attributes)) { + $model->setAttributes($attributes, false); + } else { + $model = $attributes($model, $faker, $uniqueFaker); + } + return $model; + } + + public static function dependentOn() + { + return [ + // just model class names + 'Menu', + + ]; + } +} diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/User.php b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/User.php new file mode 100644 index 00000000..9b837d6e --- /dev/null +++ b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/User.php @@ -0,0 +1,10 @@ +generateModels(['author_id' => 1]); + * $model = (new PostFaker())->generateModels(function($model, $faker, $uniqueFaker) { + * $model->scenario = 'create'; + * $model->author_id = 1; + * return $model; + * }); + **/ + public function generateModel($attributes = []) + { + $faker = $this->faker; + $uniqueFaker = $this->uniqueFaker; + $model = new User(); + //$model->id = $uniqueFaker->numberBetween(0, 1000000); + $model->name = substr($faker->text(128), 0, 128); + if (!is_callable($attributes)) { + $model->setAttributes($attributes, false); + } else { + $model = $attributes($model, $faker, $uniqueFaker); + } + return $model; + } +} diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/base/Account.php b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/base/Account.php new file mode 100644 index 00000000..d0f43293 --- /dev/null +++ b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/base/Account.php @@ -0,0 +1,56 @@ + [['name', 'paymentMethodName'], 'trim'], + 'required' => [['name'], 'required'], + 'user_id_integer' => [['user_id'], 'integer'], + 'user_id_exist' => [['user_id'], 'exist', 'targetRelation' => 'User'], + 'user2_id_integer' => [['user2_id'], 'integer'], + 'user2_id_exist' => [['user2_id'], 'exist', 'targetRelation' => 'User2'], + 'user3_integer' => [['user3'], 'integer'], + 'user3_exist' => [['user3'], 'exist', 'targetRelation' => 'User3'], + 'name_string' => [['name'], 'string', 'max' => 128], + 'paymentMethodName_string' => [['paymentMethodName'], 'string'], + ]; + } + + public function getUser() + { + return $this->hasOne(\app\models\User::class, ['id' => 'user_id']); + } + + public function getUser2() + { + return $this->hasOne(\app\models\User::class, ['id' => 'user2_id']); + } + + public function getUser3() + { + return $this->hasOne(\app\models\User::class, ['id' => 'user3']); + } +} diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/base/Menu.php b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/base/Menu.php new file mode 100644 index 00000000..ec3d0957 --- /dev/null +++ b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/base/Menu.php @@ -0,0 +1,42 @@ + [['name'], 'trim'], + 'required' => [['name'], 'required'], + 'parent_id_integer' => [['parent_id'], 'integer'], + 'parent_id_exist' => [['parent_id'], 'exist', 'targetRelation' => 'Parent'], + 'name_string' => [['name'], 'string', 'min' => 3, 'max' => 100], + ]; + } + + public function getParent() + { + return $this->hasOne(\app\models\Menu::class, ['id' => 'parent_id']); + } + + public function getChildes() + { + return $this->hasMany(\app\models\Menu::class, ['parent_id' => 'id']); + } +} diff --git a/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/base/User.php b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/base/User.php new file mode 100644 index 00000000..5f70ce96 --- /dev/null +++ b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/base/User.php @@ -0,0 +1,48 @@ + [['name'], 'trim'], + 'required' => [['name'], 'required'], + 'name_string' => [['name'], 'string', 'max' => 128], + ]; + } + + public function getAccounts() + { + return $this->hasMany(\app\models\Account::class, ['user_id' => 'id'])->inverseOf('user'); + } + + public function getAccount() + { + return $this->hasOne(\app\models\Account::class, ['user_id' => 'id'])->inverseOf('user'); + } + + public function getAccount2() + { + return $this->hasOne(\app\models\Account::class, ['user2_id' => 'id'])->inverseOf('user2'); + } + + public function getAccount3() + { + return $this->hasOne(\app\models\Account::class, ['user3' => 'id'])->inverseOf('user3'); + } +} diff --git a/tests/unit/IssueFixTest.php b/tests/unit/IssueFixTest.php index dd20604a..6af7e11b 100644 --- a/tests/unit/IssueFixTest.php +++ b/tests/unit/IssueFixTest.php @@ -364,15 +364,15 @@ public function test158BugGiiapiGeneratedRulesEnumWithTrim() // https://github.com/php-openapi/yii2-openapi/issues/25 public function test25GenerateInverseRelations() { - // 25_generate_inverse_relations $testFile = Yii::getAlias("@specs/issue_fix/25_generate_inverse_relations/index.php"); $this->runGenerator($testFile); -// $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ -// 'recursive' => true, -// ]); -// $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/25_generate_inverse_relations/mysql"), [ -// 'recursive' => true, -// ]); -// $this->checkFiles($actualFiles, $expectedFiles); + $this->runActualMigrations('mysql', 3); + $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ + 'recursive' => true, + ]); + $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/25_generate_inverse_relations/mysql"), [ + 'recursive' => true, + ]); + $this->checkFiles($actualFiles, $expectedFiles); } }