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
8 changes: 8 additions & 0 deletions src/lib/ValidationRulesBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ public function build():array
}
}
foreach ($this->model->attributes as $attribute) {
// column/field/property with name `id` is considered as Primary Key by this library and it is automatically handled by DB/Yii; so remove it from validation `rules()`
if ($attribute->columnName === 'id' || $attribute->propertyName === 'id') {
continue;
}
$this->resolveAttributeRules($attribute);
}

Expand Down Expand Up @@ -181,6 +185,10 @@ private function prepareTypeScope():void
if ($attribute->isReadOnly()) {
continue;
}
// column/field/property with name `id` is considered as Primary Key by this library and it is automatically handled by DB/Yii; so remove it from validation `rules()`
if ($attribute->columnName === 'id' || $attribute->propertyName === 'id') {
continue;
}
if ($attribute->defaultValue === null && $attribute->isRequired()) {
$this->typeScope['required'][$attribute->columnName] = $attribute->columnName;
}
Expand Down
10 changes: 6 additions & 4 deletions src/lib/migrations/BaseMigrationBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
use Yii;
use yii\db\ColumnSchema;
use yii\helpers\VarDumper;
use yii\db\{Connection, Expression};
use yii\db\Connection;
use yii\db\Expression;

abstract class BaseMigrationBuilder
{
Expand Down Expand Up @@ -478,9 +479,10 @@ public static function isEnumValuesChanged(
return false;
}

public function isDefaultValueChanged(ColumnSchema $current,
ColumnSchema $desired): bool
{
public function isDefaultValueChanged(
ColumnSchema $current,
ColumnSchema $desired
): bool {
// if the default value is object of \yii\db\Expression then default value is expression instead of constant. See https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html
// in such case instead of comparing two objects, we should compare expression

Expand Down
2 changes: 1 addition & 1 deletion tests/DbTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ protected function tearDown()
}
}

protected function runGenerator($configFile, string $dbName)
protected function runGenerator($configFile, string $dbName = 'mysql')
{
$config = require $configFile;
$config['migrationPath'] = "@app/migrations_{$dbName}_db/";
Expand Down
144 changes: 144 additions & 0 deletions tests/specs/id_not_in_rules/app/models/BaseModelFaker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php

namespace app\models;

use Faker\Factory as FakerFactory;
use Faker\Generator;
use Faker\UniqueGenerator;

/**
* Base fake data generator
*/
abstract class BaseModelFaker
{
/**
* @var Generator
*/
protected $faker;
/**
* @var UniqueGenerator
*/
protected $uniqueFaker;

public function __construct()
{
$this->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));
}
}
10 changes: 10 additions & 0 deletions tests/specs/id_not_in_rules/app/models/Fruit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace app\models;

class Fruit extends \app\models\base\Fruit
{


}

41 changes: 41 additions & 0 deletions tests/specs/id_not_in_rules/app/models/FruitFaker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php
namespace app\models;

use Faker\UniqueGenerator;

/**
* Fake data generator for Fruit
* @method static Fruit makeOne($attributes = [], ?UniqueGenerator $uniqueFaker = null);
* @method static Fruit saveOne($attributes = [], ?UniqueGenerator $uniqueFaker = null);
* @method static Fruit[] make(int $number, $commonAttributes = [], ?UniqueGenerator $uniqueFaker = null)
* @method static Fruit[] save(int $number, $commonAttributes = [], ?UniqueGenerator $uniqueFaker = null)
*/
class FruitFaker extends BaseModelFaker
{

/**
* @param array|callable $attributes
* @return Fruit|\yii\db\ActiveRecord
* @example
* $model = (new PostFaker())->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 Fruit();
//$model->id = $uniqueFaker->numberBetween(0, 1000000);
$model->name = $faker->sentence;
if (!is_callable($attributes)) {
$model->setAttributes($attributes, false);
} else {
$model = $attributes($model, $faker, $uniqueFaker);
}
return $model;
}
}
27 changes: 27 additions & 0 deletions tests/specs/id_not_in_rules/app/models/base/Fruit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace app\models\base;

/**
* Test model for model code generation that should not contain id column in rules
*
* @property int $id
* @property string $name
*
*/
abstract class Fruit extends \yii\db\ActiveRecord
{
public static function tableName()
{
return '{{%fruits}}';
}

public function rules()
{
return [
'trim' => [['name'], 'trim'],
'required' => [['name'], 'required'],
'name_string' => [['name'], 'string'],
];
}
}
13 changes: 13 additions & 0 deletions tests/specs/id_not_in_rules/id_not_in_rules.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

return [
'openApiPath' => '@specs/id_not_in_rules/id_not_in_rules.yaml',
'generateUrls' => false,
'generateModels' => true,
'excludeModels' => [
'Error',
],
'generateControllers' => false,
'generateMigrations' => false,
'generateModelFaker' => true,
];
26 changes: 26 additions & 0 deletions tests/specs/id_not_in_rules/id_not_in_rules.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: ID not in rules test
paths:
/:
get:
summary: List
operationId: list
responses:
'200':
description: The information

components:
schemas:
Fruit:
type: object
description: Test model for model code generation that should not contain id column in rules
required:
- id
- name
properties:
id:
type: integer
name:
type: string
23 changes: 23 additions & 0 deletions tests/unit/IdNotInRulesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace tests\unit;

use Yii;
use tests\DbTestCase;
use yii\helpers\FileHelper;

class IdNotInRulesTest extends DbTestCase
{
public function testIdPkNotInRules()
{
$testFile = Yii::getAlias("@specs/id_not_in_rules/id_not_in_rules.php");
$this->runGenerator($testFile);
$actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [
'recursive' => true,
]);
$expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/id_not_in_rules/app"), [
'recursive' => true,
]);
$this->checkFiles($actualFiles, $expectedFiles);
}
}
2 changes: 1 addition & 1 deletion tests/unit/MultiDbFreshMigrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ protected function tearDown()
}
}

protected function runGenerator($configFile, string $dbName)
protected function runGenerator($configFile, string $dbName = 'mysql')
{
$config = require $configFile;
$config['migrationPath'] = "@app/migrations_{$dbName}_db/";
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/MultiDbSecondaryMigrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ protected function tearDown()
}
}

protected function runGenerator($configFile, string $dbName)
protected function runGenerator($configFile, string $dbName = 'mysql')
{
$config = require $configFile;
$config['migrationPath'] = "@app/migrations_{$dbName}_db/";
Expand Down