diff --git a/docker-compose.yml b/docker-compose.yml index 67d220d1..af5b8037 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,7 @@ name: yii2-docker services: php: image: yii2-openapi-php:${PHP_VERSION:-8.3} + pull_policy: build build: dockerfile: tests/docker/Dockerfile context: . diff --git a/src/generator/default/dbmodel.php b/src/generator/default/dbmodel.php index 15c9f722..4ca45d9b 100644 --- a/src/generator/default/dbmodel.php +++ b/src/generator/default/dbmodel.php @@ -4,9 +4,52 @@ * @var string $namespace * @var string $relationNamespace **/ +use cebe\yii2openapi\lib\items\AttributeRelation; use yii\helpers\Inflector; use yii\helpers\VarDumper; +$allCoveredClasses = array_merge( + array_map(fn($r) => $r->getClassName(), (array)$model->relations), + array_map(fn($r) => $r->relatedClassName, (array)$model->many2many), + array_map(fn($r) => $r->getClassName(), (array)$model->nonDbRelations) +); +/** + * Resolve inverse relation method names from the FK column name of the other model. + * Relations already covered by $model->relations, many2many, or nonDbRelations are skipped. + * Relations from models declared with "x-table: false" are skipped (no real table, no FK). + * + * Naming logic (model being generated = e.g. "Lead", $modelSnake = "lead"): + * 1. Take the FK column name from the other model (e.g. Order.customer_lead_id) + * 2. Strip trailing "_id" → "customer_lead" + * 3. Strip trailing "_" → "customer" + * 4. If a prefix remains, prepend it to the pluralized class name: + * "customer" + "Orders" → getCustomerOrders() + * If no prefix remains (plain FK like "lead_id"): + * "" + "Orders" → getOrders() + * + * Examples: Order has three FK columns pointing to Lead ($model->name = "Lead"): + * + * FK column (on Order) | generated on Order | generated on Lead (inverse) + * --------------------------|-------------------------|---------------------------- + * lead_id | getLead() | getOrders() + * customer_lead_id | getCustomerLead() | getCustomerOrders() + * billing_lead_id | getBillingLead() | getBillingOrders() + */ +$modelSnake = Inflector::underscore($model->name); +$inverseRelations = array_map(function (AttributeRelation $relation) use ($modelSnake): array { + $inverseMethod = $relation->getMethod() === 'hasOne' ? 'hasMany' : 'hasOne'; + $classBase = $inverseMethod === 'hasMany' ? Inflector::pluralize($relation->getCamelName()) : $relation->getCamelName(); + $fkBase = preg_replace('/_id$/', '', $relation->getForeignName()); + $prefix = preg_replace('/_?' . preg_quote($modelSnake, '/') . '$/', '', $fkBase); + return [ + 'relation' => $relation, + 'inverseMethod' => $inverseMethod, + 'inverseName' => $prefix !== '' ? Inflector::camelize($prefix) . $classBase : $classBase, + ]; +}, array_filter( + (array)$model->belongsToRelations, + fn(AttributeRelation $r) => !in_array($r->getClassName(), $allCoveredClasses) && $r->getTableName() !== '' +)); ?> @@ -45,6 +88,15 @@ many2many as $relation): ?> * @property array|\\relatedClassName ?>[] $name) ?> + + + + * @property \\getClassName() ?> $ + + + * @property array|\\getClassName() ?>[] $ + + */ abstract class getClassName() ?> extends \yii\db\ActiveRecord @@ -146,14 +198,17 @@ public function getgetCamelName() ?>() } -belongsToRelations as $relationName => $relation): ?>getCamelName(), $usedRelationNames) ? $i : '' ?> +nonDbRelations as $nonDbRelation): ?> - # belongs to relation - public function getgetCamelName() . ($number) ?>() + abstract public function getgetCamelName() ?>(): \yii\db\ActiveQuery; + + + + // inverse relation + public function get() { - return $this->getMethod() ?>(\\getClassName() ?>::class, linkToString() ?>); + return $this->(\\getClassName() ?>::class, linkToString() ?>); } -getCamelName(); endforeach; ?> + } diff --git a/src/lib/FakerStubResolver.php b/src/lib/FakerStubResolver.php index 80eb7ec0..e1e904b1 100644 --- a/src/lib/FakerStubResolver.php +++ b/src/lib/FakerStubResolver.php @@ -122,6 +122,7 @@ public function resolve(): ?string $example = $this->property->getAttr('example'); $example = VarExporter::export($example); + $example = preg_replace('/\n/', "\n ", $example); return str_replace('$faker->', '$faker->optional(0.92, ' . $example . ')->', $result); } @@ -284,7 +285,8 @@ private function fakeForArray(SpecObjectInterface $property, int $count = 4): st $items = $property->items; if (!$items) { - return $this->arbitraryArray(); + // Required fields cannot use [] — Yii2's isEmpty() treats empty arrays as blank. + return $this->attribute->required ? $this->arbitraryArray() : '[]'; } if ($items instanceof Reference) { @@ -299,13 +301,16 @@ private function fakeForArray(SpecObjectInterface $property, int $count = 4): st if ($type === null) { return $this->arbitraryArray(); } - $aFaker = $this->aElementFaker($this->property->getProperty(), $this->attribute->columnName); if (in_array($type, ['string', 'number', 'integer', 'boolean', 'array'])) { + $aFaker = $this->aElementFaker($this->property->getProperty(), $this->attribute->columnName); return $this->wrapInArray($aFaker, $uniqueItems, $count); } if ($type === 'object') { $result = $this->fakeForObject($items); + if ($result === '[]') { + return '[]'; + } return $this->wrapInArray($result, $uniqueItems, $count); } @@ -313,32 +318,104 @@ private function fakeForArray(SpecObjectInterface $property, int $count = 4): st } /** + * Generates a PHP array literal string for an OpenAPI object property. + * The output is embedded as PHP code in Faker fixture files, not as JSON. + * + * Flow: Faker assigns a PHP array to the model property → ActiveRecord passes it + * to the DB driver → the driver JSON-encodes it before storing. + * Result in DB: + * PHP [] → json_encode([]) → [] (JSON array) + * PHP ['key' => 'v'] → json_encode(['key' => 'v']) → {"key": "v"} (JSON object) + * + * A non-empty associative array correctly becomes a JSON object in the DB. + * An empty PHP array always becomes [] in the DB, never {} — regardless of whether + * the OpenAPI field is typed as "object" or "array". For test/faker data without + * defined properties this is acceptable, as no schema is enforced. * @internal */ - public function fakeForObject(SpecObjectInterface $items): string + public function fakeForObject(SpecObjectInterface $items, int $depth = 1): string { if (!$items->properties) { - return $this->arbitraryArray(); + return '[]'; } - $props = '[' . PHP_EOL; + $indent = str_repeat(' ', $depth + 3); + $closingIndent = str_repeat(' ', $depth + 2); + $parts = []; foreach ($items->properties as $name => $prop) { /** @var SpecObjectInterface $prop */ if (!empty($prop->properties)) { // nested object - $result = $this->{__FUNCTION__}($prop); + $result = $this->fakeForObject($prop, $depth + 1); } else { $result = $this->aElementFaker(['items' => $prop->getSerializableData()], $name); + if (str_starts_with($result, 'array_map')) { + $result = $this->reindentArrayMapForObject($result, $depth); + } } - $props .= '\'' . $name . '\' => ' . $result . ',' . PHP_EOL; + $parts[] = $indent . '\'' . $name . '\' => ' . $result . ','; } - $props .= ']'; + $props = '[' . PHP_EOL . implode(PHP_EOL, $parts) . PHP_EOL . $closingIndent . ']'; return $props; } + /** + * Re-indents a compact wrapInArray() output string to match the correct depth inside fakeForObject(). + * wrapInArray() always uses hardcoded 12/8-space indentation; when its result is embedded as a + * property value inside a fakeForObject() output at depth >= 1, the indentation must be adjusted. + * For a nested array_map body the inner call is expanded to multi-line style via expandCompactArrayMap(). + */ + private function reindentArrayMapForObject(string $code, int $depth): string + { + $bodyIndent = str_repeat(' ', $depth + 4); + $closeIndent = str_repeat(' ', $depth + 3); + + $pat = '/^array_map\(function \(\) use \(\$faker, \$uniqueFaker\) \{\n (.*)\n \}, range\(1, (\d+)\)\)$/s'; + if (!preg_match($pat, $code, $m)) { + return $code; + } + [$body, $count] = [$m[1], $m[2]]; + + if (str_starts_with($body, 'return array_map(')) { + $inner = substr($body, 7, -1); // strip "return " prefix and trailing ";" + $expanded = $this->expandCompactArrayMap($inner, $bodyIndent); + return "array_map(function () use (\$faker, \$uniqueFaker) {\n" + . $bodyIndent . "return {$expanded};\n" + . $closeIndent . "},\n" + . $closeIndent . "range(1, {$count}))"; + } + + return "array_map(function () use (\$faker, \$uniqueFaker) {\n" + . $bodyIndent . $body . "\n" + . $closeIndent . "},\n" + . $closeIndent . "range(1, {$count}))"; + } + + /** + * Expands a compact wrapInArray() string (single-line function + range) into multi-line style, + * using $baseIndent as the reference indentation level for the opening "array_map(" line. + */ + private function expandCompactArrayMap(string $code, string $baseIndent): string + { + $pat = '/^array_map\(function \(\) use \(\$faker, \$uniqueFaker\) \{\n (.*)\n \}, range\(1, (\d+)\)\)$/s'; + if (!preg_match($pat, $code, $m)) { + return $code; + } + [$body, $count] = [$m[1], $m[2]]; + $funcIndent = $baseIndent . ' '; + $innerIndent = $baseIndent . ' '; + + return "array_map(\n" + . $funcIndent . "function () use (\$faker, \$uniqueFaker) {\n" + . $innerIndent . $body . "\n" + . $funcIndent . "},\n" + . $funcIndent . "range(1, {$count})\n" + . $baseIndent . ")"; + } + /** * This method must be only used incase of array * @param SpecObjectInterface $items @@ -355,15 +432,28 @@ public function fakeForObject(SpecObjectInterface $items): string public function handleOneOf(SpecObjectInterface $items, int $count): string { $result = ''; + $indent = str_repeat(' ', 12); foreach ($items->oneOf as $key => $aDataType) { /** @var Schema|Reference $aDataType */ $inp = $aDataType instanceof Reference ? $aDataType : ['items' => $aDataType->getSerializableData()]; $aFaker = $this->aElementFaker($inp, $this->attribute->columnName); + /** + * Each $dataTypeN gets its own line (12-space indent = wrapInArray body level). + * wrapInArray output (array_map) gets +4 spaces on continuation lines (12→16, 8→12). + * fakeForObject output (starts with "[") is left as-is — depth=1 already gives 16/12. + * return goes on its own line. + */ + if (str_contains($aFaker, PHP_EOL) && !str_starts_with($aFaker, '[')) { + $aFaker = str_replace(PHP_EOL, PHP_EOL . ' ', $aFaker); + } + if ($result !== '') { + $result .= PHP_EOL . $indent; + } $result .= '$dataType' . $key . ' = ' . $aFaker . ';'; } $ct = count($items->oneOf) - 1; - $result .= 'return ${"dataType".rand(0, ' . $ct . ')}'; + $result .= PHP_EOL . $indent . 'return ${"dataType".rand(0, ' . $ct . ')}'; return $result; } diff --git a/src/lib/openapi/PropertySchema.php b/src/lib/openapi/PropertySchema.php index 227e2011..b8dbb472 100644 --- a/src/lib/openapi/PropertySchema.php +++ b/src/lib/openapi/PropertySchema.php @@ -100,6 +100,7 @@ public function __construct(SpecObjectInterface $property, string $name, Compone $this->isPk = $name === $schema->getPkName(); $onUpdate = $onDelete = $xFaker = $reference = $fkColName = null; + $xDbTypeFalse = false; foreach ($property->allOf ?? [] as $element) { // x-fk-on-delete | x-fk-on-update @@ -122,6 +123,11 @@ public function __construct(SpecObjectInterface $property, string $name, Compone if (!empty($element->{CustomSpecAttr::FK_COLUMN_NAME})) { $fkColName = $element->{CustomSpecAttr::FK_COLUMN_NAME}; } + + // x-db-type: false → treat as non-DB reference (no FK column, no migration) + if (isset($element->{CustomSpecAttr::DB_TYPE}) && $element->{CustomSpecAttr::DB_TYPE} === false) { + $xDbTypeFalse = true; + } } if ( @@ -143,6 +149,9 @@ public function __construct(SpecObjectInterface $property, string $name, Compone $this->xFaker = $xFaker; $this->property = $reference; $property = $this->property; + } elseif ($xDbTypeFalse && $reference instanceof Reference) { + $this->property = $reference; + $property = $this->property; } // don't go reference part if `x-no-relation` is true @@ -152,6 +161,9 @@ public function __construct(SpecObjectInterface $property, string $name, Compone if ($property instanceof Reference) { $this->initReference(); + if ($xDbTypeFalse) { + $this->isNonDbReference = true; + } } elseif ( isset($property->type, $property->items) && $property->type === 'array' && $property->items instanceof Reference diff --git a/tests/docker/Dockerfile b/tests/docker/Dockerfile index 59eae6a9..8fa3101c 100644 --- a/tests/docker/Dockerfile +++ b/tests/docker/Dockerfile @@ -9,11 +9,8 @@ ENV DEBIAN_FRONTEND=noninteractive RUN echo "force-unsafe-io" > /etc/dpkg/dpkg.cfg.d/02apt-speedup && \ echo "Acquire::http {No-Cache=True;};" > /etc/apt/apt.conf.d/no-cache RUN apt-get update && \ - apt-get -y install \ - gnupg2 && \ - apt-key update && \ - apt-get update && \ apt-get install -y --no-install-recommends \ + gnupg2 \ imagemagick \ libmagickwand-dev libmagickcore-dev \ libfreetype6-dev \ diff --git a/tests/fixtures/blog.php b/tests/fixtures/blog.php index 4c484dbe..7fe3d122 100644 --- a/tests/fixtures/blog.php +++ b/tests/fixtures/blog.php @@ -117,11 +117,9 @@ ->setDescription('The User') ->setFakerStub('$faker->randomElement(\app\models\User::find()->select("id")->column())'), 'message' => (new Attribute('message', ['phpType' => 'array', 'dbType' => 'json', 'xDbType' => 'json'])) - ->setRequired()->setDefault([])->setFakerStub('$faker->words()'), + ->setRequired()->setDefault([])->setXDescriptionIsComment(null)->setFakerStub('$faker->words()'), 'meta_data' => (new Attribute('meta_data', ['phpType' => 'array', 'dbType' => 'json', 'xDbType' => 'json'])) - ->setDefault([])->setFakerStub('array_map(function () use ($faker, $uniqueFaker) { - return $faker->words(); - }, range(1, 4))'), + ->setDefault([])->setXDescriptionIsComment(null)->setFakerStub('[]'), 'created_at' => (new Attribute('created_at',['phpType' => 'int', 'dbType' => 'integer'])) ->setRequired()->setFakerStub('$faker->unixTime'), ], diff --git a/tests/specs/blog/models/CommentFaker.php b/tests/specs/blog/models/CommentFaker.php index 723562d8..ed0355f0 100644 --- a/tests/specs/blog/models/CommentFaker.php +++ b/tests/specs/blog/models/CommentFaker.php @@ -33,9 +33,7 @@ public function generateModel($attributes = []) $model->post_id = $faker->randomElement(\app\models\Post::find()->select("id")->column()); $model->author_id = $faker->randomElement(\app\models\User::find()->select("id")->column()); $model->message = $faker->words(); - $model->meta_data = array_map(function () use ($faker, $uniqueFaker) { - return $faker->words(); - }, range(1, 4)); + $model->meta_data = []; $model->created_at = $faker->unixTime; if (!is_callable($attributes)) { $model->setAttributes($attributes, false); diff --git a/tests/specs/blog/models/base/Category.php b/tests/specs/blog/models/base/Category.php index 287bdc9f..36e7887a 100644 --- a/tests/specs/blog/models/base/Category.php +++ b/tests/specs/blog/models/base/Category.php @@ -38,10 +38,4 @@ 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 2a97d0fa..80393b08 100644 --- a/tests/specs/blog/models/base/Post.php +++ b/tests/specs/blog/models/base/Post.php @@ -61,10 +61,4 @@ 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 557328f9..ee733508 100644 --- a/tests/specs/blog/models/base/User.php +++ b/tests/specs/blog/models/base/User.php @@ -17,6 +17,8 @@ * @property int $flags * @property string $created_at * + * @property array|\app\models\Post[] $createdByPosts + * @property array|\app\models\Comment[] $authorComments */ abstract class User extends \yii\db\ActiveRecord { @@ -45,15 +47,15 @@ public function rules() ]; } - # belongs to relation - public function getPost() + # inverse relation + public function getCreatedByPosts() { - return $this->hasOne(\app\models\Post::class, ['created_by_id' => 'id']); + return $this->hasMany(\app\models\Post::class, ['created_by_id' => 'id']); } - # belongs to relation - public function getComment() + # inverse relation + public function getAuthorComments() { - return $this->hasOne(\app\models\Comment::class, ['author_id' => 'id']); + return $this->hasMany(\app\models\Comment::class, ['author_id' => 'id']); } } diff --git a/tests/specs/blog_v2/migrations_mysql_db/m200000_000005_change_table_v2_comments.php b/tests/specs/blog_v2/migrations_mysql_db/m200000_000005_change_table_v2_comments.php index 8ac91b71..604e948a 100644 --- a/tests/specs/blog_v2/migrations_mysql_db/m200000_000005_change_table_v2_comments.php +++ b/tests/specs/blog_v2/migrations_mysql_db/m200000_000005_change_table_v2_comments.php @@ -7,8 +7,8 @@ class m200000_000005_change_table_v2_comments extends \yii\db\Migration { public function up() { - $this->dropForeignKey('fk_v2_comments_post_id_v2_posts_uid', '{{%v2_comments}}'); $this->dropForeignKey('fk_v2_comments_author_id_v2_users_id', '{{%v2_comments}}'); + $this->dropForeignKey('fk_v2_comments_post_id_v2_posts_uid', '{{%v2_comments}}'); $this->addColumn('{{%v2_comments}}', 'user_id', $this->bigInteger()->null()->defaultValue(null)->after('post_id')->comment('The User')); $this->dropColumn('{{%v2_comments}}', 'author_id'); $this->alterColumn('{{%v2_comments}}', 'message', $this->text()->notNull()); @@ -27,7 +27,7 @@ public function down() $this->alterColumn('{{%v2_comments}}', 'message', 'json NOT NULL'); $this->addColumn('{{%v2_comments}}', 'author_id', $this->integer()->notNull()->after('post_id')); $this->dropColumn('{{%v2_comments}}', 'user_id'); - $this->addForeignKey('fk_v2_comments_author_id_v2_users_id', '{{%v2_comments}}', 'author_id', 'itt_v2_users', 'id'); $this->addForeignKey('fk_v2_comments_post_id_v2_posts_uid', '{{%v2_comments}}', 'post_id', 'itt_v2_posts', 'uid'); + $this->addForeignKey('fk_v2_comments_author_id_v2_users_id', '{{%v2_comments}}', 'author_id', 'itt_v2_users', 'id'); } } diff --git a/tests/specs/blog_v2/models/base/Category.php b/tests/specs/blog_v2/models/base/Category.php index 1b97af75..7744c88a 100644 --- a/tests/specs/blog_v2/models/base/Category.php +++ b/tests/specs/blog_v2/models/base/Category.php @@ -38,10 +38,4 @@ 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 0b82cefc..1913f0c2 100644 --- a/tests/specs/blog_v2/models/base/Post.php +++ b/tests/specs/blog_v2/models/base/Post.php @@ -73,10 +73,4 @@ 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 820b8259..094f265e 100644 --- a/tests/specs/blog_v2/models/base/User.php +++ b/tests/specs/blog_v2/models/base/User.php @@ -17,6 +17,8 @@ * @property int $flags * @property string $created_at * + * @property array|\app\models\Post[] $createdByPosts + * @property array|\app\models\Comment[] $comments */ abstract class User extends \yii\db\ActiveRecord { @@ -48,15 +50,15 @@ public function rules() ]; } - # belongs to relation - public function getPost() + # inverse relation + public function getCreatedByPosts() { - return $this->hasOne(\app\models\Post::class, ['created_by_id' => 'id']); + return $this->hasMany(\app\models\Post::class, ['created_by_id' => 'id']); } - # belongs to relation - public function getComment() + # inverse relation + public function getComments() { - return $this->hasOne(\app\models\Comment::class, ['user_id' => 'id']); + return $this->hasMany(\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 3bb543a1..b16c1a9c 100644 --- a/tests/specs/fk_col_name/app/models/base/Delivery.php +++ b/tests/specs/fk_col_name/app/models/base/Delivery.php @@ -12,6 +12,7 @@ * @property int $id * @property string $title * + * @property array|\app\models\Webhook[] $redeliveryOfWebhooks */ abstract class Delivery extends \yii\db\ActiveRecord { @@ -28,9 +29,9 @@ public function rules() ]; } - # belongs to relation - public function getWebhook() + # inverse relation + public function getRedeliveryOfWebhooks() { - return $this->hasOne(\app\models\Webhook::class, ['redelivery_of' => 'id']); + return $this->hasMany(\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 7847f799..3e298dc7 100644 --- a/tests/specs/fk_col_name/app/models/base/User.php +++ b/tests/specs/fk_col_name/app/models/base/User.php @@ -12,6 +12,7 @@ * @property int $id * @property string $name * + * @property array|\app\models\Webhook[] $webhooks */ abstract class User extends \yii\db\ActiveRecord { @@ -29,9 +30,9 @@ public function rules() ]; } - # belongs to relation - public function getWebhook() + # inverse relation + public function getWebhooks() { - return $this->hasOne(\app\models\Webhook::class, ['user_id' => 'id']); + return $this->hasMany(\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 98b9c801..2ed793df 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 @@ -12,6 +12,8 @@ * @property int $id * @property string $title * + * @property array|\app\models\Webhook[] $redeliveryOfWebhooks + * @property array|\app\models\Webhook[] $rdAbc2Webhooks */ abstract class Delivery extends \yii\db\ActiveRecord { @@ -28,15 +30,15 @@ public function rules() ]; } - # belongs to relation - public function getWebhook() + # inverse relation + public function getRedeliveryOfWebhooks() { - return $this->hasOne(\app\models\Webhook::class, ['redelivery_of' => 'id']); + return $this->hasMany(\app\models\Webhook::class, ['redelivery_of' => 'id']); } - # belongs to relation - public function getWebhook2() + # inverse relation + public function getRdAbc2Webhooks() { - return $this->hasOne(\app\models\Webhook::class, ['rd_abc_2' => 'id']); + return $this->hasMany(\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 7847f799..3e298dc7 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 @@ -12,6 +12,7 @@ * @property int $id * @property string $name * + * @property array|\app\models\Webhook[] $webhooks */ abstract class User extends \yii\db\ActiveRecord { @@ -29,9 +30,9 @@ public function rules() ]; } - # belongs to relation - public function getWebhook() + # inverse relation + public function getWebhooks() { - return $this->hasOne(\app\models\Webhook::class, ['user_id' => 'id']); + return $this->hasMany(\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 2639a486..75ebcc6d 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 @@ -13,6 +13,7 @@ * @property string $name name * @property string $paymentMethodName * + * @property array|\app\models\Contact[] $contacts */ abstract class Mailing extends \yii\db\ActiveRecord { @@ -31,9 +32,9 @@ public function rules() ]; } - # belongs to relation - public function getContact() + # inverse relation + public function getContacts() { - return $this->hasOne(\app\models\Contact::class, ['mailing_id' => 'id']); + return $this->hasMany(\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 1f685781..66f1ac3c 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 @@ -11,6 +11,7 @@ * * @property int $id * + * @property array|\app\models\Order[] $orders */ abstract class Invoice extends \yii\db\ActiveRecord { @@ -24,9 +25,9 @@ public function rules() return []; } - # belongs to relation - public function getOrder() + # inverse relation + public function getOrders() { - return $this->hasOne(\app\models\Order::class, ['invoice_id' => 'id']); + return $this->hasMany(\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 079b93b9..a6c92e23 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 @@ -13,6 +13,7 @@ * @property string $name account name * @property string $paymentMethodName * + * @property array|\app\models\Contact[] $contacts */ abstract class Account extends \yii\db\ActiveRecord { @@ -31,9 +32,9 @@ public function rules() ]; } - # belongs to relation - public function getContact() + # inverse relation + public function getContacts() { - return $this->hasOne(\app\models\Contact::class, ['account_id' => 'id']); + return $this->hasMany(\app\models\Contact::class, ['account_id' => 'id']); } } diff --git a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/PetFaker.php b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/PetFaker.php index 24ecc5e4..2fc98db5 100644 --- a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/PetFaker.php +++ b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/PetFaker.php @@ -36,11 +36,11 @@ public function generateModel($attributes = []) return $faker->sentence; }, range(1, 4)); $model->tags_arbit = $faker->optional(0.92, [ - 'long-tail', - 'short-tail', - 'black', - 'white', -])->words(); + 'long-tail', + 'short-tail', + 'black', + 'white', + ])->words(); $model->number_arr = array_map(function () use ($faker, $uniqueFaker) { return $faker->randomFloat(); }, range(1, 4)); @@ -75,49 +75,68 @@ public function generateModel($attributes = []) }, range(1, 3)); $model->arr_of_obj = array_map(function () use ($faker, $uniqueFaker) { return [ -'id' => $uniqueFaker->numberBetween(0, 1000000), -'name' => $faker->sentence, -'age' => $faker->numberBetween(0, 200), -'user' => $faker->randomElement(\app\models\User::find()->select("id")->column()), -'user_2' => array_map(function () use ($faker, $uniqueFaker) { - return (new UserFaker)->generateModel()->attributes; - }, range(1, 4)), -'tags' => array_map(function () use ($faker, $uniqueFaker) { - return $uniqueFaker->sentence; - }, range(1, 4)), -'arr_arr_int_2' => array_map(function () use ($faker, $uniqueFaker) { - return array_map(function () use ($faker, $uniqueFaker) { - return $faker->numberBetween(0, 1000000); - }, range(1, 11)); - }, range(1, 4)), -'appearance' => [ -'height' => $faker->numberBetween(0, 20), -'weight' => $faker->numberBetween(0, 1000000), -'email' => $faker->safeEmail, -'nested_obj' => [ -'id' => $uniqueFaker->numberBetween(0, 1000000), -'title' => $faker->title, -], -], -]; + 'id' => $uniqueFaker->numberBetween(0, 1000000), + 'name' => $faker->sentence, + 'age' => $faker->numberBetween(0, 200), + 'user' => $faker->randomElement(\app\models\User::find()->select("id")->column()), + 'user_2' => array_map(function () use ($faker, $uniqueFaker) { + return (new UserFaker)->generateModel()->attributes; + }, + range(1, 4)), + 'tags' => array_map(function () use ($faker, $uniqueFaker) { + return $uniqueFaker->sentence; + }, + range(1, 4)), + 'arr_arr_int_2' => array_map(function () use ($faker, $uniqueFaker) { + return array_map( + function () use ($faker, $uniqueFaker) { + return $faker->numberBetween(0, 1000000); + }, + range(1, 11) + ); + }, + range(1, 4)), + 'appearance' => [ + 'height' => $faker->numberBetween(0, 20), + 'weight' => $faker->numberBetween(0, 1000000), + 'email' => $faker->safeEmail, + 'nested_obj' => [ + 'id' => $uniqueFaker->numberBetween(0, 1000000), + 'title' => $faker->title, + ], + ], + ]; }, range(1, 3)); $model->user_ref_obj_arr = array_map(function () use ($faker, $uniqueFaker) { return (new UserFaker)->generateModel()->attributes; }, range(1, 3)); $model->one_of_arr = array_map(function () use ($faker, $uniqueFaker) { - $dataType0 = $faker->numberBetween(0, 1000000);$dataType1 = $faker->sentence;$dataType2 = $faker->boolean;return ${"dataType".rand(0, 2)}; + $dataType0 = $faker->numberBetween(0, 1000000); + $dataType1 = $faker->sentence; + $dataType2 = $faker->boolean; + return ${"dataType".rand(0, 2)}; }, range(1, 4)); $model->one_of_arr_complex = array_map(function () use ($faker, $uniqueFaker) { - $dataType0 = $faker->numberBetween(0, 1000000);$dataType1 = $faker->sentence;$dataType2 = $faker->boolean;$dataType3 = $faker->words();$dataType4 = array_map(function () use ($faker, $uniqueFaker) { - return $faker->sentence; - }, range(1, 4));$dataType5 = [ -'id' => $uniqueFaker->numberBetween(0, 1000000), -];$dataType6 = array_map(function () use ($faker, $uniqueFaker) { - return (new UserFaker)->generateModel()->attributes; - }, range(1, 4));$dataType7 = (new FruitFaker)->generateModel()->attributes;return ${"dataType".rand(0, 7)}; + $dataType0 = $faker->numberBetween(0, 1000000); + $dataType1 = $faker->sentence; + $dataType2 = $faker->boolean; + $dataType3 = []; + $dataType4 = array_map(function () use ($faker, $uniqueFaker) { + return $faker->sentence; + }, range(1, 4)); + $dataType5 = [ + 'id' => $uniqueFaker->numberBetween(0, 1000000), + ]; + $dataType6 = array_map(function () use ($faker, $uniqueFaker) { + return (new UserFaker)->generateModel()->attributes; + }, range(1, 4)); + $dataType7 = (new FruitFaker)->generateModel()->attributes; + return ${"dataType".rand(0, 7)}; }, range(1, 8)); $model->one_of_from_multi_ref_arr = array_map(function () use ($faker, $uniqueFaker) { - $dataType0 = (new UserFaker)->generateModel()->attributes;$dataType1 = (new FruitFaker)->generateModel()->attributes;return ${"dataType".rand(0, 1)}; + $dataType0 = (new UserFaker)->generateModel()->attributes; + $dataType1 = (new FruitFaker)->generateModel()->attributes; + return ${"dataType".rand(0, 1)}; }, range(1, 4)); if (!is_callable($attributes)) { $model->setAttributes($attributes, false); 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 c1b63234..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,22 +34,4 @@ 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 547aa903..dd885188 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 @@ -12,6 +12,7 @@ * @property int $id * @property string $name * + * @property array|\app\models\Post[] $posts */ abstract class User extends \yii\db\ActiveRecord { @@ -28,9 +29,9 @@ public function rules() ]; } - # belongs to relation - public function getPost() + # inverse relation + public function getPosts() { - return $this->hasOne(\app\models\Post::class, ['user' => 'id']); + return $this->hasMany(\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 bfd317f7..47f7f373 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 @@ -12,6 +12,7 @@ * @property int $id * @property string $name * + * @property array|\app\models\Invoice[] $invoices */ abstract class Animal extends \yii\db\ActiveRecord { @@ -28,9 +29,9 @@ public function rules() ]; } - # belongs to relation - public function getInvoice() + # inverse relation + public function getInvoices() { - return $this->hasOne(\app\models\Invoice::class, ['animal_id' => 'id']); + return $this->hasMany(\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 ab5c4054..e06f3985 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 @@ -12,6 +12,7 @@ * @property int $id * @property string $name * + * @property array|\app\models\Invoice[] $invoices */ abstract class Fruit extends \yii\db\ActiveRecord { @@ -28,9 +29,9 @@ public function rules() ]; } - # belongs to relation - public function getInvoice() + # inverse relation + public function getInvoices() { - return $this->hasOne(\app\models\Invoice::class, ['fruit_id' => 'id']); + return $this->hasMany(\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 c132c14b..91161672 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 @@ -12,6 +12,8 @@ * @property int $id * @property string $name * + * @property array|\app\models\Invoice[] $invoices + * @property array|\app\models\Invoice[] $user2Invoices */ abstract class User extends \yii\db\ActiveRecord { @@ -28,15 +30,15 @@ public function rules() ]; } - # belongs to relation - public function getInvoice() + # inverse relation + public function getInvoices() { - return $this->hasOne(\app\models\Invoice::class, ['user_id' => 'id']); + return $this->hasMany(\app\models\Invoice::class, ['user_id' => 'id']); } - # belongs to relation - public function getInvoice2() + # inverse relation + public function getUser2Invoices() { - return $this->hasOne(\app\models\Invoice::class, ['user_2_id' => 'id']); + return $this->hasMany(\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/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 e42f43c1..77af2bfe 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 @@ -12,6 +12,7 @@ * @property int $id * @property string $name * + * @property array|\app\models\Human[] $humans */ abstract class Address extends \yii\db\ActiveRecord { @@ -28,9 +29,9 @@ public function rules() ]; } - # belongs to relation - public function getHuman() + # inverse relation + public function getHumans() { - return $this->hasOne(\app\models\Human::class, ['address_id' => 'id']); + return $this->hasMany(\app\models\Human::class, ['address_id' => '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 index a6a35b66..bb7a36af 100644 --- 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 @@ -11,6 +11,7 @@ * * @property int $id * + * @property array|\app\models\Address[] $addresses */ abstract class User extends \yii\db\ActiveRecord { @@ -24,9 +25,9 @@ public function rules() return []; } - # belongs to relation - public function getAddress() + # inverse relation + public function getAddresses() { - return $this->hasOne(\app\models\Address::class, ['user_id' => 'id']); + return $this->hasMany(\app\models\Address::class, ['user_id' => 'id']); } } diff --git a/tests/specs/issue_fix/inverse_relations/index.php b/tests/specs/issue_fix/inverse_relations/index.php new file mode 100644 index 00000000..e8f33acc --- /dev/null +++ b/tests/specs/issue_fix/inverse_relations/index.php @@ -0,0 +1,10 @@ + '@specs/issue_fix/inverse_relations/index.yaml', + 'generateUrls' => false, + 'generateModels' => true, + 'generateControllers' => false, + 'generateMigrations' => false, + 'generateModelFaker' => false, +]; diff --git a/tests/specs/issue_fix/inverse_relations/index.yaml b/tests/specs/issue_fix/inverse_relations/index.yaml new file mode 100644 index 00000000..fe604cca --- /dev/null +++ b/tests/specs/issue_fix/inverse_relations/index.yaml @@ -0,0 +1,86 @@ +openapi: "3.0.0" + +info: + version: 1.0.0 + title: inverse_relations + +paths: + /: + get: + responses: + '200': + description: The response + +components: + schemas: + Company: + x-table: companies + type: object + properties: + id: + type: integer + readOnly: true + name: + type: string + Lead: + x-table: leads + type: object + properties: + id: + type: integer + readOnly: true + name: + type: string + company: + $ref: '#/components/schemas/Company' + Order: + x-table: orders + type: object + properties: + id: + type: integer + readOnly: true + lead: + $ref: '#/components/schemas/Lead' + customer_lead: + $ref: '#/components/schemas/Lead' + billing_lead: + $ref: '#/components/schemas/Lead' + invoice: + $ref: '#/components/schemas/Invoice' + Invoice: + x-table: invoices + type: object + properties: + id: + type: integer + readOnly: true + Booking: + x-table: bookings + type: object + properties: + id: + type: integer + readOnly: true + bank_account_transaction: + $ref: '#/components/schemas/BankAccountTransaction' + BankAccountTransaction: + x-table: bank_account_transactions + type: object + properties: + id: + type: integer + readOnly: true + booking: + allOf: + - $ref: '#/components/schemas/Booking' + - x-db-type: false + VirtualTask: + x-table: false + type: object + properties: + id: + type: integer + readOnly: true + lead: + $ref: '#/components/schemas/Lead' diff --git a/tests/specs/issue_fix/inverse_relations/mysql/models/BankAccountTransaction.php b/tests/specs/issue_fix/inverse_relations/mysql/models/BankAccountTransaction.php new file mode 100644 index 00000000..b6ff4754 --- /dev/null +++ b/tests/specs/issue_fix/inverse_relations/mysql/models/BankAccountTransaction.php @@ -0,0 +1,10 @@ + [['bank_account_transaction_id'], 'integer'], + 'bank_account_transaction_id_exist' => [['bank_account_transaction_id'], 'exist', 'targetRelation' => 'bankAccountTransaction'], + ]; + } + + public function getBankAccountTransaction() + { + return $this->hasOne(\app\models\BankAccountTransaction::class, ['id' => 'bank_account_transaction_id']); + } +} diff --git a/tests/specs/issue_fix/inverse_relations/mysql/models/base/Company.php b/tests/specs/issue_fix/inverse_relations/mysql/models/base/Company.php new file mode 100644 index 00000000..17d91f70 --- /dev/null +++ b/tests/specs/issue_fix/inverse_relations/mysql/models/base/Company.php @@ -0,0 +1,37 @@ + [['name'], 'trim'], + 'name_string' => [['name'], 'string'], + ]; + } + + # inverse relation + public function getLeads() + { + return $this->hasMany(\app\models\Lead::class, ['company_id' => 'id']); + } +} diff --git a/tests/specs/issue_fix/inverse_relations/mysql/models/base/Invoice.php b/tests/specs/issue_fix/inverse_relations/mysql/models/base/Invoice.php new file mode 100644 index 00000000..66f1ac3c --- /dev/null +++ b/tests/specs/issue_fix/inverse_relations/mysql/models/base/Invoice.php @@ -0,0 +1,33 @@ +hasMany(\app\models\Order::class, ['invoice_id' => 'id']); + } +} diff --git a/tests/specs/issue_fix/inverse_relations/mysql/models/base/Lead.php b/tests/specs/issue_fix/inverse_relations/mysql/models/base/Lead.php new file mode 100644 index 00000000..fadb239a --- /dev/null +++ b/tests/specs/issue_fix/inverse_relations/mysql/models/base/Lead.php @@ -0,0 +1,60 @@ + [['name'], 'trim'], + 'name_string' => [['name'], 'string'], + 'company_id_integer' => [['company_id'], 'integer'], + 'company_id_exist' => [['company_id'], 'exist', 'targetRelation' => 'company'], + ]; + } + + public function getCompany() + { + return $this->hasOne(\app\models\Company::class, ['id' => 'company_id']); + } + + # inverse relation + public function getOrders() + { + return $this->hasMany(\app\models\Order::class, ['lead_id' => 'id']); + } + + # inverse relation + public function getCustomerOrders() + { + return $this->hasMany(\app\models\Order::class, ['customer_lead_id' => 'id']); + } + + # inverse relation + public function getBillingOrders() + { + return $this->hasMany(\app\models\Order::class, ['billing_lead_id' => 'id']); + } +} diff --git a/tests/specs/issue_fix/inverse_relations/mysql/models/base/Order.php b/tests/specs/issue_fix/inverse_relations/mysql/models/base/Order.php new file mode 100644 index 00000000..931d8265 --- /dev/null +++ b/tests/specs/issue_fix/inverse_relations/mysql/models/base/Order.php @@ -0,0 +1,63 @@ + [['lead_id'], 'integer'], + 'lead_id_exist' => [['lead_id'], 'exist', 'targetRelation' => 'lead'], + 'customer_lead_id_integer' => [['customer_lead_id'], 'integer'], + 'customer_lead_id_exist' => [['customer_lead_id'], 'exist', 'targetRelation' => 'customerLead'], + 'billing_lead_id_integer' => [['billing_lead_id'], 'integer'], + 'billing_lead_id_exist' => [['billing_lead_id'], 'exist', 'targetRelation' => 'billingLead'], + 'invoice_id_integer' => [['invoice_id'], 'integer'], + 'invoice_id_exist' => [['invoice_id'], 'exist', 'targetRelation' => 'invoice'], + ]; + } + + public function getLead() + { + return $this->hasOne(\app\models\Lead::class, ['id' => 'lead_id']); + } + + public function getCustomerLead() + { + return $this->hasOne(\app\models\Lead::class, ['id' => 'customer_lead_id']); + } + + public function getBillingLead() + { + return $this->hasOne(\app\models\Lead::class, ['id' => 'billing_lead_id']); + } + + public function getInvoice() + { + return $this->hasOne(\app\models\Invoice::class, ['id' => 'invoice_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 a296c4c0..48ec0be6 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 @@ -12,6 +12,9 @@ * @property int $id * @property string $name account name * + * @property array|\app\models\E123[] $e123s + * @property array|\app\models\E123[] $account2E123s + * @property array|\app\models\E123[] $account3E123s */ abstract class Account extends \yii\db\ActiveRecord { @@ -29,21 +32,21 @@ public function rules() ]; } - # belongs to relation - public function getE123() + # inverse relation + public function getE123s() { - return $this->hasOne(\app\models\E123::class, ['account_id' => 'id']); + return $this->hasMany(\app\models\E123::class, ['account_id' => 'id']); } - # belongs to relation - public function getE1232() + # inverse relation + public function getAccount2E123s() { - return $this->hasOne(\app\models\E123::class, ['account_2_id' => 'id']); + return $this->hasMany(\app\models\E123::class, ['account_2_id' => 'id']); } - # belongs to relation - public function getE1233() + # inverse relation + public function getAccount3E123s() { - return $this->hasOne(\app\models\E123::class, ['account_3_id' => 'id']); + return $this->hasMany(\app\models\E123::class, ['account_3_id' => 'id']); } } diff --git a/tests/specs/json_arr_obj.php b/tests/specs/json_arr_obj.php new file mode 100644 index 00000000..7b6e08f1 --- /dev/null +++ b/tests/specs/json_arr_obj.php @@ -0,0 +1,8 @@ + '@specs/json_arr_obj.yaml', + 'generateUrls' => false, + 'generateControllers' => false, + 'generateModels' => true, + 'generateMigrations' => false, +]; \ No newline at end of file diff --git a/tests/specs/json_arr_obj.yaml b/tests/specs/json_arr_obj.yaml new file mode 100644 index 00000000..3b3b7a59 --- /dev/null +++ b/tests/specs/json_arr_obj.yaml @@ -0,0 +1,50 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: Spec for array/object JSON column faker stubs (json_arr_obj) + +paths: + /: + get: + responses: + '200': + description: OK + +components: + schemas: + JsonArrObj: + x-table: json_arr_obj + type: object + description: > + Schema for testing faker stub generation for JSON columns with array/object types. + Each property triggers a specific code path in FakerStubResolver (fakeForArray / + fakeForObject) that previously returned a broken faker expression. + properties: + id: + type: integer + readOnly: true + + # Bug fix 1 (fakeForArray): array with no "items" definition. + # Before fix: returned $faker->words() — invalid for a jsonb column. + # After fix: returns [] — empty array is the correct safe default. + arr_no_items: + type: array + x-db-type: jsonb + + # Bug fix 2 (fakeForObject): object with no "properties" definition. + # Before fix: returned $faker->words() — invalid for an object. + # After fix: returns [] — empty array is the correct safe default. + obj_no_props: + type: object + x-db-type: jsonb + + # Bug fix 3 (fakeForArray + fakeForObject interaction): array whose items are an + # object with no "properties". fakeForObject returns [] for the items, and + # fakeForArray must NOT wrap [] in array_map() — it must return [] directly. + # Before fix: produced array_map(function() { return []; }, range(1, 4)) — broken. + # After fix: returns [] — early return when fakeForObject returns []. + arr_obj_no_props: + type: array + x-db-type: jsonb + items: + type: object \ No newline at end of file diff --git a/tests/specs/json_arr_obj/models/BaseModelFaker.php b/tests/specs/json_arr_obj/models/BaseModelFaker.php new file mode 100644 index 00000000..c367fbb4 --- /dev/null +++ b/tests/specs/json_arr_obj/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/json_arr_obj/models/JsonArrObj.php b/tests/specs/json_arr_obj/models/JsonArrObj.php new file mode 100644 index 00000000..b07c90a7 --- /dev/null +++ b/tests/specs/json_arr_obj/models/JsonArrObj.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 JsonArrObj(); + //$model->id = $uniqueFaker->numberBetween(0, 1000000); + $model->arr_no_items = []; + $model->obj_no_props = []; + $model->arr_obj_no_props = []; + if (!is_callable($attributes)) { + $model->setAttributes($attributes, false); + } else { + $model = $attributes($model, $faker, $uniqueFaker); + } + return $model; + } +} diff --git a/tests/specs/json_arr_obj/models/base/JsonArrObj.php b/tests/specs/json_arr_obj/models/base/JsonArrObj.php new file mode 100644 index 00000000..9851238b --- /dev/null +++ b/tests/specs/json_arr_obj/models/base/JsonArrObj.php @@ -0,0 +1,31 @@ + [['arr_no_items', 'obj_no_props', 'arr_obj_no_props'], 'safe'], + ]; + } +} diff --git a/tests/specs/menu/models/MenuFaker.php b/tests/specs/menu/models/MenuFaker.php index 6e5bda4d..8af0e400 100644 --- a/tests/specs/menu/models/MenuFaker.php +++ b/tests/specs/menu/models/MenuFaker.php @@ -32,8 +32,8 @@ public function generateModel($attributes = []) //$model->id = $uniqueFaker->numberBetween(0, 1000000); $model->name = substr($faker->text(100), 0, 100); $model->parent_id = $faker->randomElement(\app\models\Menu::find()->select("id")->column()); - $model->args = $faker->words(); - $model->kwargs = $faker->words(); + $model->args = []; + $model->kwargs = []; if (!is_callable($attributes)) { $model->setAttributes($attributes, false); } else { diff --git a/tests/specs/petstore/models/base/Store.php b/tests/specs/petstore/models/base/Store.php index d9eafaca..ddd0896a 100644 --- a/tests/specs/petstore/models/base/Store.php +++ b/tests/specs/petstore/models/base/Store.php @@ -12,6 +12,7 @@ * @property int $id * @property string $name * + * @property array|\app\models\Pet[] $pets */ abstract class Store extends \yii\db\ActiveRecord { @@ -29,9 +30,9 @@ public function rules() ]; } - # belongs to relation - public function getPet() + # inverse relation + public function getPets() { - return $this->hasOne(\app\models\Pet::class, ['store_id' => 'id']); + return $this->hasMany(\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 0f057dc1..e60cfb3a 100644 --- a/tests/specs/petstore_jsonapi/models/base/Pet.php +++ b/tests/specs/petstore_jsonapi/models/base/Pet.php @@ -64,9 +64,5 @@ 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']); - } + abstract public function getDoctor(): \yii\db\ActiveQuery; } diff --git a/tests/specs/petstore_namespace/mymodels/base/Store.php b/tests/specs/petstore_namespace/mymodels/base/Store.php index 35aa907f..761a0b67 100644 --- a/tests/specs/petstore_namespace/mymodels/base/Store.php +++ b/tests/specs/petstore_namespace/mymodels/base/Store.php @@ -12,6 +12,7 @@ * @property int $id * @property string $name * + * @property array|\app\mymodels\Pet[] $pets */ abstract class Store extends \yii\db\ActiveRecord { @@ -29,9 +30,9 @@ public function rules() ]; } - # belongs to relation - public function getPet() + # inverse relation + public function getPets() { - return $this->hasOne(\app\mymodels\Pet::class, ['store_id' => 'id']); + return $this->hasMany(\app\mymodels\Pet::class, ['store_id' => 'id']); } } diff --git a/tests/specs/postgres_custom/models/CustomFaker.php b/tests/specs/postgres_custom/models/CustomFaker.php index cc1b2cd0..3732716d 100644 --- a/tests/specs/postgres_custom/models/CustomFaker.php +++ b/tests/specs/postgres_custom/models/CustomFaker.php @@ -31,10 +31,10 @@ public function generateModel($attributes = []) $model = new Custom(); //$model->id = $uniqueFaker->numberBetween(0, 1000000); $model->num = $faker->numberBetween(0, 1000000); - $model->json1 = $faker->words(); - $model->json2 = $faker->words(); - $model->json3 = $faker->words(); - $model->json4 = $faker->words(); + $model->json1 = []; + $model->json2 = []; + $model->json3 = []; + $model->json4 = []; $model->status = $faker->randomElement(['active','draft']); $model->status_x = $faker->randomElement(['active','draft']); if (!is_callable($attributes)) { 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 74d782f2..37105036 100644 --- a/tests/specs/relations_in_faker/app/models/base/A123.php +++ b/tests/specs/relations_in_faker/app/models/base/A123.php @@ -14,6 +14,7 @@ * @property int $b123_id desc * * @property \app\models\B123 $b123 + * @property array|\app\models\Routing[] $routings */ abstract class A123 extends \yii\db\ActiveRecord { @@ -37,9 +38,9 @@ public function getB123() return $this->hasOne(\app\models\B123::class, ['id' => 'b123_id']); } - # belongs to relation - public function getRouting() + # inverse relation + public function getRoutings() { - return $this->hasOne(\app\models\Routing::class, ['a123_id' => 'id']); + return $this->hasMany(\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 c5bfedb1..d8dde018 100644 --- a/tests/specs/relations_in_faker/app/models/base/Account.php +++ b/tests/specs/relations_in_faker/app/models/base/Account.php @@ -12,6 +12,7 @@ * @property int $id * @property string $name account name * + * @property array|\app\models\Domain[] $domains */ abstract class Account extends \yii\db\ActiveRecord { @@ -29,9 +30,9 @@ public function rules() ]; } - # belongs to relation - public function getDomain() + # inverse relation + public function getDomains() { - return $this->hasOne(\app\models\Domain::class, ['account_id' => 'id']); + return $this->hasMany(\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 63727b75..95074fd0 100644 --- a/tests/specs/relations_in_faker/app/models/base/B123.php +++ b/tests/specs/relations_in_faker/app/models/base/B123.php @@ -14,6 +14,8 @@ * @property int $c123_id desc * * @property \app\models\C123 $c123 + * @property array|\app\models\A123[] $a123s + * @property array|\app\models\E123[] $e123s */ abstract class B123 extends \yii\db\ActiveRecord { @@ -37,15 +39,15 @@ public function getC123() return $this->hasOne(\app\models\C123::class, ['id' => 'c123_id']); } - # belongs to relation - public function getA123() + # inverse relation + public function getA123s() { - return $this->hasOne(\app\models\A123::class, ['b123_id' => 'id']); + return $this->hasMany(\app\models\A123::class, ['b123_id' => 'id']); } - # belongs to relation - public function getE123() + # inverse relation + public function getE123s() { - return $this->hasOne(\app\models\E123::class, ['b123_id' => 'id']); + return $this->hasMany(\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 7f516f78..d45db89a 100644 --- a/tests/specs/relations_in_faker/app/models/base/C123.php +++ b/tests/specs/relations_in_faker/app/models/base/C123.php @@ -12,6 +12,7 @@ * @property int $id * @property string $name * + * @property array|\app\models\B123[] $b123s */ abstract class C123 extends \yii\db\ActiveRecord { @@ -28,9 +29,9 @@ public function rules() ]; } - # belongs to relation - public function getB123() + # inverse relation + public function getB123s() { - return $this->hasOne(\app\models\B123::class, ['c123_id' => 'id']); + return $this->hasMany(\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 71555b18..90a1599d 100644 --- a/tests/specs/relations_in_faker/app/models/base/D123.php +++ b/tests/specs/relations_in_faker/app/models/base/D123.php @@ -12,6 +12,7 @@ * @property int $id * @property string $name * + * @property array|\app\models\Routing[] $routings */ abstract class D123 extends \yii\db\ActiveRecord { @@ -28,9 +29,9 @@ public function rules() ]; } - # belongs to relation - public function getRouting() + # inverse relation + public function getRoutings() { - return $this->hasOne(\app\models\Routing::class, ['d123_id' => 'id']); + return $this->hasMany(\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 fa563120..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,10 +44,4 @@ 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']); - } } diff --git a/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/AlldbdatatypeFaker.php b/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/AlldbdatatypeFaker.php index 8a4a3594..32934ebe 100644 --- a/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/AlldbdatatypeFaker.php +++ b/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/AlldbdatatypeFaker.php @@ -66,11 +66,11 @@ public function generateModel($attributes = []) $model->datetime_col = $faker->dateTimeThisYear('now', 'UTC')->format('Y-m-d H:i:s'); $model->timestamp_col = $faker->dateTimeThisYear('now', 'UTC')->format('Y-m-d H:i:s'); $model->year_col = $faker->year; - $model->json_col = $faker->words(); - $model->json_col_def = $faker->words(); - $model->json_col_def_2 = $faker->words(); + $model->json_col = []; + $model->json_col_def = []; + $model->json_col_def_2 = []; $model->text_def = $faker->sentence; - $model->json_def = $faker->words(); + $model->json_def = []; if (!is_callable($attributes)) { $model->setAttributes($attributes, false); } else { diff --git a/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/EditcolumnFaker.php b/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/EditcolumnFaker.php index 1f183cd5..2c4ae559 100644 --- a/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/EditcolumnFaker.php +++ b/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/EditcolumnFaker.php @@ -40,8 +40,8 @@ public function generateModel($attributes = []) $model->json_col = $faker->sentence; $model->json_col_2 = $faker->words(); $model->numeric_col = $faker->randomFloat(); - $model->json_col_def_n = $faker->words(); - $model->json_col_def_n_2 = $faker->words(); + $model->json_col_def_n = []; + $model->json_col_def_n_2 = []; if (!is_callable($attributes)) { $model->setAttributes($attributes, false); } else { diff --git a/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/NewcolumnFaker.php b/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/NewcolumnFaker.php index 0532b9c1..a60800cd 100644 --- a/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/NewcolumnFaker.php +++ b/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/NewcolumnFaker.php @@ -34,10 +34,10 @@ public function generateModel($attributes = []) $model->name = substr($faker->text(255), 0, 255); $model->last_name = $faker->sentence; $model->dec_col = $faker->randomFloat(); - $model->json_col = $faker->words(); + $model->json_col = []; $model->varchar_col = substr($faker->text(5), 0, 5); $model->numeric_col = $faker->randomFloat(); - $model->json_col_def_n = $faker->words(); + $model->json_col_def_n = []; if (!is_callable($attributes)) { $model->setAttributes($attributes, false); } else { diff --git a/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/PristineFaker.php b/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/PristineFaker.php index 293dc2df..2fdf01e5 100644 --- a/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/PristineFaker.php +++ b/tests/specs/x_db_type/rules_and_more/maria/app/models/mariafaker/PristineFaker.php @@ -37,7 +37,7 @@ public function generateModel($attributes = []) $model->col_5 = $faker->randomFloat(); $model->col_6 = $faker->randomFloat(); $model->col_7 = $faker->randomFloat(); - $model->col_8 = $faker->words(); + $model->col_8 = []; $model->col_9 = substr($faker->text(9), 0, 9); $model->col_10 = substr($faker->text(10), 0, 10); $model->col_11 = $faker->sentence; diff --git a/tests/specs/x_db_type/rules_and_more/mysql/app/models/AlldbdatatypeFaker.php b/tests/specs/x_db_type/rules_and_more/mysql/app/models/AlldbdatatypeFaker.php index 795f4344..9c3e308b 100644 --- a/tests/specs/x_db_type/rules_and_more/mysql/app/models/AlldbdatatypeFaker.php +++ b/tests/specs/x_db_type/rules_and_more/mysql/app/models/AlldbdatatypeFaker.php @@ -65,11 +65,11 @@ public function generateModel($attributes = []) $model->datetime_col = $faker->dateTimeThisYear('now', 'UTC')->format('Y-m-d H:i:s'); $model->timestamp_col = $faker->dateTimeThisYear('now', 'UTC')->format('Y-m-d H:i:s'); $model->year_col = $faker->year; - $model->json_col = $faker->words(); - $model->json_col_def = $faker->words(); - $model->json_col_def_2 = $faker->words(); + $model->json_col = []; + $model->json_col_def = []; + $model->json_col_def_2 = []; $model->text_def = $faker->sentence; - $model->json_def = $faker->words(); + $model->json_def = []; if (!is_callable($attributes)) { $model->setAttributes($attributes, false); } else { diff --git a/tests/specs/x_db_type/rules_and_more/mysql/app/models/EditcolumnFaker.php b/tests/specs/x_db_type/rules_and_more/mysql/app/models/EditcolumnFaker.php index 6a14b7de..c2318f0d 100644 --- a/tests/specs/x_db_type/rules_and_more/mysql/app/models/EditcolumnFaker.php +++ b/tests/specs/x_db_type/rules_and_more/mysql/app/models/EditcolumnFaker.php @@ -39,8 +39,8 @@ public function generateModel($attributes = []) $model->json_col = $faker->sentence; $model->json_col_2 = $faker->words(); $model->numeric_col = $faker->randomFloat(); - $model->json_col_def_n = $faker->words(); - $model->json_col_def_n_2 = $faker->words(); + $model->json_col_def_n = []; + $model->json_col_def_n_2 = []; if (!is_callable($attributes)) { $model->setAttributes($attributes, false); } else { diff --git a/tests/specs/x_db_type/rules_and_more/mysql/app/models/NewcolumnFaker.php b/tests/specs/x_db_type/rules_and_more/mysql/app/models/NewcolumnFaker.php index fe3319dd..87763eb3 100644 --- a/tests/specs/x_db_type/rules_and_more/mysql/app/models/NewcolumnFaker.php +++ b/tests/specs/x_db_type/rules_and_more/mysql/app/models/NewcolumnFaker.php @@ -33,10 +33,10 @@ public function generateModel($attributes = []) $model->name = substr($faker->text(255), 0, 255); $model->last_name = $faker->sentence; $model->dec_col = $faker->randomFloat(); - $model->json_col = $faker->words(); + $model->json_col = []; $model->varchar_col = substr($faker->text(5), 0, 5); $model->numeric_col = $faker->randomFloat(); - $model->json_col_def_n = $faker->words(); + $model->json_col_def_n = []; if (!is_callable($attributes)) { $model->setAttributes($attributes, false); } else { diff --git a/tests/specs/x_db_type/rules_and_more/mysql/app/models/PristineFaker.php b/tests/specs/x_db_type/rules_and_more/mysql/app/models/PristineFaker.php index ecd7b315..41b186d9 100644 --- a/tests/specs/x_db_type/rules_and_more/mysql/app/models/PristineFaker.php +++ b/tests/specs/x_db_type/rules_and_more/mysql/app/models/PristineFaker.php @@ -36,7 +36,7 @@ public function generateModel($attributes = []) $model->col_5 = $faker->randomFloat(); $model->col_6 = $faker->randomFloat(); $model->col_7 = $faker->randomFloat(); - $model->col_8 = $faker->words(); + $model->col_8 = []; $model->col_9 = substr($faker->text(9), 0, 9); $model->col_10 = substr($faker->text(10), 0, 10); $model->col_11 = $faker->sentence; diff --git a/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/AlldbdatatypeFaker.php b/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/AlldbdatatypeFaker.php index 70c2016c..e2af0af0 100644 --- a/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/AlldbdatatypeFaker.php +++ b/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/AlldbdatatypeFaker.php @@ -34,7 +34,7 @@ public function generateModel($attributes = []) $model->string_col = $faker->sentence; $model->varchar_col = $faker->sentence; $model->text_col = $faker->sentence; - $model->text_col_array = $faker->words(); + $model->text_col_array = []; $model->varchar_4_col = substr($faker->word(4), 0, 4); $model->varchar_5_col = substr($faker->text(5), 0, 5); $model->char_4_col = substr($faker->word(4), 0, 4); @@ -90,13 +90,13 @@ public function generateModel($attributes = []) $model->character_n = substr($faker->text(12), 0, 12); $model->character_varying = $faker->sentence; $model->character_varying_n = substr($faker->text(12), 0, 12); - $model->json_col = $faker->words(); - $model->jsonb_col = $faker->words(); - $model->json_col_def = $faker->words(); - $model->json_col_def_2 = $faker->words(); + $model->json_col = []; + $model->jsonb_col = []; + $model->json_col_def = []; + $model->json_col_def_2 = []; $model->text_def = $faker->sentence; - $model->json_def = $faker->words(); - $model->jsonb_def = $faker->words(); + $model->json_def = []; + $model->jsonb_def = []; $model->cidr_col = $faker->sentence; $model->circle_col = $faker->sentence; $model->date_col_z = $faker->dateTimeThisCentury->format('Y-m-d'); diff --git a/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/EditcolumnFaker.php b/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/EditcolumnFaker.php index e32c6d82..6488870c 100644 --- a/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/EditcolumnFaker.php +++ b/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/EditcolumnFaker.php @@ -40,9 +40,9 @@ public function generateModel($attributes = []) $model->json_col = $faker->sentence; $model->json_col_2 = $faker->words(); $model->numeric_col = $faker->randomFloat(); - $model->json_col_def_n = $faker->words(); - $model->json_col_def_n_2 = $faker->words(); - $model->text_col_array = $faker->words(); + $model->json_col_def_n = []; + $model->json_col_def_n_2 = []; + $model->text_col_array = []; if (!is_callable($attributes)) { $model->setAttributes($attributes, false); } else { diff --git a/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/NewcolumnFaker.php b/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/NewcolumnFaker.php index 64c32ef1..1b41dae7 100644 --- a/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/NewcolumnFaker.php +++ b/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/NewcolumnFaker.php @@ -35,12 +35,12 @@ public function generateModel($attributes = []) $model->first_name = $faker->sentence; $model->last_name = $faker->sentence; $model->dec_col = $faker->randomFloat(); - $model->json_col = $faker->words(); + $model->json_col = []; $model->varchar_col = $faker->sentence; $model->numeric_col = $faker->randomFloat(); - $model->json_col_def_n = $faker->words(); - $model->json_col_def_n_2 = $faker->words(); - $model->text_col_array = $faker->words(); + $model->json_col_def_n = []; + $model->json_col_def_n_2 = []; + $model->text_col_array = []; if (!is_callable($attributes)) { $model->setAttributes($attributes, false); } else { diff --git a/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/PristineFaker.php b/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/PristineFaker.php index ab484826..aac33ab7 100644 --- a/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/PristineFaker.php +++ b/tests/specs/x_db_type/rules_and_more/pgsql/app/models/pgsqlfaker/PristineFaker.php @@ -37,7 +37,7 @@ public function generateModel($attributes = []) $model->col_5 = $faker->randomFloat(); $model->col_6 = $faker->randomFloat(); $model->col_7 = $faker->randomFloat(); - $model->col_8 = $faker->words(); + $model->col_8 = []; $model->col_9 = $faker->sentence; $model->col_10 = $faker->sentence; $model->col_11 = $faker->sentence; diff --git a/tests/unit/InverseRelationsSpecTest.php b/tests/unit/InverseRelationsSpecTest.php new file mode 100644 index 00000000..fea46dd0 --- /dev/null +++ b/tests/unit/InverseRelationsSpecTest.php @@ -0,0 +1,36 @@ +runGenerator($testFile); + + $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ + 'recursive' => true, + 'except' => ['*VirtualTask*'], + ]); + $expectedFiles = FileHelper::findFiles( + Yii::getAlias('@specs/issue_fix/inverse_relations/mysql'), + ['recursive' => true, 'except' => ['*VirtualTask*']] + ); + $this->checkFiles($actualFiles, $expectedFiles); + } +}