Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0', '8.1', '8.2', '8.3']
php-versions: ['8.0', '8.1', '8.2', '8.3']

steps:
- uses: actions/checkout@v3
Expand Down Expand Up @@ -60,7 +60,7 @@ jobs:
run: make UID=0 migrate

- name: Check style
if: "!contains(matrix.php-versions, '8.')"
if: "matrix.php-versions == '8.0'"
run: make check-style-from-host

- name: Run tests
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"source": "https://github.com/cebe/yii2-openapi"
},
"require": {
"php": "^7.4 || ^8.0",
"php": "^8.0",
"cebe/php-openapi": "^1.7.0",
"yiisoft/yii2": "~2.0.48",
"yiisoft/yii2-gii": "~2.0.0 | ~2.1.0 | ~2.2.0| ~2.3.0",
Expand Down
49 changes: 35 additions & 14 deletions src/lib/FakerStubResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,37 @@ public function resolve(): ?string
return null;
}

if (!$this->property->hasAttr('example') ||
// No optional wrapping for required/non-nullable fields without an example (always generate a real value),
// or for unique-items fields (optional fallback would break uniqueness).
if (
(($this->attribute->isRequired() || $this->attribute->nullable === false) && !$this->property->hasAttr('example')) ||
$this->property->getAttr('uniqueItems')
) {
if ($this->attribute->phpType === 'string' && $this->attribute->size) {
return 'substr(' . $result . ', 0, '.$this->attribute->size.')';
}
return $result;
}

$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);

/**
* $example must be the exact value that goes into the DB column, e.g. '2020-03-14 21:42:17'
* for a datetime column — not a DateTime object or ISO string with timezone offset.
* optional() without a default returns null on miss; all -> are made nullsafe so the whole
* chain collapses to null, then ?? $example inserts the ready-to-store fallback value.
*
* Negative lookbehind prevents turning an existing ?-> into ??->
*/
$wrapped = str_replace('$faker?->', '$faker->optional(0.92)->', preg_replace('/(?<!\?)->/', '?->', $result));

if ($this->attribute->phpType === 'string' && $this->attribute->size) {
return 'is_string($s = ' . $wrapped . ') ? substr($s, 0, '.$this->attribute->size.') : ' . $example;
}

return $wrapped . ' ?? ' . $example;
}

private function fakeForString(): ?string
Expand Down Expand Up @@ -161,8 +182,7 @@ private function fakeForString(): ?string
return '$faker->title';
}
if ($this->attribute->primary || $this->attribute->isReference()) {
$size = $this->attribute->size ?? 255;
return 'substr($uniqueFaker->sha256, 0, ' . $size . ')';
return '$uniqueFaker->sha256';
}

$patterns = [
Expand Down Expand Up @@ -197,22 +217,19 @@ private function fakeForString(): ?string
'~(url|site|website|href)~i' => '$faker->url',
'~(username|login)~i' => '$faker->userName',
];
$size = $this->attribute->size > 0 ? $this->attribute->size : null;
foreach ($patterns as $pattern => $fake) {
if (preg_match($pattern, $this->attribute->columnName)) {
if ($size) {
return 'substr(' . $fake . ', 0, ' . $size . ')';
}
return $fake;
}
}

$size = $this->attribute->size > 0 ? $this->attribute->size : null;
if ($size) {
$method = 'text';
if ($size < 5) {
$method = 'word';
}
return 'substr($faker->' . $method . '(' . $size . '), 0, ' . $size . ')';
return '$faker->' . $method . '(' . $size . ')';
}
return '$faker->sentence';
}
Expand Down Expand Up @@ -389,10 +406,8 @@ private function reindentArrayMapForObject(string $code, int $depth): string
}
[$body, $count] = [$m[1], $m[2]];

// For nested array_map: wrapInArray places the inner return at 12 spaces and the inner closing
// brace at 8 spaces. We want the inner return at bodyIndent+4 and the inner brace at bodyIndent,
// so the shift is bodyIndent - 8. For simple (non-nested) bodies the shift is bodyIndent - 12.
$shift = strlen($bodyIndent) - (str_starts_with($body, 'return array_map(') ? 8 : 12);
// wrapInArray shifts nested array_map bodies by 4 spaces, so all bodies now start at 12 spaces.
$shift = strlen($bodyIndent) - 12;
if ($shift > 0) {
$body = preg_replace('/\n/', "\n" . str_repeat(' ', $shift), $body);
}
Expand Down Expand Up @@ -443,8 +458,14 @@ public function handleOneOf(SpecObjectInterface $items, int $count): string
public function wrapInArray(string $aFaker, bool $uniqueItems, int $count, bool $oneOf = false): string
{
$ret = $oneOf ? '' : 'return ';
$inner = $uniqueItems ? str_replace('$faker->', '$uniqueFaker->', $aFaker) : $aFaker;
// Only shift when the inner value is itself a nested array_map;
// handleOneOf and fakeForObject results already carry correct indentation
if (str_starts_with($inner, 'array_map(') && str_contains($inner, PHP_EOL)) {
$inner = str_replace(PHP_EOL, PHP_EOL . ' ', $inner);
}
return 'array_map(function () use ($faker, $uniqueFaker) {
' . $ret . ($uniqueItems ? str_replace('$faker->', '$uniqueFaker->', $aFaker) : $aFaker) . ';
' . $ret . $inner . ';
}, range(1, ' . $count . '))';
}

Expand Down
2 changes: 2 additions & 0 deletions src/lib/migrations/BaseMigrationBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ protected function buildRelations():void
$this->migration->dependencies[] = $refTable;
}
}
ksort($existedRelations);
foreach ($existedRelations as $fkName => $relation) {
['fkCol' => $fkCol, 'refCol' => $refCol, 'refTable' => $refTable] = $relation;
$this->migration
Expand Down Expand Up @@ -414,6 +415,7 @@ protected function buildRelationsForJunction(ManyToManyRelation $relation):void
$this->migration->dependencies[] = $refTable;
}
}
ksort($existedRelations);
foreach ($existedRelations as $fkName => $rel) {
['fkCol' => $fkCol, 'refCol' => $refCol, 'refTable' => $refTable] = $rel;
$this->migration
Expand Down
8 changes: 4 additions & 4 deletions tests/fixtures/blog.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
->setDefault('reader')
->setFakerStub('$faker->randomElement([\'admin\', \'editor\', \'reader\'])'),
'flags' => (new Attribute('flags', ['phpType'=>'int', 'dbType'=>'integer']))->setDefault(0)->setFakerStub
('$faker->numberBetween(0, 1000000)'),
('$faker->optional(0.92)->numberBetween(0, 1000000) ?? null'),
'created_at' => (new Attribute('created_at', ['phpType' => 'string', 'dbType' => 'datetime']))
->setDefault(new \yii\db\Expression('(CURRENT_TIMESTAMP)'))->setFakerStub('$faker->dateTimeThisYear(\'now\', \'UTC\')->format(\'Y-m-d H:i:s\')'),
->setDefault(new \yii\db\Expression('(CURRENT_TIMESTAMP)'))->setFakerStub('$faker->optional(0.92)->dateTimeThisYear(\'now\', \'UTC\')?->format(\'Y-m-d H:i:s\') ?? null'),
],
'relations' => [],
'indexes' => [
Expand Down Expand Up @@ -66,7 +66,7 @@
'title' => (new Attribute('title', ['phpType' => 'string', 'dbType' => 'string']))
->setRequired()->setSize(255)->setFakerStub('substr($faker->sentence, 0, 255)'),
'slug' => (new Attribute('slug', ['phpType' => 'string', 'dbType' => 'string']))
->setSize(200)->setLimits(null, null, 1)->setFakerStub('substr($uniqueFaker->slug, 0, 200)'),
->setSize(200)->setLimits(null, null, 1)->setFakerStub('is_string($s = $uniqueFaker?->slug) ? substr($s, 0, 200) : null'),
'active' => (new Attribute('active', ['phpType' => 'bool', 'dbType' => 'boolean']))
->setRequired()->setDefault(false)->setFakerStub('$faker->boolean'),
'category' => (new Attribute('category', ['phpType' => 'int', 'dbType' => 'integer']))
Expand All @@ -75,7 +75,7 @@
->setDescription('Category of posts')
->setFakerStub('$faker->randomElement(\app\models\Category::find()->select("id")->column())'),
'created_at' => (new Attribute('created_at', ['phpType' => 'string', 'dbType' => 'date']))
->setFakerStub('$faker->dateTimeThisCentury->format(\'Y-m-d\')'),
->setFakerStub('$faker->optional(0.92)->dateTimeThisCentury?->format(\'Y-m-d\') ?? null'),
'created_by' => (new Attribute('created_by', ['phpType' => 'int', 'dbType' => 'integer']))
->asReference('User')
->setDescription('The User')
Expand Down
8 changes: 8 additions & 0 deletions tests/specs/blog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,17 @@ components:
str_date:
type: string
format: date
str_date_ex:
type: string
format: date
example: '2020-03-14'
str_datetime:
type: string
format: date-time
str_datetime_ex:
type: string
format: date-time
example: '2020-03-14 21:42:17'
str_country:
type: string
Error:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ public function up()
'str_text' => $this->text()->null(),
'str_varchar' => $this->string(100)->null()->defaultValue(null),
'str_date' => $this->date()->null()->defaultValue(null),
'str_date_ex' => $this->date()->null()->defaultValue(null),
'str_datetime' => $this->timestamp()->null()->defaultValue(null),
'str_datetime_ex' => $this->timestamp()->null()->defaultValue(null),
'str_country' => $this->text()->null(),
]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ public function up()
'str_text' => $this->text()->null()->defaultValue(null),
'str_varchar' => $this->string(100)->null()->defaultValue(null),
'str_date' => $this->date()->null()->defaultValue(null),
'str_date_ex' => $this->date()->null()->defaultValue(null),
'str_datetime' => $this->timestamp()->null()->defaultValue(null),
'str_datetime_ex' => $this->timestamp()->null()->defaultValue(null),
'str_country' => $this->text()->null()->defaultValue(null),
]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ public function up()
'str_text' => $this->text()->null(),
'str_varchar' => $this->string(100)->null()->defaultValue(null),
'str_date' => $this->date()->null()->defaultValue(null),
'str_date_ex' => $this->date()->null()->defaultValue(null),
'str_datetime' => $this->timestamp()->null()->defaultValue(null),
'str_datetime_ex' => $this->timestamp()->null()->defaultValue(null),
'str_country' => $this->text()->null(),
]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ public function safeUp()
'str_text' => $this->text()->null()->defaultValue(null),
'str_varchar' => $this->string(100)->null()->defaultValue(null),
'str_date' => $this->date()->null()->defaultValue(null),
'str_date_ex' => $this->date()->null()->defaultValue(null),
'str_datetime' => $this->timestamp()->null()->defaultValue(null),
'str_datetime_ex' => $this->timestamp()->null()->defaultValue(null),
'str_country' => $this->text()->null()->defaultValue(null),
]);
}
Expand Down
32 changes: 17 additions & 15 deletions tests/specs/blog/models/FakerableFaker.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,23 @@ public function generateModel($attributes = [])
$faker = $this->faker;
$uniqueFaker = $this->uniqueFaker;
$model = new Fakerable();
//$model->id = $uniqueFaker->numberBetween(0, 1000000);
$model->active = $faker->boolean;
$model->floatval = $faker->randomFloat();
$model->floatval_lim = $faker->randomFloat(null, 0, 1);
$model->doubleval = $faker->randomFloat();
$model->int_min = $faker->numberBetween(5, 1000000);
$model->int_max = $faker->numberBetween(0, 5);
$model->int_minmax = $faker->numberBetween(5, 25);
$model->int_created_at = $faker->unixTime;
$model->int_simple = $faker->numberBetween(0, 1000000);
$model->str_text = $faker->sentence;
$model->str_varchar = substr($faker->text(100), 0, 100);
$model->str_date = $faker->dateTimeThisCentury->format('Y-m-d');
$model->str_datetime = $faker->dateTimeThisYear('now', 'UTC')->format('Y-m-d H:i:s');
$model->str_country = $faker->countryCode;
//$model->id = $uniqueFaker?->numberBetween(0, 1000000) ?? null;
$model->active = $faker->optional(0.92)->boolean ?? null;
$model->floatval = $faker->optional(0.92)->randomFloat() ?? null;
$model->floatval_lim = $faker->optional(0.92)->randomFloat(null, 0, 1) ?? null;
$model->doubleval = $faker->optional(0.92)->randomFloat() ?? null;
$model->int_min = $faker->optional(0.92)->numberBetween(5, 1000000) ?? null;
$model->int_max = $faker->optional(0.92)->numberBetween(0, 5) ?? null;
$model->int_minmax = $faker->optional(0.92)->numberBetween(5, 25) ?? null;
$model->int_created_at = $faker->optional(0.92)->unixTime ?? null;
$model->int_simple = $faker->optional(0.92)->numberBetween(0, 1000000) ?? null;
$model->str_text = $faker->optional(0.92)->sentence ?? null;
$model->str_varchar = is_string($s = $faker->optional(0.92)->text(100)) ? substr($s, 0, 100) : null;
$model->str_date = $faker->optional(0.92)->dateTimeThisCentury?->format('Y-m-d') ?? null;
$model->str_date_ex = $faker->optional(0.92)->dateTimeThisCentury?->format('Y-m-d') ?? '2020-03-14';
$model->str_datetime = $faker->optional(0.92)->dateTimeThisYear('now', 'UTC')?->format('Y-m-d H:i:s') ?? null;
$model->str_datetime_ex = $faker->optional(0.92)->dateTimeThisYear('now', 'UTC')?->format('Y-m-d H:i:s') ?? '2020-03-14 21:42:17';
$model->str_country = $faker->optional(0.92)->countryCode ?? null;
if (!is_callable($attributes)) {
$model->setAttributes($attributes, false);
} else {
Expand Down
4 changes: 2 additions & 2 deletions tests/specs/blog/models/PostFaker.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ public function generateModel($attributes = [])
$model = new Post();
$model->uid = substr($uniqueFaker->sha256, 0, 128);
$model->title = substr($faker->sentence, 0, 255);
$model->slug = substr($uniqueFaker->slug, 0, 200);
$model->slug = is_string($s = $uniqueFaker?->slug) ? substr($s, 0, 200) : null;
$model->category_id = $faker->randomElement(\app\models\Category::find()->select("id")->column());
$model->active = $faker->boolean;
$model->created_at = $faker->dateTimeThisCentury->format('Y-m-d');
$model->created_at = $faker->optional(0.92)->dateTimeThisCentury?->format('Y-m-d') ?? null;
$model->created_by_id = $faker->randomElement(\app\models\User::find()->select("id")->column());
if (!is_callable($attributes)) {
$model->setAttributes($attributes, false);
Expand Down
4 changes: 2 additions & 2 deletions tests/specs/blog/models/UserFaker.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ public function generateModel($attributes = [])
$model->email = substr($faker->safeEmail, 0, 200);
$model->password = $faker->password;
$model->role = $faker->randomElement(['admin', 'editor', 'reader']);
$model->flags = $faker->numberBetween(0, 1000000);
$model->created_at = $faker->dateTimeThisYear('now', 'UTC')->format('Y-m-d H:i:s');
$model->flags = $faker->optional(0.92)->numberBetween(0, 1000000) ?? null;
$model->created_at = $faker->optional(0.92)->dateTimeThisYear('now', 'UTC')?->format('Y-m-d H:i:s') ?? null;
if (!is_callable($attributes)) {
$model->setAttributes($attributes, false);
} else {
Expand Down
6 changes: 5 additions & 1 deletion tests/specs/blog/models/base/Fakerable.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
* @property string $str_text
* @property string $str_varchar
* @property string $str_date
* @property string $str_date_ex
* @property string $str_datetime
* @property string $str_datetime_ex
* @property string $str_country
*
*/
Expand All @@ -36,7 +38,7 @@ public static function tableName()
public function rules()
{
return [
'trim' => [['str_text', 'str_varchar', 'str_date', 'str_datetime', 'str_country'], 'trim'],
'trim' => [['str_text', 'str_varchar', 'str_date', 'str_date_ex', 'str_datetime', 'str_datetime_ex', 'str_country'], 'trim'],
'int_min_default' => [['int_min'], 'default', 'value' => 3],
'active_boolean' => [['active'], 'boolean'],
'floatval_double' => [['floatval'], 'double'],
Expand All @@ -50,7 +52,9 @@ public function rules()
'str_text_string' => [['str_text'], 'string'],
'str_varchar_string' => [['str_varchar'], 'string', 'max' => 100],
'str_date_date' => [['str_date'], 'date', 'format' => 'php:Y-m-d'],
'str_date_ex_date' => [['str_date_ex'], 'date', 'format' => 'php:Y-m-d'],
'str_datetime_datetime' => [['str_datetime'], 'datetime', 'format' => 'php:Y-m-d H:i:s'],
'str_datetime_ex_datetime' => [['str_datetime_ex'], 'datetime', 'format' => 'php:Y-m-d H:i:s'],
'str_country_string' => [['str_country'], 'string'],
];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ class m200000_000005_change_table_v2_comments extends \yii\db\Migration
{
public function safeUp()
{
$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->dropForeignKey('fk_v2_comments_author_id_v2_users_id', '{{%v2_comments}}');
$this->addColumn('{{%v2_comments}}', 'user_id', $this->bigInteger()->null()->defaultValue(null)->comment('The User'));
$this->dropColumn('{{%v2_comments}}', 'author_id');
$this->alterColumn('{{%v2_comments}}', 'message', 'text NOT NULL USING "message"::text');
Expand All @@ -33,7 +33,7 @@ public function safeDown()
$this->alterColumn('{{%v2_comments}}', 'message', "SET DEFAULT '[]'");
$this->alterColumn('{{%v2_comments}}', 'meta_data', "SET NOT NULL");
$this->alterColumn('{{%v2_comments}}', 'meta_data', "SET DEFAULT '[]'");
$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');
$this->addForeignKey('fk_v2_comments_post_id_v2_posts_uid', '{{%v2_comments}}', 'post_id', 'itt_v2_posts', 'uid');
}
}
2 changes: 1 addition & 1 deletion tests/specs/blog_v2/models/CommentFaker.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function generateModel($attributes = [])
$model->post_id = $faker->randomElement(\app\models\Post::find()->select("id")->column());
$model->user_id = $faker->randomElement(\app\models\User::find()->select("id")->column());
$model->message = $faker->sentence;
$model->meta_data = substr($faker->optional(0.92, 'type==\'ticket\' && status==\'closed\'')->text(300), 0, 300);
$model->meta_data = is_string($s = $faker->optional(0.92)->text(300)) ? substr($s, 0, 300) : 'type==\'ticket\' && status==\'closed\'';
$model->created_at = $faker->dateTimeThisYear('now', 'UTC')->format('Y-m-d H:i:s');
if (!is_callable($attributes)) {
$model->setAttributes($attributes, false);
Expand Down
Loading
Loading