From cbb5adb788a8eb333741a828f90e728dfea4ba0f Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Wed, 12 Feb 2025 15:41:38 +0530 Subject: [PATCH 01/20] Create PR --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f68df469..4cc1d72a 100644 --- a/README.md +++ b/README.md @@ -779,3 +779,4 @@ Professional support, consulting as well as software development services are av https://www.cebe.cc/en/contact Development of this library is sponsored by [cebe.:cloud: "Your Professional Deployment Platform"](https://cebe.cloud). + From ca8e804144cf3a13a03b480219694278d1bd099c Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Fri, 14 Feb 2025 15:28:15 +0530 Subject: [PATCH 02/20] Add passing test --- README.md | 1 - .../index.php | 13 ++ .../index.yaml | 24 +++ .../mysql/models/BaseModelFaker.php | 144 ++++++++++++++++++ .../mysql/models/Payment.php | 10 ++ .../mysql/models/PaymentFaker.php | 42 +++++ .../mysql/models/base/Payment.php | 31 ++++ tests/unit/IssueFixTest.php | 13 ++ 8 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/index.php create mode 100644 tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/index.yaml create mode 100644 tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/mysql/models/BaseModelFaker.php create mode 100644 tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/mysql/models/Payment.php create mode 100644 tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/mysql/models/PaymentFaker.php create mode 100644 tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/mysql/models/base/Payment.php diff --git a/README.md b/README.md index 4cc1d72a..f68df469 100644 --- a/README.md +++ b/README.md @@ -779,4 +779,3 @@ Professional support, consulting as well as software development services are av https://www.cebe.cc/en/contact Development of this library is sponsored by [cebe.:cloud: "Your Professional Deployment Platform"](https://cebe.cloud). - diff --git a/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/index.php b/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/index.php new file mode 100644 index 00000000..9a6fa0f2 --- /dev/null +++ b/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/index.php @@ -0,0 +1,13 @@ + '@specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/index.yaml', + 'generateUrls' => false, + 'generateModels' => true, + 'excludeModels' => [ + 'Error', + ], + 'generateControllers' => false, + 'generateMigrations' => false, + 'generateModelFaker' => true, // `generateModels` must be `true` in order to use `generateModelFaker` as `true` +]; diff --git a/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/index.yaml b/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/index.yaml new file mode 100644 index 00000000..8f748b8e --- /dev/null +++ b/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/index.yaml @@ -0,0 +1,24 @@ +openapi: 3.0.3 + +info: + title: '#78' + version: 1.0.0 + +paths: + /: + get: + responses: + '200': + description: The Response + +components: + schemas: + Payment: + properties: + id: + type: integer + amount: + type: integer + readOnly: true + currency: + type: string diff --git a/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/mysql/models/BaseModelFaker.php b/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/mysql/models/BaseModelFaker.php new file mode 100644 index 00000000..c367fbb4 --- /dev/null +++ b/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/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/78_properties_that_are_marked_as_readonly_are_not_read_only/mysql/models/Payment.php b/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/mysql/models/Payment.php new file mode 100644 index 00000000..660670f4 --- /dev/null +++ b/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/mysql/models/Payment.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 Payment(); + //$model->id = $uniqueFaker->numberBetween(0, 1000000); + $model->amount = $faker->numberBetween(0, 1000000); + $model->currency = $faker->currencyCode; + if (!is_callable($attributes)) { + $model->setAttributes($attributes, false); + } else { + $model = $attributes($model, $faker, $uniqueFaker); + } + return $model; + } +} diff --git a/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/mysql/models/base/Payment.php b/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/mysql/models/base/Payment.php new file mode 100644 index 00000000..129cce90 --- /dev/null +++ b/tests/specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/mysql/models/base/Payment.php @@ -0,0 +1,31 @@ + [['currency'], 'trim'], + 'currency_string' => [['currency'], 'string'], + ]; + } +} diff --git a/tests/unit/IssueFixTest.php b/tests/unit/IssueFixTest.php index 64ca764d..c2d8796c 100644 --- a/tests/unit/IssueFixTest.php +++ b/tests/unit/IssueFixTest.php @@ -1001,4 +1001,17 @@ public function test74InvalidSchemaReferenceError() $this->runActualMigrations(); } + // https://github.com/php-openapi/yii2-openapi/issues/78 + public function test78PropertiesThatAreMarkedAsReadonlyAreNotReadOnly() + { + $testFile = Yii::getAlias("@specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/index.php"); + $this->runGenerator($testFile); + $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ + 'recursive' => true, + ]); + $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/78_properties_that_are_marked_as_readonly_are_not_read_only/mysql"), [ + 'recursive' => true, + ]); + $this->checkFiles($actualFiles, $expectedFiles); + } } From b050abdc8afb3b33797fbba7a4e01f07222683ff Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sat, 1 Mar 2025 11:33:16 +0530 Subject: [PATCH 03/20] Failing test --- .../index.php | 13 +++++++ .../index.yaml | 33 ++++++++++++++++ .../mysql/models/Address.php | 10 +++++ .../mysql/models/Human.php | 10 +++++ .../mysql/models/base/Address.php | 30 ++++++++++++++ .../mysql/models/base/Human.php | 39 +++++++++++++++++++ tests/unit/IssueFixTest.php | 14 +++++++ 7 files changed, 149 insertions(+) create mode 100644 tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.php create mode 100644 tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.yaml create mode 100644 tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/Address.php create mode 100644 tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/Human.php create mode 100644 tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/base/Address.php create mode 100644 tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/base/Human.php diff --git a/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.php b/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.php new file mode 100644 index 00000000..59eb14b0 --- /dev/null +++ b/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.php @@ -0,0 +1,13 @@ + '@specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.yaml', + 'generateUrls' => false, + 'generateModels' => true, + 'excludeModels' => [ + 'Error', + ], + 'generateControllers' => false, + 'generateMigrations' => false, + 'generateModelFaker' => false, // `generateModels` must be `true` in order to use `generateModelFaker` as `true` +]; diff --git a/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.yaml b/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.yaml new file mode 100644 index 00000000..2e5ac94f --- /dev/null +++ b/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.yaml @@ -0,0 +1,33 @@ +openapi: "3.0.0" + +info: + version: 1.0.0 + title: '#88' + +paths: + /: + get: + responses: + '200': + description: The response + +components: + schemas: + Address: + type: object + properties: + id: + type: integer + name: + type: string + Human: + type: object + properties: + id: + type: integer + name: + type: string + address: + $ref: '#/components/schemas/Address' + + diff --git a/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/Address.php b/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/Address.php new file mode 100644 index 00000000..abfd7d59 --- /dev/null +++ b/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/Address.php @@ -0,0 +1,10 @@ + [['name'], 'trim'], + 'name_string' => [['name'], 'string'], + ]; + } +} diff --git a/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/base/Human.php b/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/base/Human.php new file mode 100644 index 00000000..dadaa276 --- /dev/null +++ b/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/base/Human.php @@ -0,0 +1,39 @@ + [['name'], 'trim'], + 'name_string' => [['name'], 'string'], + 'address_id_integer' => [['address_id'], 'integer'], + 'address_id_exist' => [['address_id'], 'exist', 'targetRelation' => 'address'], + ]; + } + + public function getAddress() + { + return $this->hasOne(\app\models\Address::class, ['id' => 'address_id']); + } +} diff --git a/tests/unit/IssueFixTest.php b/tests/unit/IssueFixTest.php index ad3ff8fd..c4af6abf 100644 --- a/tests/unit/IssueFixTest.php +++ b/tests/unit/IssueFixTest.php @@ -1014,4 +1014,18 @@ public function test22BugRulesRequiredIsGeneratedBeforeDefault() ]); $this->checkFiles($actualFiles, $expectedFiles); } + + // https://github.com/php-openapi/yii2-openapi/issues/88 + public function test88InCaseOfUpdatingAModelGeneratorCreatesRedundantInverseRelations() + { + $testFile = Yii::getAlias("@specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.php"); + $this->runGenerator($testFile); + $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ + 'recursive' => true, + ]); + $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql"), [ + 'recursive' => true, + ]); + $this->checkFiles($actualFiles, $expectedFiles); + } } From 45f3f9050b5a06c1f55f1db5032aaebb14475301 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sat, 1 Mar 2025 11:33:30 +0530 Subject: [PATCH 04/20] Fix --- src/lib/AttributeResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index cc41a6fb..2f917bdd 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -279,7 +279,7 @@ protected function resolveProperty( $relation->asSelfReference(); } $this->relations[$property->getName()] = $relation; - if (!$property->isRefPointerToSelf()) { + if (!$property->isRefPointerToSelf() && $property->hasRefItems()) { $this->addInverseRelation($relatedClassName, $attribute, $property, $fkProperty); } } From e69fc04d55f2e88844483872c8c2eb7d7bef46b5 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 3 Mar 2025 15:50:32 +0530 Subject: [PATCH 05/20] Fix failing tests --- tests/specs/blog/models/base/Category.php | 5 ----- tests/specs/blog/models/base/Post.php | 5 ----- tests/specs/blog/models/base/User.php | 10 ---------- tests/specs/blog_v2/models/base/Category.php | 5 ----- tests/specs/blog_v2/models/base/Post.php | 5 ----- tests/specs/blog_v2/models/base/User.php | 10 ---------- .../fk_col_name/app/models/base/Delivery.php | 5 ----- tests/specs/fk_col_name/app/models/base/User.php | 5 ----- .../app/models/base/Delivery.php | 10 ---------- .../fk_col_name_index/app/models/base/User.php | 5 ----- .../maria/models/base/Mailing.php | 5 ----- .../app/models/base/Invoice.php | 5 ----- .../pgsql/models/base/Account.php | 5 ----- .../mysql/models/base/User.php | 15 --------------- .../mysql/models/base/User.php | 5 ----- .../mysql/models/base/Animal.php | 5 ----- .../mysql/models/base/Fruit.php | 5 ----- .../mysql/models/base/User.php | 10 ---------- .../app/models/base/Account.php | 15 --------------- tests/specs/petstore/models/base/Store.php | 5 ----- tests/specs/petstore_jsonapi/models/base/Pet.php | 5 ----- .../petstore_namespace/mymodels/base/Store.php | 5 ----- .../relations_in_faker/app/models/base/A123.php | 5 ----- .../app/models/base/Account.php | 5 ----- .../relations_in_faker/app/models/base/B123.php | 10 ---------- .../relations_in_faker/app/models/base/C123.php | 5 ----- .../relations_in_faker/app/models/base/D123.php | 5 ----- .../relations_in_faker/app/models/base/Domain.php | 5 ----- 28 files changed, 185 deletions(-) diff --git a/tests/specs/blog/models/base/Category.php b/tests/specs/blog/models/base/Category.php index 188ec03c..36e7887a 100644 --- a/tests/specs/blog/models/base/Category.php +++ b/tests/specs/blog/models/base/Category.php @@ -38,9 +38,4 @@ public function getPosts() { 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 633de3f4..80393b08 100644 --- a/tests/specs/blog/models/base/Post.php +++ b/tests/specs/blog/models/base/Post.php @@ -61,9 +61,4 @@ public function getComments() { 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 dbd3a708..c0d73705 100644 --- a/tests/specs/blog/models/base/User.php +++ b/tests/specs/blog/models/base/User.php @@ -44,14 +44,4 @@ public function rules() 'email_unique' => [['email'], 'unique'], ]; } - - 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 4207f516..7744c88a 100644 --- a/tests/specs/blog_v2/models/base/Category.php +++ b/tests/specs/blog_v2/models/base/Category.php @@ -38,9 +38,4 @@ public function getPosts() { 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 debc5008..1913f0c2 100644 --- a/tests/specs/blog_v2/models/base/Post.php +++ b/tests/specs/blog_v2/models/base/Post.php @@ -73,9 +73,4 @@ 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 45c5b4e0..7123c988 100644 --- a/tests/specs/blog_v2/models/base/User.php +++ b/tests/specs/blog_v2/models/base/User.php @@ -47,14 +47,4 @@ public function rules() 'email_unique' => [['email'], 'unique'], ]; } - - 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 75a64400..214c5278 100644 --- a/tests/specs/fk_col_name/app/models/base/Delivery.php +++ b/tests/specs/fk_col_name/app/models/base/Delivery.php @@ -27,9 +27,4 @@ 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 ede2e7c0..9c127aeb 100644 --- a/tests/specs/fk_col_name/app/models/base/User.php +++ b/tests/specs/fk_col_name/app/models/base/User.php @@ -28,9 +28,4 @@ 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 a17531d1..214c5278 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 @@ -27,14 +27,4 @@ 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 ede2e7c0..9c127aeb 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 @@ -28,9 +28,4 @@ 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/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 57bb103f..ff8629b5 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 @@ -30,9 +30,4 @@ 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 cf4c0f73..21bfa112 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 @@ -23,9 +23,4 @@ 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 cd597631..e776fe40 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 @@ -30,9 +30,4 @@ 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/25_generate_inverse_relations/mysql/models/base/User.php b/tests/specs/issue_fix/25_generate_inverse_relations/mysql/models/base/User.php index 7e26bb25..0240e1e5 100644 --- 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 @@ -34,19 +34,4 @@ 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/specs/issue_fix/29_extension_fk_column_name_cause_error_in_case_of_column_name_without_underscore/mysql/models/base/User.php b/tests/specs/issue_fix/29_extension_fk_column_name_cause_error_in_case_of_column_name_without_underscore/mysql/models/base/User.php index 390d8bdb..bce4e105 100644 --- a/tests/specs/issue_fix/29_extension_fk_column_name_cause_error_in_case_of_column_name_without_underscore/mysql/models/base/User.php +++ b/tests/specs/issue_fix/29_extension_fk_column_name_cause_error_in_case_of_column_name_without_underscore/mysql/models/base/User.php @@ -27,9 +27,4 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } - - public function getPost() - { - return $this->hasOne(\app\models\Post::class, ['user' => 'id'])->inverseOf('user'); - } } diff --git a/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Animal.php b/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Animal.php index 81e157ac..a99b34da 100644 --- a/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Animal.php +++ b/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Animal.php @@ -27,9 +27,4 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } - - public function getInvoice() - { - return $this->hasOne(\app\models\Invoice::class, ['animal_id' => 'id'])->inverseOf('animal'); - } } diff --git a/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Fruit.php b/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Fruit.php index d56f106c..ccdbe894 100644 --- a/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Fruit.php +++ b/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Fruit.php @@ -27,9 +27,4 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } - - public function getInvoice() - { - return $this->hasOne(\app\models\Invoice::class, ['fruit_id' => 'id'])->inverseOf('fruit'); - } } diff --git a/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/User.php b/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/User.php index a6ce6e79..bce4e105 100644 --- a/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/User.php +++ b/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/User.php @@ -27,14 +27,4 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } - - public function getInvoice() - { - return $this->hasOne(\app\models\Invoice::class, ['user_id' => 'id'])->inverseOf('user'); - } - - public function getInvoice2() - { - return $this->hasOne(\app\models\Invoice::class, ['user_2_id' => 'id'])->inverseOf('user_2'); - } } 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 f875eadd..b90b713b 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 @@ -28,19 +28,4 @@ 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'); - } } diff --git a/tests/specs/petstore/models/base/Store.php b/tests/specs/petstore/models/base/Store.php index 9589d1b8..b450803e 100644 --- a/tests/specs/petstore/models/base/Store.php +++ b/tests/specs/petstore/models/base/Store.php @@ -28,9 +28,4 @@ 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 6444cf28..cf242429 100644 --- a/tests/specs/petstore_jsonapi/models/base/Pet.php +++ b/tests/specs/petstore_jsonapi/models/base/Pet.php @@ -63,9 +63,4 @@ 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 24d0f4e4..8ae6c907 100644 --- a/tests/specs/petstore_namespace/mymodels/base/Store.php +++ b/tests/specs/petstore_namespace/mymodels/base/Store.php @@ -28,9 +28,4 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } - - public function getPet() - { - return $this->hasOne(\app\mymodels\Pet::class, ['store_id' => 'id'])->inverseOf('store'); - } } 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 0c733222..fb8681d6 100644 --- a/tests/specs/relations_in_faker/app/models/base/A123.php +++ b/tests/specs/relations_in_faker/app/models/base/A123.php @@ -36,9 +36,4 @@ 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 e1198a95..b90b713b 100644 --- a/tests/specs/relations_in_faker/app/models/base/Account.php +++ b/tests/specs/relations_in_faker/app/models/base/Account.php @@ -28,9 +28,4 @@ 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 7ad4eb77..9fee3543 100644 --- a/tests/specs/relations_in_faker/app/models/base/B123.php +++ b/tests/specs/relations_in_faker/app/models/base/B123.php @@ -36,14 +36,4 @@ 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 6c5677dd..611526d8 100644 --- a/tests/specs/relations_in_faker/app/models/base/C123.php +++ b/tests/specs/relations_in_faker/app/models/base/C123.php @@ -27,9 +27,4 @@ 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 80a5017d..c45d7214 100644 --- a/tests/specs/relations_in_faker/app/models/base/D123.php +++ b/tests/specs/relations_in_faker/app/models/base/D123.php @@ -27,9 +27,4 @@ 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 df6e0822..3c5ca1b1 100644 --- a/tests/specs/relations_in_faker/app/models/base/Domain.php +++ b/tests/specs/relations_in_faker/app/models/base/Domain.php @@ -44,9 +44,4 @@ public function getRoutings() { 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'); - } } From ab6629bcfaa34d81d9fe47b6829b1736058c443a Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 3 Mar 2025 18:14:15 +0530 Subject: [PATCH 06/20] Create PR --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f68df469..4cc1d72a 100644 --- a/README.md +++ b/README.md @@ -779,3 +779,4 @@ Professional support, consulting as well as software development services are av https://www.cebe.cc/en/contact Development of this library is sponsored by [cebe.:cloud: "Your Professional Deployment Platform"](https://cebe.cloud). + From 207228b8b7e8bdb60ff171869ea73555d7afcbbe Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 3 Mar 2025 18:23:26 +0530 Subject: [PATCH 07/20] Add test stub --- README.md | 1 - .../index.php | 13 ++++++++++ .../index.yaml | 26 +++++++++++++++++++ tests/unit/IssueFixTest.php | 14 ++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/index.php create mode 100644 tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/index.yaml diff --git a/README.md b/README.md index 4cc1d72a..f68df469 100644 --- a/README.md +++ b/README.md @@ -779,4 +779,3 @@ Professional support, consulting as well as software development services are av https://www.cebe.cc/en/contact Development of this library is sponsored by [cebe.:cloud: "Your Professional Deployment Platform"](https://cebe.cloud). - diff --git a/tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/index.php b/tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/index.php new file mode 100644 index 00000000..2c6dfb9b --- /dev/null +++ b/tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/index.php @@ -0,0 +1,13 @@ + '@specs/issue_fix/90_implement_belongs_to_relations_in_models/index.yaml', + 'generateUrls' => false, + 'generateModels' => true, + 'excludeModels' => [ + 'Error', + ], + 'generateControllers' => false, + 'generateMigrations' => false, + 'generateModelFaker' => false, // `generateModels` must be `true` in order to use `generateModelFaker` as `true` +]; diff --git a/tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/index.yaml b/tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/index.yaml new file mode 100644 index 00000000..b62591f0 --- /dev/null +++ b/tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/index.yaml @@ -0,0 +1,26 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: \#90 +paths: + /: + get: + responses: + '200': + description: The information + +components: + schemas: + User: + type: object + properties: + id: + type: integer + + Address: + type: object + properties: + id: + type: integer + user: + $ref: '#/components/schemas/User' diff --git a/tests/unit/IssueFixTest.php b/tests/unit/IssueFixTest.php index ad3ff8fd..371c0657 100644 --- a/tests/unit/IssueFixTest.php +++ b/tests/unit/IssueFixTest.php @@ -1014,4 +1014,18 @@ public function test22BugRulesRequiredIsGeneratedBeforeDefault() ]); $this->checkFiles($actualFiles, $expectedFiles); } + + // https://github.com/php-openapi/yii2-openapi/issues/90 + public function test90ImplementBelongsToRelationsInModels() + { + $testFile = Yii::getAlias("@specs/issue_fix/90_implement_belongs_to_relations_in_models/index.php"); + $this->runGenerator($testFile); + $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ + 'recursive' => true, + ]); + $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql"), [ + 'recursive' => true, + ]); + $this->checkFiles($actualFiles, $expectedFiles); + } } From 4c409f75d2a94c4c19b930628357dfb279137f01 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Tue, 4 Mar 2025 12:07:10 +0530 Subject: [PATCH 08/20] WIP --- src/lib/AttributeResolver.php | 1 + tests/unit/IssueFixTest.php | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index cc41a6fb..a49a1b65 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -232,6 +232,7 @@ protected function resolveProperty( ->setForeignKeyColumnName($property->fkColName) ->setFakerStub($this->guessFakerStub($attribute, $property)) ->setTableName($this->componentSchema->resolveTableName($this->schemaName)); + if ($property->isReference()) { if ($property->isVirtual()) { throw new InvalidDefinitionException('References not supported for virtual attributes'); diff --git a/tests/unit/IssueFixTest.php b/tests/unit/IssueFixTest.php index 371c0657..dd1bc1c1 100644 --- a/tests/unit/IssueFixTest.php +++ b/tests/unit/IssueFixTest.php @@ -1020,12 +1020,12 @@ public function test90ImplementBelongsToRelationsInModels() { $testFile = Yii::getAlias("@specs/issue_fix/90_implement_belongs_to_relations_in_models/index.php"); $this->runGenerator($testFile); - $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ - 'recursive' => true, - ]); - $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql"), [ - 'recursive' => true, - ]); - $this->checkFiles($actualFiles, $expectedFiles); +// $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ +// 'recursive' => true, +// ]); +// $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql"), [ +// 'recursive' => true, +// ]); +// $this->checkFiles($actualFiles, $expectedFiles); } } From 2eda480c5d8863b7bad28280d584870be1799fc5 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Tue, 4 Mar 2025 12:27:26 +0530 Subject: [PATCH 09/20] Refactor --- src/lib/AttributeResolver.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index 2f917bdd..1a87557c 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -232,6 +232,7 @@ protected function resolveProperty( ->setForeignKeyColumnName($property->fkColName) ->setFakerStub($this->guessFakerStub($attribute, $property)) ->setTableName($this->componentSchema->resolveTableName($this->schemaName)); + if ($property->isReference()) { if ($property->isVirtual()) { throw new InvalidDefinitionException('References not supported for virtual attributes'); @@ -279,10 +280,8 @@ protected function resolveProperty( $relation->asSelfReference(); } $this->relations[$property->getName()] = $relation; - if (!$property->isRefPointerToSelf() && $property->hasRefItems()) { - $this->addInverseRelation($relatedClassName, $attribute, $property, $fkProperty); - } } + if (!$property->isReference() && !$property->hasRefItems()) { [$min, $max] = $property->guessMinMax(); $attribute->setIsVirtual($property->isVirtual()) @@ -337,7 +336,14 @@ protected function resolveProperty( ) ->asHasMany([$foreignPk => $this->componentSchema->getPkName()]); return; + } else { # handle inverse relation + if ($property->isReference()) { + $relatedClassName = $property->getRefClassName(); + $fkProperty = $property->getTargetProperty(); + $this->addInverseRelation($relatedClassName, $attribute, $property, $fkProperty); + } } + $relatedClassName = $property->getRefClassName(); $relatedTableName = $property->getRefSchema()->resolveTableName($relatedClassName); if ($this->catchManyToMany( From 12315315ef81ba1e6f50b61a1b7e2cab4f9081e5 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Tue, 4 Mar 2025 12:47:46 +0530 Subject: [PATCH 10/20] Refactor 2 --- src/generator/default/dbmodel.php | 8 -------- src/lib/AttributeResolver.php | 29 ----------------------------- src/lib/SchemaToDatabase.php | 11 ----------- src/lib/items/DbModel.php | 5 ----- 4 files changed, 53 deletions(-) diff --git a/src/generator/default/dbmodel.php b/src/generator/default/dbmodel.php index a85fa7bb..1f9d1b40 100644 --- a/src/generator/default/dbmodel.php +++ b/src/generator/default/dbmodel.php @@ -144,12 +144,4 @@ public function getgetCamelName() ?>() } -inverseRelations as $relationName => $relation): ?> - - public function getgetCamelName().($i===1 ? '' : $i) ?>() - { - return $this->getMethod() ?>(\\getClassName() ?>::class, linkToString() ?>)->inverseOf('getInverse() ?>'); - } - } diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index 1a87557c..b199e74c 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -60,11 +60,6 @@ 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; @@ -336,12 +331,6 @@ protected function resolveProperty( ) ->asHasMany([$foreignPk => $this->componentSchema->getPkName()]); return; - } else { # handle inverse relation - if ($property->isReference()) { - $relatedClassName = $property->getRefClassName(); - $fkProperty = $property->getTargetProperty(); - $this->addInverseRelation($relatedClassName, $attribute, $property, $fkProperty); - } } $relatedClassName = $property->getRefClassName(); @@ -524,22 +513,4 @@ public static function relationName(string $propertyName, ?string $fkColumnName) } return $relationName; } - - /** - * @throws InvalidConfigException - */ - 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 ccb230a3..2c784a54 100644 --- a/src/lib/SchemaToDatabase.php +++ b/src/lib/SchemaToDatabase.php @@ -106,21 +106,10 @@ public function prepareModels(): array /** @var AttributeResolver $resolver */ $resolver = Yii::createObject(AttributeResolver::class, [$schemaName, $schema, $junctions, $this->config]); - // $models[$schemaName] = $resolver->resolve(); $resolvers[$schemaName] = $resolver; $models[$schemaName] = $resolvers[$schemaName]->resolve(); } - // handle inverse relation - foreach ($resolvers as $aResolver) { - foreach ($aResolver->inverseRelations as $name => $relations) { - foreach ($relations as $relation) { - /** @var AttributeRelation $relation */ - $models[$name]->inverseRelations[] = $relation; - } - } - } - foreach ($models as $model) { foreach ($model->many2many as $relation) { if (isset($models[$relation->viaModelName])) { diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 08135d71..6ccceae9 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -67,11 +67,6 @@ class DbModel extends BaseObject */ public array $many2many = []; - /** - * @var array|AttributeRelation[] inverse relations - */ - public array $inverseRelations = []; - public array $junctionCols = []; /** From a69f43a4ac401b08826a96267c806b3b971c6b23 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Tue, 4 Mar 2025 14:46:50 +0530 Subject: [PATCH 11/20] Implement --- src/generator/default/dbmodel.php | 10 ++++++++++ src/lib/AttributeResolver.php | 12 ++++++++++++ src/lib/SchemaToDatabase.php | 10 ++++++++++ src/lib/items/DbModel.php | 9 +++++++-- 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/generator/default/dbmodel.php b/src/generator/default/dbmodel.php index 1f9d1b40..f975464f 100644 --- a/src/generator/default/dbmodel.php +++ b/src/generator/default/dbmodel.php @@ -144,4 +144,14 @@ public function getgetCamelName() ?>() } +belongsToRelations as $relationName => $relation): ?> + + # belongs to relation + public function getgetCamelName() . ($i === 1 ? '' : $i) ?>() + { + return $this->getMethod() ?>(\\getClassName() ?>::class, linkToString() ?>); + } + } diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index b199e74c..1e3f731a 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -37,6 +37,11 @@ class AttributeResolver */ public array $relations = []; + /** + * @var AttributeRelation[]|array + */ + public array $belongsToRelations = []; + /** * @var NonDbRelation[]|array */ @@ -273,6 +278,13 @@ protected function resolveProperty( $relation->onDeleteFkConstraint = $property->onDeleteFkConstraint; if ($property->isRefPointerToSelf()) { $relation->asSelfReference(); + } else { # belongs to relations https://github.com/php-openapi/yii2-openapi/issues/90 + $belongsToRelation = Yii::createObject( + AttributeRelation::class, + [$this->schemaName, $this->tableName, $this->schemaName] + ) + ->asHasOne([$attribute->columnName => $fkProperty->getName()]); + $this->belongsToRelations[$property->getRefClassName()][] = $belongsToRelation; } $this->relations[$property->getName()] = $relation; } diff --git a/src/lib/SchemaToDatabase.php b/src/lib/SchemaToDatabase.php index 2c784a54..929c8ee3 100644 --- a/src/lib/SchemaToDatabase.php +++ b/src/lib/SchemaToDatabase.php @@ -110,6 +110,16 @@ public function prepareModels(): array $models[$schemaName] = $resolvers[$schemaName]->resolve(); } + // handle belongs to relation + foreach ($resolvers as $aResolver) { + foreach ($aResolver->belongsToRelations as $name => $relations) { + foreach ($relations as $relation) { + /** @var AttributeRelation $relation */ + $models[$name]->belongsToRelations[] = $relation; + } + } + } + foreach ($models as $model) { foreach ($model->many2many as $relation) { if (isset($models[$relation->viaModelName])) { diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 6ccceae9..f314b8e8 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -86,9 +86,9 @@ class DbModel extends BaseObject public bool $descriptionIsComment = false; /** - * @var array Automatically generated scenarios from the model 'x-scenarios'. + * @var array|AttributeRelation[] belongs to relations */ - private $_scenarios; + public array $belongsToRelations = []; /** * @var bool @@ -98,6 +98,11 @@ class DbModel extends BaseObject */ public $drop = false; + /** + * @var array Automatically generated scenarios from the model 'x-scenarios'. + */ + private $_scenarios; + public function getTableAlias(): string { return '{{%' . $this->tableName . '}}'; From f92b7824be1225ef15585ab0967ab66f0143dd89 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Tue, 4 Mar 2025 14:50:52 +0530 Subject: [PATCH 12/20] Complete the test --- .../mysql/models/Address.php | 10 ++++++ .../mysql/models/User.php | 10 ++++++ .../mysql/models/base/Address.php | 36 +++++++++++++++++++ .../mysql/models/base/User.php | 32 +++++++++++++++++ tests/unit/IssueFixTest.php | 14 ++++---- 5 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql/models/Address.php create mode 100644 tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql/models/User.php create mode 100644 tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql/models/base/Address.php create mode 100644 tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql/models/base/User.php diff --git a/tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql/models/Address.php b/tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql/models/Address.php new file mode 100644 index 00000000..abfd7d59 --- /dev/null +++ b/tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql/models/Address.php @@ -0,0 +1,10 @@ + [['user_id'], 'integer'], + 'user_id_exist' => [['user_id'], 'exist', 'targetRelation' => 'user'], + ]; + } + + public function getUser() + { + return $this->hasOne(\app\models\User::class, ['id' => 'user_id']); + } +} diff --git a/tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql/models/base/User.php b/tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql/models/base/User.php new file mode 100644 index 00000000..a6a35b66 --- /dev/null +++ b/tests/specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql/models/base/User.php @@ -0,0 +1,32 @@ +hasOne(\app\models\Address::class, ['user_id' => 'id']); + } +} diff --git a/tests/unit/IssueFixTest.php b/tests/unit/IssueFixTest.php index 025128e6..f3f13a4b 100644 --- a/tests/unit/IssueFixTest.php +++ b/tests/unit/IssueFixTest.php @@ -1034,12 +1034,12 @@ public function test90ImplementBelongsToRelationsInModels() { $testFile = Yii::getAlias("@specs/issue_fix/90_implement_belongs_to_relations_in_models/index.php"); $this->runGenerator($testFile); -// $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ -// 'recursive' => true, -// ]); -// $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql"), [ -// 'recursive' => true, -// ]); -// $this->checkFiles($actualFiles, $expectedFiles); + $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ + 'recursive' => true, + ]); + $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/90_implement_belongs_to_relations_in_models/mysql"), [ + 'recursive' => true, + ]); + $this->checkFiles($actualFiles, $expectedFiles); } } From 5b1ec545c62e4d54b0e98023819c40d88428b7f3 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Tue, 4 Mar 2025 15:28:05 +0530 Subject: [PATCH 13/20] Fix failing tests --- src/generator/default/dbmodel.php | 8 ++++---- tests/specs/blog/models/base/Category.php | 6 ++++++ tests/specs/blog/models/base/Post.php | 6 ++++++ tests/specs/blog/models/base/User.php | 12 ++++++++++++ tests/specs/blog_v2/models/base/Category.php | 6 ++++++ tests/specs/blog_v2/models/base/Post.php | 6 ++++++ tests/specs/blog_v2/models/base/User.php | 12 ++++++++++++ .../fk_col_name/app/models/base/Delivery.php | 6 ++++++ .../specs/fk_col_name/app/models/base/User.php | 6 ++++++ .../app/models/base/Delivery.php | 12 ++++++++++++ .../fk_col_name_index/app/models/base/User.php | 6 ++++++ .../maria/models/base/Mailing.php | 6 ++++++ .../app/models/base/Invoice.php | 6 ++++++ .../pgsql/models/base/Account.php | 6 ++++++ .../mysql/models/base/User.php | 18 ++++++++++++++++++ .../mysql/models/base/User.php | 6 ++++++ .../mysql/models/base/Animal.php | 6 ++++++ .../mysql/models/base/Fruit.php | 6 ++++++ .../mysql/models/base/User.php | 12 ++++++++++++ .../index.yaml | 2 -- .../mysql/models/base/Address.php | 6 ++++++ .../app/models/base/Account.php | 18 ++++++++++++++++++ tests/specs/petstore/models/base/Store.php | 6 ++++++ .../specs/petstore_jsonapi/models/base/Pet.php | 6 ++++++ .../petstore_namespace/mymodels/base/Store.php | 6 ++++++ .../app/models/base/A123.php | 6 ++++++ .../app/models/base/Account.php | 6 ++++++ .../app/models/base/B123.php | 12 ++++++++++++ .../app/models/base/C123.php | 6 ++++++ .../app/models/base/D123.php | 6 ++++++ .../app/models/base/Domain.php | 6 ++++++ 31 files changed, 232 insertions(+), 6 deletions(-) diff --git a/src/generator/default/dbmodel.php b/src/generator/default/dbmodel.php index f975464f..448aa5ba 100644 --- a/src/generator/default/dbmodel.php +++ b/src/generator/default/dbmodel.php @@ -144,14 +144,14 @@ public function getgetCamelName() ?>() } -belongsToRelations as $relationName => $relation): ?> +belongsToRelations as $relationName => $relation): ?>getCamelName(), $usedRelationNames) ? $i : '' ?> # belongs to relation - public function getgetCamelName() . ($i === 1 ? '' : $i) ?>() + public function getgetCamelName() . ($number) ?>() { return $this->getMethod() ?>(\\getClassName() ?>::class, linkToString() ?>); } - +getCamelName(); endforeach; ?> } diff --git a/tests/specs/blog/models/base/Category.php b/tests/specs/blog/models/base/Category.php index 36e7887a..287bdc9f 100644 --- a/tests/specs/blog/models/base/Category.php +++ b/tests/specs/blog/models/base/Category.php @@ -38,4 +38,10 @@ public function getPosts() { return $this->hasMany(\app\models\Post::class, ['category_id' => 'id'])->inverseOf('category'); } + + # belongs to relation + public function getPost() + { + return $this->hasOne(\app\models\Post::class, ['category_id' => 'id']); + } } diff --git a/tests/specs/blog/models/base/Post.php b/tests/specs/blog/models/base/Post.php index 80393b08..2a97d0fa 100644 --- a/tests/specs/blog/models/base/Post.php +++ b/tests/specs/blog/models/base/Post.php @@ -61,4 +61,10 @@ public function getComments() { return $this->hasMany(\app\models\Comment::class, ['post_id' => 'uid'])->inverseOf('post'); } + + # belongs to relation + public function getComment() + { + return $this->hasOne(\app\models\Comment::class, ['post_id' => 'uid']); + } } diff --git a/tests/specs/blog/models/base/User.php b/tests/specs/blog/models/base/User.php index c0d73705..557328f9 100644 --- a/tests/specs/blog/models/base/User.php +++ b/tests/specs/blog/models/base/User.php @@ -44,4 +44,16 @@ public function rules() 'email_unique' => [['email'], 'unique'], ]; } + + # belongs to relation + public function getPost() + { + return $this->hasOne(\app\models\Post::class, ['created_by_id' => 'id']); + } + + # belongs to relation + public function getComment() + { + return $this->hasOne(\app\models\Comment::class, ['author_id' => 'id']); + } } diff --git a/tests/specs/blog_v2/models/base/Category.php b/tests/specs/blog_v2/models/base/Category.php index 7744c88a..1b97af75 100644 --- a/tests/specs/blog_v2/models/base/Category.php +++ b/tests/specs/blog_v2/models/base/Category.php @@ -38,4 +38,10 @@ public function getPosts() { return $this->hasMany(\app\models\Post::class, ['category_id' => 'id'])->inverseOf('category'); } + + # belongs to relation + public function getPost() + { + return $this->hasOne(\app\models\Post::class, ['category_id' => 'id']); + } } diff --git a/tests/specs/blog_v2/models/base/Post.php b/tests/specs/blog_v2/models/base/Post.php index 1913f0c2..0b82cefc 100644 --- a/tests/specs/blog_v2/models/base/Post.php +++ b/tests/specs/blog_v2/models/base/Post.php @@ -73,4 +73,10 @@ public function getTags() return $this->hasMany(\app\models\Tag::class, ['id' => 'tag_id']) ->viaTable('posts2tags', ['post_id' => 'id']); } + + # belongs to relation + public function getComment() + { + return $this->hasOne(\app\models\Comment::class, ['post_id' => 'id']); + } } diff --git a/tests/specs/blog_v2/models/base/User.php b/tests/specs/blog_v2/models/base/User.php index 7123c988..820b8259 100644 --- a/tests/specs/blog_v2/models/base/User.php +++ b/tests/specs/blog_v2/models/base/User.php @@ -47,4 +47,16 @@ public function rules() 'email_unique' => [['email'], 'unique'], ]; } + + # belongs to relation + public function getPost() + { + return $this->hasOne(\app\models\Post::class, ['created_by_id' => 'id']); + } + + # belongs to relation + public function getComment() + { + return $this->hasOne(\app\models\Comment::class, ['user_id' => 'id']); + } } 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 214c5278..3bb543a1 100644 --- a/tests/specs/fk_col_name/app/models/base/Delivery.php +++ b/tests/specs/fk_col_name/app/models/base/Delivery.php @@ -27,4 +27,10 @@ public function rules() 'title_string' => [['title'], 'string'], ]; } + + # belongs to relation + public function getWebhook() + { + return $this->hasOne(\app\models\Webhook::class, ['redelivery_of' => 'id']); + } } 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 9c127aeb..7847f799 100644 --- a/tests/specs/fk_col_name/app/models/base/User.php +++ b/tests/specs/fk_col_name/app/models/base/User.php @@ -28,4 +28,10 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + # belongs to relation + public function getWebhook() + { + return $this->hasOne(\app\models\Webhook::class, ['user_id' => 'id']); + } } 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 214c5278..98b9c801 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 @@ -27,4 +27,16 @@ public function rules() 'title_string' => [['title'], 'string'], ]; } + + # belongs to relation + public function getWebhook() + { + return $this->hasOne(\app\models\Webhook::class, ['redelivery_of' => 'id']); + } + + # belongs to relation + public function getWebhook2() + { + return $this->hasOne(\app\models\Webhook::class, ['rd_abc_2' => 'id']); + } } 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 9c127aeb..7847f799 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 @@ -28,4 +28,10 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + # belongs to relation + public function getWebhook() + { + return $this->hasOne(\app\models\Webhook::class, ['user_id' => 'id']); + } } 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 ff8629b5..2639a486 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 @@ -30,4 +30,10 @@ public function rules() 'paymentMethodName_string' => [['paymentMethodName'], 'string'], ]; } + + # belongs to relation + public function getContact() + { + return $this->hasOne(\app\models\Contact::class, ['mailing_id' => 'id']); + } } 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 21bfa112..1f685781 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 @@ -23,4 +23,10 @@ public function rules() { return []; } + + # belongs to relation + public function getOrder() + { + return $this->hasOne(\app\models\Order::class, ['invoice_id' => 'id']); + } } 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 e776fe40..079b93b9 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 @@ -30,4 +30,10 @@ public function rules() 'paymentMethodName_string' => [['paymentMethodName'], 'string'], ]; } + + # belongs to relation + public function getContact() + { + return $this->hasOne(\app\models\Contact::class, ['account_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 index 0240e1e5..c1b63234 100644 --- 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 @@ -34,4 +34,22 @@ public function getAccounts() { return $this->hasMany(\app\models\Account::class, ['user_id' => 'id'])->inverseOf('user'); } + + # belongs to relation + public function getAccount() + { + return $this->hasOne(\app\models\Account::class, ['user_id' => 'id']); + } + + # belongs to relation + public function getAccount2() + { + return $this->hasOne(\app\models\Account::class, ['user2_id' => 'id']); + } + + # belongs to relation + public function getAccount3() + { + return $this->hasOne(\app\models\Account::class, ['user3' => 'id']); + } } diff --git a/tests/specs/issue_fix/29_extension_fk_column_name_cause_error_in_case_of_column_name_without_underscore/mysql/models/base/User.php b/tests/specs/issue_fix/29_extension_fk_column_name_cause_error_in_case_of_column_name_without_underscore/mysql/models/base/User.php index bce4e105..547aa903 100644 --- a/tests/specs/issue_fix/29_extension_fk_column_name_cause_error_in_case_of_column_name_without_underscore/mysql/models/base/User.php +++ b/tests/specs/issue_fix/29_extension_fk_column_name_cause_error_in_case_of_column_name_without_underscore/mysql/models/base/User.php @@ -27,4 +27,10 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + # belongs to relation + public function getPost() + { + return $this->hasOne(\app\models\Post::class, ['user' => 'id']); + } } diff --git a/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Animal.php b/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Animal.php index a99b34da..bfd317f7 100644 --- a/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Animal.php +++ b/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Animal.php @@ -27,4 +27,10 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + # belongs to relation + public function getInvoice() + { + return $this->hasOne(\app\models\Invoice::class, ['animal_id' => 'id']); + } } diff --git a/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Fruit.php b/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Fruit.php index ccdbe894..ab5c4054 100644 --- a/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Fruit.php +++ b/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/Fruit.php @@ -27,4 +27,10 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + # belongs to relation + public function getInvoice() + { + return $this->hasOne(\app\models\Invoice::class, ['fruit_id' => 'id']); + } } diff --git a/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/User.php b/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/User.php index bce4e105..c132c14b 100644 --- a/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/User.php +++ b/tests/specs/issue_fix/52_bug_dependenton_allof_with_x_faker_false/mysql/models/base/User.php @@ -27,4 +27,16 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + # belongs to relation + public function getInvoice() + { + return $this->hasOne(\app\models\Invoice::class, ['user_id' => 'id']); + } + + # belongs to relation + public function getInvoice2() + { + return $this->hasOne(\app\models\Invoice::class, ['user_2_id' => 'id']); + } } diff --git a/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.yaml b/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.yaml index 2e5ac94f..8acbf164 100644 --- a/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.yaml +++ b/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/index.yaml @@ -29,5 +29,3 @@ components: type: string address: $ref: '#/components/schemas/Address' - - diff --git a/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/base/Address.php b/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/base/Address.php index aa298e27..e42f43c1 100644 --- a/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/base/Address.php +++ b/tests/specs/issue_fix/88_in_case_of_updating_a_model_generator_creates_redundant_inverse_relations/mysql/models/base/Address.php @@ -27,4 +27,10 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + # belongs to relation + public function getHuman() + { + return $this->hasOne(\app\models\Human::class, ['address_id' => 'id']); + } } 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 b90b713b..a296c4c0 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 @@ -28,4 +28,22 @@ public function rules() 'name_string' => [['name'], 'string', 'max' => 40], ]; } + + # belongs to relation + public function getE123() + { + return $this->hasOne(\app\models\E123::class, ['account_id' => 'id']); + } + + # belongs to relation + public function getE1232() + { + return $this->hasOne(\app\models\E123::class, ['account_2_id' => 'id']); + } + + # belongs to relation + public function getE1233() + { + return $this->hasOne(\app\models\E123::class, ['account_3_id' => 'id']); + } } diff --git a/tests/specs/petstore/models/base/Store.php b/tests/specs/petstore/models/base/Store.php index b450803e..d9eafaca 100644 --- a/tests/specs/petstore/models/base/Store.php +++ b/tests/specs/petstore/models/base/Store.php @@ -28,4 +28,10 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + # belongs to relation + public function getPet() + { + return $this->hasOne(\app\models\Pet::class, ['store_id' => 'id']); + } } diff --git a/tests/specs/petstore_jsonapi/models/base/Pet.php b/tests/specs/petstore_jsonapi/models/base/Pet.php index cf242429..0f057dc1 100644 --- a/tests/specs/petstore_jsonapi/models/base/Pet.php +++ b/tests/specs/petstore_jsonapi/models/base/Pet.php @@ -63,4 +63,10 @@ public function getDuplicates() { return $this->hasMany(\app\models\Pet::class, ['tag' => 'tag']); } + + # belongs to relation + public function getPetStatistic() + { + return $this->hasOne(\app\models\PetStatistic::class, ['parentPet_id' => 'id']); + } } diff --git a/tests/specs/petstore_namespace/mymodels/base/Store.php b/tests/specs/petstore_namespace/mymodels/base/Store.php index 8ae6c907..35aa907f 100644 --- a/tests/specs/petstore_namespace/mymodels/base/Store.php +++ b/tests/specs/petstore_namespace/mymodels/base/Store.php @@ -28,4 +28,10 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + # belongs to relation + public function getPet() + { + return $this->hasOne(\app\mymodels\Pet::class, ['store_id' => 'id']); + } } 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 fb8681d6..74d782f2 100644 --- a/tests/specs/relations_in_faker/app/models/base/A123.php +++ b/tests/specs/relations_in_faker/app/models/base/A123.php @@ -36,4 +36,10 @@ public function getB123() { return $this->hasOne(\app\models\B123::class, ['id' => 'b123_id']); } + + # belongs to relation + public function getRouting() + { + return $this->hasOne(\app\models\Routing::class, ['a123_id' => 'id']); + } } 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 b90b713b..c5bfedb1 100644 --- a/tests/specs/relations_in_faker/app/models/base/Account.php +++ b/tests/specs/relations_in_faker/app/models/base/Account.php @@ -28,4 +28,10 @@ public function rules() 'name_string' => [['name'], 'string', 'max' => 40], ]; } + + # belongs to relation + public function getDomain() + { + return $this->hasOne(\app\models\Domain::class, ['account_id' => 'id']); + } } 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 9fee3543..63727b75 100644 --- a/tests/specs/relations_in_faker/app/models/base/B123.php +++ b/tests/specs/relations_in_faker/app/models/base/B123.php @@ -36,4 +36,16 @@ public function getC123() { return $this->hasOne(\app\models\C123::class, ['id' => 'c123_id']); } + + # belongs to relation + public function getA123() + { + return $this->hasOne(\app\models\A123::class, ['b123_id' => 'id']); + } + + # belongs to relation + public function getE123() + { + return $this->hasOne(\app\models\E123::class, ['b123_id' => 'id']); + } } 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 611526d8..7f516f78 100644 --- a/tests/specs/relations_in_faker/app/models/base/C123.php +++ b/tests/specs/relations_in_faker/app/models/base/C123.php @@ -27,4 +27,10 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + # belongs to relation + public function getB123() + { + return $this->hasOne(\app\models\B123::class, ['c123_id' => 'id']); + } } 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 c45d7214..71555b18 100644 --- a/tests/specs/relations_in_faker/app/models/base/D123.php +++ b/tests/specs/relations_in_faker/app/models/base/D123.php @@ -27,4 +27,10 @@ public function rules() 'name_string' => [['name'], 'string'], ]; } + + # belongs to relation + public function getRouting() + { + return $this->hasOne(\app\models\Routing::class, ['d123_id' => 'id']); + } } 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 3c5ca1b1..fa563120 100644 --- a/tests/specs/relations_in_faker/app/models/base/Domain.php +++ b/tests/specs/relations_in_faker/app/models/base/Domain.php @@ -44,4 +44,10 @@ public function getRoutings() { return $this->hasMany(\app\models\Routing::class, ['domain_id' => 'id'])->inverseOf('domain'); } + + # belongs to relation + public function getRouting() + { + return $this->hasOne(\app\models\Routing::class, ['domain_id' => 'id']); + } } From 2134ddf05336cb4c454caa31949eb312990df971 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Tue, 4 Mar 2025 15:59:03 +0530 Subject: [PATCH 14/20] Refactor and add docs --- src/lib/AttributeResolver.php | 2 +- src/lib/SchemaToDatabase.php | 6 ++---- src/lib/items/DbModel.php | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index 1e3f731a..0c0c41c9 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -38,7 +38,7 @@ class AttributeResolver public array $relations = []; /** - * @var AttributeRelation[]|array + * @var array keys contains class names and value contains array of belongs to relations */ public array $belongsToRelations = []; diff --git a/src/lib/SchemaToDatabase.php b/src/lib/SchemaToDatabase.php index 929c8ee3..0c513b94 100644 --- a/src/lib/SchemaToDatabase.php +++ b/src/lib/SchemaToDatabase.php @@ -113,10 +113,8 @@ public function prepareModels(): array // handle belongs to relation foreach ($resolvers as $aResolver) { foreach ($aResolver->belongsToRelations as $name => $relations) { - foreach ($relations as $relation) { - /** @var AttributeRelation $relation */ - $models[$name]->belongsToRelations[] = $relation; - } + /** @var AttributeRelation[] $relations */ + $models[$name]->belongsToRelations = [...$models[$name]->belongsToRelations, ...$relations]; } } diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index f314b8e8..5b19d03c 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -86,7 +86,7 @@ class DbModel extends BaseObject public bool $descriptionIsComment = false; /** - * @var array|AttributeRelation[] belongs to relations + * @var AttributeRelation[] belongs to relations */ public array $belongsToRelations = []; From 426fecb1c5afcb2c100f2549112be9398f7091a8 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Fri, 7 Mar 2025 16:20:29 +0530 Subject: [PATCH 15/20] Modify a file to create pull request at GitHub --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f68df469..4cc1d72a 100644 --- a/README.md +++ b/README.md @@ -779,3 +779,4 @@ Professional support, consulting as well as software development services are av https://www.cebe.cc/en/contact Development of this library is sponsored by [cebe.:cloud: "Your Professional Deployment Platform"](https://cebe.cloud). + From 49cd50e33135debecf375cff2b3ba81e7d4f821a Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Fri, 7 Mar 2025 18:18:19 +0530 Subject: [PATCH 16/20] Add message --- README.md | 1 - src/lib/generators/ControllersGenerator.php | 13 +++++-- .../index.php | 13 +++++++ .../index.yml | 36 +++++++++++++++++++ tests/unit/IssueFixTest.php | 14 ++++++++ 5 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/index.php create mode 100644 tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/index.yml diff --git a/README.md b/README.md index 4cc1d72a..f68df469 100644 --- a/README.md +++ b/README.md @@ -779,4 +779,3 @@ Professional support, consulting as well as software development services are av https://www.cebe.cc/en/contact Development of this library is sponsored by [cebe.:cloud: "Your Professional Deployment Platform"](https://cebe.cloud). - diff --git a/src/lib/generators/ControllersGenerator.php b/src/lib/generators/ControllersGenerator.php index bd749c9f..e3ddcf23 100644 --- a/src/lib/generators/ControllersGenerator.php +++ b/src/lib/generators/ControllersGenerator.php @@ -59,7 +59,7 @@ public function generate():CodeFiles $controllerPath = $path; /** * @var RestAction|FractalAction $action - **/ + **/ $action = $actions[0]; if ($action->prefix && !empty($action->prefixSettings)) { $controllerNamespace = trim($action->prefixSettings['namespace'], '\\'); @@ -126,15 +126,22 @@ protected function makeCustomController( ]; $reflection->addMethod('checkAccess', $params, AbstractMemberGenerator::FLAG_PUBLIC, '//TODO implement checkAccess'); foreach ($abstractActions as $action) { + + $responseHttpStatusCodes = ''; + foreach ($this->config->getOpenApi()->paths->getPaths()[$action->urlPath]->getOperations() as $verb => $operation) { + if ($verb === strtolower($action->requestMethod)) { + $responseHttpStatusCodes = implode(', ', array_keys($operation->responses->getResponses())); + } + } + $params = array_map(static function ($param) { return ['name' => $param]; }, $action->getParamNames()); - $reflection->addMethod( $action->actionMethodName, $params, AbstractMemberGenerator::FLAG_PUBLIC, - '//TODO implement ' . $action->actionMethodName + '// TODO implement ' . $action->actionMethodName . PHP_EOL . '// In order to conform with OpenAPI spec, response of this action must have one of the following HTTP status code: ' . $responseHttpStatusCodes ); } $classFileGenerator->setClasses([$reflection]); diff --git a/tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/index.php b/tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/index.php new file mode 100644 index 00000000..cf88e27a --- /dev/null +++ b/tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/index.php @@ -0,0 +1,13 @@ + '@specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/index.yml', + 'generateUrls' => false, + 'generateModels' => false, + 'excludeModels' => [ + 'Error', + ], + 'generateControllers' => true, + 'generateMigrations' => false, + 'generateModelFaker' => false, +]; diff --git a/tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/index.yml b/tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/index.yml new file mode 100644 index 00000000..df5b1314 --- /dev/null +++ b/tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/index.yml @@ -0,0 +1,36 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: 79_response_status_codes_are_not_the_codes_defined_in_spec +paths: + /mango/cake: + get: + responses: + '200': + description: The information + '403': + description: The information + '404': + description: The information + post: + responses: + '201': + description: The information + '403': + description: The information + '404': + description: The information + '422': + description: The information + +components: + schemas: + Address: + type: object + description: desc + properties: + id: + type: integer + name: + type: string + maxLength: 64 diff --git a/tests/unit/IssueFixTest.php b/tests/unit/IssueFixTest.php index ad3ff8fd..cda4e714 100644 --- a/tests/unit/IssueFixTest.php +++ b/tests/unit/IssueFixTest.php @@ -1014,4 +1014,18 @@ public function test22BugRulesRequiredIsGeneratedBeforeDefault() ]); $this->checkFiles($actualFiles, $expectedFiles); } + + // https://github.com/php-openapi/yii2-openapi/issues/79 + public function test79ResponseStatusCodesAreNotTheCodesDefinedInSpec() + { + $testFile = Yii::getAlias("@specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/index.php"); + $this->runGenerator($testFile); +// $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ +// 'recursive' => true, +// ]); +// $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/mysql"), [ +// 'recursive' => true, +// ]); +// $this->checkFiles($actualFiles, $expectedFiles); + } } From ecbee9432dc6581367e0983c338be3656d125af1 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Fri, 7 Mar 2025 18:19:17 +0530 Subject: [PATCH 17/20] Complete the test --- .../mysql/controllers/MangoController.php | 27 +++++++++++++++ .../controllers/base/MangoController.php | 34 +++++++++++++++++++ tests/unit/IssueFixTest.php | 14 ++++---- 3 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/mysql/controllers/MangoController.php create mode 100644 tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/mysql/controllers/base/MangoController.php diff --git a/tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/mysql/controllers/MangoController.php b/tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/mysql/controllers/MangoController.php new file mode 100644 index 00000000..4b3e60e3 --- /dev/null +++ b/tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/mysql/controllers/MangoController.php @@ -0,0 +1,27 @@ + [ + 'class' => \yii\rest\OptionsAction::class, + ], + ]; + } + + /** + * Checks the privilege of the current user. + * + * This method checks whether the current user has the privilege + * to run the specified action against the specified data model. + * If the user does not have access, a [[ForbiddenHttpException]] should be thrown. + * + * @param string $action the ID of the action to be executed + * @param object $model the model to be accessed. If null, it means no specific model is being accessed. + * @param array $params additional parameters + * @throws \yii\web\ForbiddenHttpException if the user does not have access + */ + abstract public function checkAccess($action, $model = null, $params = []); + + abstract public function actionCake(); + + abstract public function actionCreateCake(); + +} diff --git a/tests/unit/IssueFixTest.php b/tests/unit/IssueFixTest.php index cda4e714..13bf8702 100644 --- a/tests/unit/IssueFixTest.php +++ b/tests/unit/IssueFixTest.php @@ -1020,12 +1020,12 @@ public function test79ResponseStatusCodesAreNotTheCodesDefinedInSpec() { $testFile = Yii::getAlias("@specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/index.php"); $this->runGenerator($testFile); -// $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ -// 'recursive' => true, -// ]); -// $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/mysql"), [ -// 'recursive' => true, -// ]); -// $this->checkFiles($actualFiles, $expectedFiles); + $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ + 'recursive' => true, + ]); + $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/mysql"), [ + 'recursive' => true, + ]); + $this->checkFiles($actualFiles, $expectedFiles); } } From 229c0034438060e562aee47ad230c9db564ea8ee Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sat, 8 Mar 2025 07:31:40 +0530 Subject: [PATCH 18/20] Refactor --- src/lib/generators/ControllersGenerator.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/generators/ControllersGenerator.php b/src/lib/generators/ControllersGenerator.php index e3ddcf23..372af037 100644 --- a/src/lib/generators/ControllersGenerator.php +++ b/src/lib/generators/ControllersGenerator.php @@ -129,8 +129,9 @@ protected function makeCustomController( $responseHttpStatusCodes = ''; foreach ($this->config->getOpenApi()->paths->getPaths()[$action->urlPath]->getOperations() as $verb => $operation) { - if ($verb === strtolower($action->requestMethod)) { - $responseHttpStatusCodes = implode(', ', array_keys($operation->responses->getResponses())); + $codes = array_keys($operation->responses->getResponses()); + if ($verb === strtolower($action->requestMethod) && $codes !== [200]) { + $responseHttpStatusCodes = implode(', ', $codes); } } @@ -141,7 +142,7 @@ protected function makeCustomController( $action->actionMethodName, $params, AbstractMemberGenerator::FLAG_PUBLIC, - '// TODO implement ' . $action->actionMethodName . PHP_EOL . '// In order to conform with OpenAPI spec, response of this action must have one of the following HTTP status code: ' . $responseHttpStatusCodes + '//TODO implement ' . $action->actionMethodName . ($responseHttpStatusCodes ? PHP_EOL . '// In order to conform with OpenAPI spec, response of this action must have one of the following HTTP status code: ' . $responseHttpStatusCodes : '') ); } $classFileGenerator->setClasses([$reflection]); From d89fe286ae94d906092e809d36d09e6a7ee8b844 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sat, 8 Mar 2025 08:12:43 +0530 Subject: [PATCH 19/20] Refactor and fix failing test --- src/lib/generators/ControllersGenerator.php | 12 ++++++++++-- .../specs/blog_v2/controllers/CommentController.php | 2 ++ .../pgsql/controllers/ContactController.php | 2 ++ .../pgsql/controllers/AccountController.php | 1 + .../mysql/controllers/MangoController.php | 4 ++-- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/lib/generators/ControllersGenerator.php b/src/lib/generators/ControllersGenerator.php index 372af037..ea0c73c4 100644 --- a/src/lib/generators/ControllersGenerator.php +++ b/src/lib/generators/ControllersGenerator.php @@ -126,11 +126,19 @@ protected function makeCustomController( ]; $reflection->addMethod('checkAccess', $params, AbstractMemberGenerator::FLAG_PUBLIC, '//TODO implement checkAccess'); foreach ($abstractActions as $action) { - $responseHttpStatusCodes = ''; foreach ($this->config->getOpenApi()->paths->getPaths()[$action->urlPath]->getOperations() as $verb => $operation) { $codes = array_keys($operation->responses->getResponses()); - if ($verb === strtolower($action->requestMethod) && $codes !== [200]) { + + $only200OrDefault = false; + if ($codes === [200] || $codes === ['default']) { + $only200OrDefault = true; + } + if (in_array('default', $codes) && in_array(200, $codes) && count($codes) === 2) { + $only200OrDefault = true; + } + + if ($verb === strtolower($action->requestMethod) && !$only200OrDefault) { $responseHttpStatusCodes = implode(', ', $codes); } } diff --git a/tests/specs/blog_v2/controllers/CommentController.php b/tests/specs/blog_v2/controllers/CommentController.php index 0448ec72..c46307ed 100644 --- a/tests/specs/blog_v2/controllers/CommentController.php +++ b/tests/specs/blog_v2/controllers/CommentController.php @@ -18,6 +18,7 @@ public function actionListForPost($postId) public function actionCreateForPost($postId) { //TODO implement actionCreateForPost + // In order to conform with OpenAPI spec, response of this action must have one of the following HTTP status code: 201, default } public function actionViewForPost($slug, $id) @@ -28,6 +29,7 @@ public function actionViewForPost($slug, $id) public function actionDeleteForPost($slug, $id) { //TODO implement actionDeleteForPost + // In order to conform with OpenAPI spec, response of this action must have one of the following HTTP status code: 204 } public function actionUpdateForPost($slug, $id) diff --git a/tests/specs/issue_fix/163_generator_crash_when_using_reference_inside_an_object/pgsql/controllers/ContactController.php b/tests/specs/issue_fix/163_generator_crash_when_using_reference_inside_an_object/pgsql/controllers/ContactController.php index 10422b18..5f21cabb 100644 --- a/tests/specs/issue_fix/163_generator_crash_when_using_reference_inside_an_object/pgsql/controllers/ContactController.php +++ b/tests/specs/issue_fix/163_generator_crash_when_using_reference_inside_an_object/pgsql/controllers/ContactController.php @@ -13,11 +13,13 @@ public function checkAccess($action, $model = null, $params = []) public function actionListForAccount($accountId) { //TODO implement actionListForAccount + // In order to conform with OpenAPI spec, response of this action must have one of the following HTTP status code: 200, 403 } public function actionViewForAccount($accountId, $contactId) { //TODO implement actionViewForAccount + // In order to conform with OpenAPI spec, response of this action must have one of the following HTTP status code: 200, 403 } diff --git a/tests/specs/issue_fix/175_bug_allof_with_multiple_dollarrefs/pgsql/controllers/AccountController.php b/tests/specs/issue_fix/175_bug_allof_with_multiple_dollarrefs/pgsql/controllers/AccountController.php index b4044b8f..cfa4a6d3 100644 --- a/tests/specs/issue_fix/175_bug_allof_with_multiple_dollarrefs/pgsql/controllers/AccountController.php +++ b/tests/specs/issue_fix/175_bug_allof_with_multiple_dollarrefs/pgsql/controllers/AccountController.php @@ -13,6 +13,7 @@ public function checkAccess($action, $model = null, $params = []) public function actionView($id) { //TODO implement actionView + // In order to conform with OpenAPI spec, response of this action must have one of the following HTTP status code: 200, 404 } diff --git a/tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/mysql/controllers/MangoController.php b/tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/mysql/controllers/MangoController.php index 4b3e60e3..46c40ad1 100644 --- a/tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/mysql/controllers/MangoController.php +++ b/tests/specs/issue_fix/79_response_status_codes_are_not_the_codes_defined_in_spec/mysql/controllers/MangoController.php @@ -12,13 +12,13 @@ public function checkAccess($action, $model = null, $params = []) public function actionCake() { - // TODO implement actionCake + //TODO implement actionCake // In order to conform with OpenAPI spec, response of this action must have one of the following HTTP status code: 200, 403, 404 } public function actionCreateCake() { - // TODO implement actionCreateCake + //TODO implement actionCreateCake // In order to conform with OpenAPI spec, response of this action must have one of the following HTTP status code: 201, 403, 404, 422 } From 4aa7668657b8f56a9c434c5e7c62d7961124081d Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sat, 8 Mar 2025 10:24:38 +0530 Subject: [PATCH 20/20] Fix --- src/lib/SchemaToDatabase.php | 4 +- .../index.php | 13 ++ .../index.yml | 13 ++ .../mysql/models/BaseModelFaker.php | 144 ++++++++++++++++++ tests/unit/IssueFixTest.php | 14 ++ 5 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 tests/specs/issue_fix/96_component_schema_should_be_optional/index.php create mode 100644 tests/specs/issue_fix/96_component_schema_should_be_optional/index.yml create mode 100644 tests/specs/issue_fix/96_component_schema_should_be_optional/mysql/models/BaseModelFaker.php diff --git a/src/lib/SchemaToDatabase.php b/src/lib/SchemaToDatabase.php index ccb230a3..beb27180 100644 --- a/src/lib/SchemaToDatabase.php +++ b/src/lib/SchemaToDatabase.php @@ -94,7 +94,7 @@ public function prepareModels(): array $openApi = $this->config->getOpenApi(); $junctions = $this->findJunctionSchemas(); - foreach ($openApi->components->schemas as $schemaName => $openApiSchema) { + foreach ($openApi->components->schemas ?? [] as $schemaName => $openApiSchema) { $schema = Yii::createObject(ComponentSchema::class, [$openApiSchema, $schemaName]); if (!$this->canGenerateModel($schemaName, $schema)) { @@ -153,7 +153,7 @@ public function findJunctionSchemas(): JunctionSchemas { $junctions = []; $openApi = $this->config->getOpenApi(); - foreach ($openApi->components->schemas as $schemaName => $openApiSchema) { + foreach ($openApi->components->schemas ?? [] as $schemaName => $openApiSchema) { /**@var ComponentSchema $schema */ $schema = Yii::createObject(ComponentSchema::class, [$openApiSchema, $schemaName]); if ($schema->isNonDb()) { diff --git a/tests/specs/issue_fix/96_component_schema_should_be_optional/index.php b/tests/specs/issue_fix/96_component_schema_should_be_optional/index.php new file mode 100644 index 00000000..34db291a --- /dev/null +++ b/tests/specs/issue_fix/96_component_schema_should_be_optional/index.php @@ -0,0 +1,13 @@ + '@specs/issue_fix/96_component_schema_should_be_optional/index.yml', + 'generateUrls' => false, + 'generateModels' => true, + 'excludeModels' => [ + 'Error', + ], + 'generateControllers' => false, + 'generateMigrations' => false, + 'generateModelFaker' => true, +]; diff --git a/tests/specs/issue_fix/96_component_schema_should_be_optional/index.yml b/tests/specs/issue_fix/96_component_schema_should_be_optional/index.yml new file mode 100644 index 00000000..5dcbc3cb --- /dev/null +++ b/tests/specs/issue_fix/96_component_schema_should_be_optional/index.yml @@ -0,0 +1,13 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: 96_component_schema_should_be_optional + +paths: + /: + get: + summary: List + operationId: list + responses: + '200': + description: The information diff --git a/tests/specs/issue_fix/96_component_schema_should_be_optional/mysql/models/BaseModelFaker.php b/tests/specs/issue_fix/96_component_schema_should_be_optional/mysql/models/BaseModelFaker.php new file mode 100644 index 00000000..c367fbb4 --- /dev/null +++ b/tests/specs/issue_fix/96_component_schema_should_be_optional/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/unit/IssueFixTest.php b/tests/unit/IssueFixTest.php index ad3ff8fd..c92d1f70 100644 --- a/tests/unit/IssueFixTest.php +++ b/tests/unit/IssueFixTest.php @@ -1014,4 +1014,18 @@ public function test22BugRulesRequiredIsGeneratedBeforeDefault() ]); $this->checkFiles($actualFiles, $expectedFiles); } + + // https://github.com/php-openapi/yii2-openapi/issues/96 + public function test96ComponentSchemaShouldBeOptional() + { + $testFile = Yii::getAlias("@specs/issue_fix/96_component_schema_should_be_optional/index.php"); + $this->runGenerator($testFile); + $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ + 'recursive' => true, + ]); + $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/96_component_schema_should_be_optional/mysql"), [ + 'recursive' => true, + ]); + $this->checkFiles($actualFiles, $expectedFiles); + } }