diff --git a/Makefile b/Makefile index 4d67097c..72e173f8 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,9 @@ up: cli: docker-compose exec --user=$(UID) php bash +cli_root: + docker-compose exec --user="root" php bash + cli_mysql: docker-compose exec --user=$(UID) mysql bash diff --git a/README.md b/README.md index 0ca03ff5..0084d741 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,9 @@ return $config; To use the web generator, open `index.php?r=gii` and select the `REST API Generator`. -On console you can run the generator with `./yii gii/api --openApiPath=@app/openapi.yaml`. Where `@app/openapi.yaml` should be the absolute path to your OpenAPI spec file. This can be JSON as well as YAML (see also [php-openapi/php-openapi](https://github.com/php-openapi/php-openapi/) for supported formats). +On console, you can run the generator with `./yii gii/api --openApiPath=@app/openapi.yaml`. Where `@app/openapi.yaml` +should be the absolute path to your OpenAPI spec file. This can be JSON as well as YAML (see +also [php-openapi/php-openapi](https://github.com/php-openapi/php-openapi/) for supported formats). Run `./yii gii/api --help` for all options. Example: Disable generation of migrations files `./yii gii/api --generateMigrations=0` @@ -317,6 +319,60 @@ Provide custom database table column name in case of relationship column. This w - x-fk-column-name: redelivery_of # this will create `redelivery_of` column instead of `redelivery_of_id` ``` +### `x-no-relation` + +To differentiate a component schema property from one-to-many or many-to-many relation in favour of array(json) of +related objects, `x-no-relation` (type: boolean, default: false) is used. + +```yaml + comments: + type: array + items: + $ref: "#/components/schemas/Comment" +``` + +This will not generate 'comments' column in database migrations. But it will generate `getComments()` relation in Yii model file. + +In order to make it real database column, extension `x-no-relation` can be used. + +```yaml + comments: + type: array + x-no-relation: true + items: + $ref: "#/components/schemas/Comment" +``` + +Database column type can be `array`, `json` etc. to store such data. + +Now if the Comment schema from the above example is + +```yaml + Comment: + properties: + id: + type: integer + content: + type: string +``` + +then the value for `comments` can be + +```json +[ + { + "id": 1, + "content": "Hi there" + }, + { + "id": 2, + "content": "Hi there 2" + } +] +``` + +`x-no-relation` can be only used with OpenAPI schema data type `array`. + ### `x-route` To customize route (controller ID/action ID) for a path, use custom key `x-route` with value `/`. It can be used for non-crud paths. It must be used under HTTP method key but not @@ -399,8 +455,8 @@ There are two ways for define many-to-many relations: ### Simple many-to-many without junction model - property name for many-to-many relation should be equal lower-cased, pluralized related schema name - - - referenced schema should contains mirrored reference to current schema + +- referenced schema should contain mirrored reference to current schema - migration for junction table can be generated automatically - table name should be [pluralized, lower-cased schema_name1]2[pluralized, lower-cased schema name2], in alphabetical order; @@ -591,12 +647,13 @@ created_at: ## Assumptions When generating code from an OpenAPI description there are many possible ways to achive a fitting result. -Thus there are some assumptions and limitations that are currently applied to make this work. +Thus, there are some assumptions and limitations that are currently applied to make this work. Here is a (possibly incomplete) list: - The current implementation works best with OpenAPI description that follows the [JSON:API](https://jsonapi.org/) guidelines. - The request and response format/schema is currently not extracted from OpenAPI schema and may need to be adjusted manually if it does not follow JSON:API -- 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()` +- 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()` - other fields can currently be used as primary keys using the `x-pk` OpenAPI extension (see below) but it may not be work correctly in all cases, please report bugs if you find them. Other things to keep in mind: diff --git a/composer.json b/composer.json index 22556abf..c8994ba8 100644 --- a/composer.json +++ b/composer.json @@ -19,13 +19,14 @@ }, "require": { "php": "^7.4 || ^8.0", - "cebe/php-openapi": "^1.5.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", "laminas/laminas-code": ">=3.4 <=4.13", "php-openapi/yii2-fractal": "^1.0.0", "fakerphp/faker": "^1.9", "sam-it/yii2-mariadb": "^2.0", + "symfony/var-exporter": "^5.4", "symfony/polyfill-php80": "^1.30" }, "require-dev": { diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index f8e9edca..844fc10c 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -219,7 +219,13 @@ protected function resolveProperty( $nullableValue = $property->getProperty()->getSerializableData()->nullable ?? null; } $attribute = Yii::createObject(Attribute::class, [$property->getName()]); + + if (!empty($property->getAttr(CustomSpecAttr::NO_RELATION))) { + $this->attributes[$property->getName()] = $attribute->setFakerStub($this->guessFakerStub($attribute, $property)); + } + $attribute->setRequired($isRequired) + ->setPhpType($property->guessPhpType()) ->setDescription($property->getAttr('description', '')) ->setReadOnly($property->isReadonly()) ->setDefault($property->guessDefault()) @@ -227,7 +233,8 @@ protected function resolveProperty( ->setXDbDefaultExpression($property->getAttr(CustomSpecAttr::DB_DEFAULT_EXPRESSION)) ->setNullable($nullableValue) ->setIsPrimary($property->isPrimaryKey()) - ->setForeignKeyColumnName($property->fkColName); + ->setForeignKeyColumnName($property->fkColName) + ->setFakerStub($this->guessFakerStub($attribute, $property)); if ($property->isReference()) { if ($property->isVirtual()) { throw new InvalidDefinitionException('References not supported for virtual attributes'); @@ -261,7 +268,8 @@ protected function resolveProperty( ->setSize($fkProperty->getMaxLength()) ->setDescription($property->getRefSchema()->getDescription()) ->setDefault($fkProperty->guessDefault()) - ->setLimits($min, $max, $fkProperty->getMinLength()); + ->setLimits($min, $max, $fkProperty->getMinLength()) + ->setFakerStub($this->guessFakerStub($attribute, $property)); $relation = Yii::createObject( AttributeRelation::class, diff --git a/src/lib/CustomSpecAttr.php b/src/lib/CustomSpecAttr.php index 1c87e4db..550f6827 100644 --- a/src/lib/CustomSpecAttr.php +++ b/src/lib/CustomSpecAttr.php @@ -41,6 +41,11 @@ class CustomSpecAttr */ public const FK_COLUMN_NAME = 'x-fk-column-name'; + /** + * Foreign key column name. See README for usage docs + */ + public const NO_RELATION = 'x-no-relation'; + /** * Custom route (controller ID/action ID) instead of auto-generated. See README for usage docs. https://github.com/cebe/yii2-openapi/issues/144 */ diff --git a/src/lib/FakerStubResolver.php b/src/lib/FakerStubResolver.php index bd11bc2a..80eb7ec0 100644 --- a/src/lib/FakerStubResolver.php +++ b/src/lib/FakerStubResolver.php @@ -7,10 +7,27 @@ /** @noinspection InterfacesAsConstructorDependenciesInspection */ /** @noinspection PhpUndefinedFieldInspection */ + namespace cebe\yii2openapi\lib; +use cebe\openapi\exceptions\IOException; +use cebe\openapi\exceptions\TypeErrorException; +use cebe\openapi\exceptions\UnresolvableReferenceException; +use cebe\openapi\ReferenceContext; +use cebe\openapi\spec\Reference; +use cebe\openapi\spec\Schema; +use cebe\openapi\SpecObjectInterface; +use cebe\yii2openapi\lib\exceptions\InvalidDefinitionException; use cebe\yii2openapi\lib\items\Attribute; +use cebe\yii2openapi\lib\items\JunctionSchemas; +use cebe\yii2openapi\lib\openapi\ComponentSchema; use cebe\yii2openapi\lib\openapi\PropertySchema; +use stdClass; +use Symfony\Component\VarExporter\Exception\ExceptionInterface; +use Symfony\Component\VarExporter\VarExporter; +use Yii; +use yii\base\InvalidConfigException; +use yii\helpers\Json; use yii\helpers\VarDumper; use function str_replace; use const PHP_EOL; @@ -22,18 +39,12 @@ class FakerStubResolver { public const MAX_INT = 1000000; - /** - * @var \cebe\yii2openapi\lib\items\Attribute - */ - private $attribute; - /** - * @var \cebe\yii2openapi\lib\openapi\PropertySchema - */ - private $property; + private Attribute $attribute; - /** @var Config */ - private $config; + private PropertySchema $property; + + private ?Config $config; public function __construct(Attribute $attribute, PropertySchema $property, ?Config $config = null) { @@ -42,7 +53,15 @@ public function __construct(Attribute $attribute, PropertySchema $property, ?Con $this->config = $config; } - public function resolve():?string + /** + * @throws InvalidConfigException + * @throws TypeErrorException + * @throws UnresolvableReferenceException + * @throws InvalidDefinitionException + * @throws ExceptionInterface + * @throws IOException + */ + public function resolve(): ?string { if ($this->property->xFaker === false) { $this->attribute->setFakerStub(null); @@ -61,38 +80,52 @@ public function resolve():?string return null; } - // column name ends with `_id` + // column name ends with `_id`/FK if (substr($this->attribute->columnName, -3) === '_id' || !empty($this->attribute->fkColName)) { $config = $this->config; if (!$config) { $config = new Config; } $mn = $config->modelNamespace; - return '$faker->randomElement(\\'.$mn - . ($mn ? '\\' : '') - . ucfirst($this->attribute->reference).'::find()->select("id")->column())'; + return '$faker->randomElement(\\' . $mn + . ($mn ? '\\' : '') + . ucfirst((string)$this->attribute->reference) . '::find()->select("id")->column())'; // TODO PK "id" can be also something else } $limits = $this->attribute->limits; - switch ($this->attribute->phpType) { - case 'bool': - return '$faker->boolean'; - case 'int': - case 'integer': - return $this->fakeForInt($limits['min'], $limits['max']); - case 'string': - return $this->fakeForString(); - case 'float': - case 'double': - return $this->fakeForFloat($limits['min'], $limits['max']); - case 'array': - return $this->fakeForArray(); - default: - return null; + + if ($this->attribute->phpType === 'bool') { + $result = '$faker->boolean'; + } elseif (in_array($this->attribute->phpType, ['int', 'integer'])) { + $result = $this->fakeForInt($limits['min'], $limits['max']); + } elseif ($this->attribute->phpType === 'string') { + $result = $this->fakeForString(); + } elseif (in_array($this->attribute->phpType, ['float', 'double'])) { + $result = $this->fakeForFloat($limits['min'], $limits['max']); + } elseif ($this->attribute->phpType === 'array' || + substr($this->attribute->phpType, -2) === '[]') { + $result = $this->fakeForArray($this->property->getProperty()); + if ($result !== '$faker->words()') { # example for array will only work with a list/`$faker->words()` + return $result; + } + } elseif ($this->attribute->phpType === 'object') { + $result = $this->fakeForObject($this->property->getProperty()); + } else { + return null; + } + + if (!$this->property->hasAttr('example') || + $this->property->getAttr('uniqueItems') + ) { + return $result; } + + $example = $this->property->getAttr('example'); + $example = VarExporter::export($example); + return str_replace('$faker->', '$faker->optional(0.92, ' . $example . ')->', $result); } - private function fakeForString():?string + private function fakeForString(): ?string { $formats = [ 'date' => '$faker->dateTimeThisCentury->format(\'Y-m-d\')', @@ -112,7 +145,7 @@ private function fakeForString():?string } $enum = $this->property->getAttr('enum'); if (!empty($enum) && is_array($enum)) { - $items = str_replace([PHP_EOL, ' ',',]'], ['', '', ']'], VarDumper::export($enum)); + $items = str_replace([PHP_EOL, ' ', ',]'], ['', '', ']'], VarDumper::export($enum)); return '$faker->randomElement(' . $items . ')'; } if ($this->attribute->columnName === 'title' @@ -157,7 +190,7 @@ 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; + $size = $this->attribute->size > 0 ? $this->attribute->size : null; foreach ($patterns as $pattern => $fake) { if (preg_match($pattern, $this->attribute->columnName)) { if ($size) { @@ -167,19 +200,17 @@ private function fakeForString():?string } } - // TODO maybe also consider OpenAPI examples here - if ($size) { $method = 'text'; if ($size < 5) { $method = 'word'; } - return 'substr($faker->'.$method.'(' . $size . '), 0, ' . $size . ')'; + return 'substr($faker->' . $method . '(' . $size . '), 0, ' . $size . ')'; } return '$faker->sentence'; } - private function fakeForInt(?int $min, ?int $max):?string + private function fakeForInt(?int $min, ?int $max): ?string { $fakerVariable = 'faker'; if (preg_match('~_?id$~', $this->attribute->columnName)) { @@ -190,7 +221,7 @@ private function fakeForInt(?int $min, ?int $max):?string } if ($min !== null) { - return "\${$fakerVariable}->numberBetween($min, ".self::MAX_INT.")"; + return "\${$fakerVariable}->numberBetween($min, " . self::MAX_INT . ")"; } if ($max !== null) { @@ -208,10 +239,10 @@ private function fakeForInt(?int $min, ?int $max):?string return $fake; } } - return "\${$fakerVariable}->numberBetween(0, ".self::MAX_INT.")"; + return "\${$fakerVariable}->numberBetween(0, " . self::MAX_INT . ")"; } - private function fakeForFloat(?int $min, ?int $max):?string + private function fakeForFloat(?int $min, ?int $max): ?string { if ($min !== null && $max !== null) { return "\$faker->randomFloat(null, $min, $max)"; @@ -225,11 +256,178 @@ private function fakeForFloat(?int $min, ?int $max):?string return '$faker->randomFloat()'; } - private function fakeForArray():string + /** + * @param int $count let's set a number to default number of elements + * @throws InvalidConfigException + * @throws TypeErrorException + * @throws UnresolvableReferenceException + * @throws InvalidDefinitionException|ExceptionInterface + * @throws IOException + */ + private function fakeForArray(SpecObjectInterface $property, int $count = 4): string { - if ($this->attribute->required) { - return '["a" => "b"]'; + $uniqueItems = false; + if ($property->minItems) { + $count = $property->minItems; } + if ($property->maxItems) { + $maxItems = $property->maxItems; + if ($maxItems < $count) { + $count = $maxItems; + } + } + if (!empty($property->uniqueItems)) { + $uniqueItems = $property->uniqueItems; + } + + /** @var Schema|Reference|null $items */ + $items = $property->items; + + if (!$items) { + return $this->arbitraryArray(); + } + + if ($items instanceof Reference) { + $aFakerForRef = $this->aElementFaker($items, $this->attribute->columnName); + return $this->wrapInArray($aFakerForRef, $uniqueItems, $count); + } + if (!empty($items->oneOf)) { + return $this->wrapInArray($this->handleOneOf($items, $count), $uniqueItems, $count, true); + } + + $type = $items->type; + if ($type === null) { + return $this->arbitraryArray(); + } + $aFaker = $this->aElementFaker($this->property->getProperty(), $this->attribute->columnName); + if (in_array($type, ['string', 'number', 'integer', 'boolean', 'array'])) { + return $this->wrapInArray($aFaker, $uniqueItems, $count); + } + + if ($type === 'object') { + $result = $this->fakeForObject($items); + return $this->wrapInArray($result, $uniqueItems, $count); + } + return '[]'; } + + /** + * @internal + */ + public function fakeForObject(SpecObjectInterface $items): string + { + if (!$items->properties) { + return $this->arbitraryArray(); + } + + $props = '[' . PHP_EOL; + + foreach ($items->properties as $name => $prop) { + /** @var SpecObjectInterface $prop */ + + if (!empty($prop->properties)) { // nested object + $result = $this->{__FUNCTION__}($prop); + } else { + $result = $this->aElementFaker(['items' => $prop->getSerializableData()], $name); + } + $props .= '\'' . $name . '\' => ' . $result . ',' . PHP_EOL; + } + + $props .= ']'; + + return $props; + } + + /** + * This method must be only used incase of array + * @param SpecObjectInterface $items + * @param int $count + * @return string + * @throws ExceptionInterface + * @throws IOException + * @throws InvalidConfigException + * @throws InvalidDefinitionException + * @throws TypeErrorException + * @throws UnresolvableReferenceException + * @internal + */ + public function handleOneOf(SpecObjectInterface $items, int $count): string + { + $result = ''; + 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); + $result .= '$dataType' . $key . ' = ' . $aFaker . ';'; + } + $ct = count($items->oneOf) - 1; + $result .= 'return ${"dataType".rand(0, ' . $ct . ')}'; + return $result; + } + + public function wrapInArray(string $aFaker, bool $uniqueItems, int $count, bool $oneOf = false): string + { + $ret = $oneOf ? '' : 'return '; + return 'array_map(function () use ($faker, $uniqueFaker) { + ' . $ret . ($uniqueItems ? str_replace('$faker->', '$uniqueFaker->', $aFaker) : $aFaker) . '; + }, range(1, ' . $count . '))'; + } + + public function arbitraryArray(): string + { + $theFaker = $this->property->getAttr('uniqueItems') ? '$uniqueFaker' : '$faker'; + return $theFaker . '->words()'; + } + + /** + * This method is only for `fakeForArray()` or methods only used inside `fakeForArray()`. If needed to use outside `fakeForArray()` context then some changes might be required. + * Also see OpenAPI extension `x-no-relation` in README.md + * @param $data array|stdClass|SpecObjectInterface + * @param string|null $columnName + * @return string|null + * @throws ExceptionInterface + * @throws IOException + * @throws InvalidConfigException + * @throws InvalidDefinitionException + * @throws TypeErrorException + * @throws UnresolvableReferenceException + * @internal + */ + public function aElementFaker($data, ?string $columnName = null): ?string + { + if ($data instanceof Reference) { + $class = str_replace('#/components/schemas/', '', $data->getReference()); + $class .= 'Faker'; + return '(new ' . $class . ')->generateModel()->attributes'; + } + + $inp = $data instanceof SpecObjectInterface ? $data->getSerializableData() : $data; + $aElementData = Json::decode(Json::encode($inp)); + $columnName = $columnName ?? 'unnamedProp'; + $compoSchemaData = [ + 'properties' => [ + $columnName => $aElementData['items'] + ] + ]; + + // This condition is only for properties with type = array + // If you intend to use this method from out of `fakeForArray()` context then below condition should be changed depending on your use case + // Also see OpenAPI extension `x-no-relation` in README.md + if (!empty($compoSchemaData['properties'][$columnName]['items']['$ref'])) { + $compoSchemaData['properties'][$columnName][CustomSpecAttr::NO_RELATION] = true; + } + + $schema = new Schema($compoSchemaData); + $compo = 'UnnamedCompo'; + $cs = new ComponentSchema($schema, $compo); + if ($this->config) { + $rc = new ReferenceContext($this->config->getOpenApi(), Yii::getAlias($this->config->openApiPath)); + $schema->setReferenceContext($rc); + } + $dbModels = (new AttributeResolver($compo, $cs, new JunctionSchemas([]), $this->config))->resolve(); + + return (new static($dbModels->attributes[$columnName], $cs->getProperty($columnName), $this->config))->resolve(); + } } diff --git a/src/lib/items/Attribute.php b/src/lib/items/Attribute.php index 0907426b..909a485d 100644 --- a/src/lib/items/Attribute.php +++ b/src/lib/items/Attribute.php @@ -7,20 +7,14 @@ namespace cebe\yii2openapi\lib\items; +use cebe\yii2openapi\db\ColumnSchema; +use cebe\yii2openapi\lib\exceptions\InvalidDefinitionException; use cebe\yii2openapi\lib\helpers\FormatHelper; -use yii\helpers\VarDumper; -use \Yii; use cebe\yii2openapi\lib\openapi\PropertySchema; -use cebe\yii2openapi\generator\ApiGenerator; -use cebe\yii2openapi\lib\exceptions\InvalidDefinitionException; use yii\base\BaseObject; -use cebe\yii2openapi\db\ColumnSchema; -use yii\helpers\Inflector; -use yii\helpers\StringHelper; -use yii\db\mysql\Schema as MySqlSchema; -use SamIT\Yii2\MariaDb\Schema as MariaDbSchema; -use yii\db\pgsql\Schema as PgSqlSchema; +use yii\base\InvalidConfigException; use yii\base\NotSupportedException; +use yii\helpers\Inflector; use function is_array; use function strtolower; @@ -308,7 +302,7 @@ public function getPropertyAnnotation(): string return $annotation; } - public function toColumnSchema():ColumnSchema + public function toColumnSchema(): ColumnSchema { $column = new ColumnSchema([ 'name' => $this->columnName, @@ -338,7 +332,11 @@ public function toColumnSchema():ColumnSchema } /** - * @throws \yii\base\InvalidConfigException + * @param string $dbType + * @return string + * @throws InvalidDefinitionException + * @throws NotSupportedException + * @throws InvalidConfigException */ private function yiiAbstractTypeForDbSpecificType(string $dbType): string { diff --git a/src/lib/openapi/PropertySchema.php b/src/lib/openapi/PropertySchema.php index e4a282dd..38f1873c 100644 --- a/src/lib/openapi/PropertySchema.php +++ b/src/lib/openapi/PropertySchema.php @@ -408,8 +408,6 @@ public function guessPhpType():string return 'bool'; case 'number': // can be double and float return $this->getAttr('format') === 'double' ? 'double' : 'float'; -// case 'array': -// return $property->type; default: return $this->getAttr('type', 'string'); } diff --git a/tests/fixtures/blog.php b/tests/fixtures/blog.php index 74b04000..3c24df46 100644 --- a/tests/fixtures/blog.php +++ b/tests/fixtures/blog.php @@ -117,9 +117,11 @@ ->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('["a" => "b"]'), + ->setRequired()->setDefault([])->setFakerStub('$faker->words()'), 'meta_data' => (new Attribute('meta_data', ['phpType' => 'array', 'dbType' => 'json', 'xDbType' => 'json'])) - ->setDefault([])->setFakerStub('[]'), + ->setDefault([])->setFakerStub('array_map(function () use ($faker, $uniqueFaker) { + return $faker->words(); + }, range(1, 4))'), 'created_at' => (new Attribute('created_at',['phpType' => 'int', 'dbType' => 'integer'])) ->setRequired()->setFakerStub('$faker->unixTime'), ], diff --git a/tests/fixtures/non-db.php b/tests/fixtures/non-db.php index 220a486d..04714844 100644 --- a/tests/fixtures/non-db.php +++ b/tests/fixtures/non-db.php @@ -21,7 +21,7 @@ 'catsCount' => (new Attribute('catsCount', ['phpType' => 'int', 'dbType' => 'integer'])), 'summary' => (new Attribute('summary', ['phpType' => 'string', 'dbType' => 'text'])), 'parentPet' => (new Attribute('parentPet', ['phpType' => 'int', 'dbType' => 'bigint'])) - ->asReference('Pet')->setDescription('A Pet'), + ->asReference('Pet')->setDescription('A Pet')->setFakerStub('$faker->randomElement(\app\models\Pet::find()->select("id")->column())'), ], 'relations' => [ 'parentPet' => new AttributeRelation('parentPet', 'pets', 'Pet', 'hasOne', ['id' => 'parentPet_id']), diff --git a/tests/specs/blog/models/CommentFaker.php b/tests/specs/blog/models/CommentFaker.php index 368d541c..723562d8 100644 --- a/tests/specs/blog/models/CommentFaker.php +++ b/tests/specs/blog/models/CommentFaker.php @@ -32,8 +32,10 @@ public function generateModel($attributes = []) //$model->id = $uniqueFaker->numberBetween(0, 1000000); $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 = ["a" => "b"]; - $model->meta_data = []; + $model->message = $faker->words(); + $model->meta_data = array_map(function () use ($faker, $uniqueFaker) { + return $faker->words(); + }, range(1, 4)); $model->created_at = $faker->unixTime; if (!is_callable($attributes)) { $model->setAttributes($attributes, false); diff --git a/tests/specs/blog_v2.yaml b/tests/specs/blog_v2.yaml index 68d09367..e4ab5a48 100644 --- a/tests/specs/blog_v2.yaml +++ b/tests/specs/blog_v2.yaml @@ -349,6 +349,7 @@ components: $ref: "#/components/schemas/User" comments: type: array + # x-no-relation: true items: $ref: "#/components/schemas/Comment" tags: diff --git a/tests/specs/blog_v2/models/CommentFaker.php b/tests/specs/blog_v2/models/CommentFaker.php index 968d1a10..68535e74 100644 --- a/tests/specs/blog_v2/models/CommentFaker.php +++ b/tests/specs/blog_v2/models/CommentFaker.php @@ -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->text(300), 0, 300); + $model->meta_data = substr($faker->optional(0.92, 'type==\'ticket\' && status==\'closed\'')->text(300), 0, 300); $model->created_at = $faker->dateTimeThisYear('now', 'UTC')->format('Y-m-d H:i:s'); if (!is_callable($attributes)) { $model->setAttributes($attributes, false); diff --git a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/index.php b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/index.php new file mode 100644 index 00000000..14397d3e --- /dev/null +++ b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/index.php @@ -0,0 +1,13 @@ + '@specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/index.yaml', + 'generateUrls' => false, + 'generateModels' => true, +// 'excludeModels' => [ +// 'Error', +// ], + 'generateControllers' => false, + 'generateMigrations' => true, + 'generateModelFaker' => true, // `generateModels` must be `true` in order to use `generateModelFaker` as `true` +]; diff --git a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/index.yaml b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/index.yaml new file mode 100644 index 00000000..97638e4b --- /dev/null +++ b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/index.yaml @@ -0,0 +1,216 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + title: Consider OpenAPI spec examples in faker code generation https://github.com/php-openapi/yii2-openapi/issues/20. And also generate faker for arrays + +paths: + /pet: + get: + summary: get a pet + operationId: aPet + responses: + 200: + description: A pet + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + +components: + schemas: + User: + properties: + id: + type: integer + name: + type: string + Fruit: + properties: + id: + type: integer + name: + type: string + Pet: + required: + - id + - name + properties: + id: + type: integer + name: + type: string + example: cat + age: + type: integer + example: 2 + tags: + type: array + items: + type: string + tags_arbit: + type: array + items: { } # array of arbitrary types e.g. [ "hello", -2, true, [5.7], {"id": 5} ] + minItems: 6 + maxItems: 10 + # uniqueItems: true + example: [ 'long-tail', 'short-tail', 'black', 'white' ] + number_arr: + type: array + items: + type: number + + number_arr_min_uniq: + type: array + items: + type: number + minItems: 6 + uniqueItems: true + + int_arr: + type: array + # uniqueItems: true + example: [ 4, 5 ] + items: + type: integer + + int_arr_min_uniq: + type: array + items: + type: integer + minItems: 7 + uniqueItems: true + + bool_arr: + type: array + items: + type: boolean + + arr_arr_int: # [ [1, 2], [3, 4], [5, 6, 7] ] + type: array + items: + type: array + items: + type: integer + + arr_arr_str: + type: array + items: + type: array + items: + type: string + + arr_arr_arr_str: + type: array + minItems: 3 + items: + type: array + minItems: 4 + items: + type: array + minItems: 5 + items: + type: string + + arr_of_obj: + type: array + minItems: 3 + items: + type: object + properties: + id: + type: integer + name: + type: string + age: + type: integer + minimum: 0 + maximum: 200 + user: + $ref: '#/components/schemas/User' + user_2: + type: array + # x-no-relation: true # it is not required since we only implemented handling of such object for arrays only + items: + $ref: '#/components/schemas/User' + tags: + type: array + items: + type: string + uniqueItems: true + arr_arr_int_2: # [ [1, 2], [3, 4], [5, 6, 7] ] + type: array + items: + type: array + minItems: 11 + items: + type: integer + appearance: + type: object + properties: + height: + type: integer + maximum: 20 + weight: + type: integer + email: + type: string + format: email + nested_obj: + type: object + properties: + id: + type: integer + title: + type: string + maxLength: 4 + + user_ref_obj_arr_normal: # faker for this won't be generated + type: array + maxItems: 3 + items: + $ref: '#/components/schemas/User' + + user_ref_obj_arr: # special + type: array + maxItems: 3 + x-no-relation: true # it is required because this property is not part of any array + items: + $ref: '#/components/schemas/User' + + one_of_arr: + type: array # ["foo", 5, -2, "bar"] + maxItems: 8 + items: + oneOf: + - type: integer + - type: string + - type: boolean + + one_of_arr_complex: + type: array + minItems: 8 + items: + oneOf: + - type: integer + - type: string + - type: boolean + - type: array + - type: array + items: + type: string + - type: object + properties: + id: + type: integer + - type: array + items: + $ref: '#/components/schemas/User' + - $ref: '#/components/schemas/Fruit' + + one_of_from_multi_ref_arr: + type: array + # x-no-relation: true # it is not required since we only implemented handling of oneOf for arrays only + items: + oneOf: + - $ref: '#/components/schemas/User' + - $ref: '#/components/schemas/Fruit' diff --git a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/migrations_mysql_db/m200000_000000_create_table_fruits.php b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/migrations_mysql_db/m200000_000000_create_table_fruits.php new file mode 100644 index 00000000..18bf5d9f --- /dev/null +++ b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/migrations_mysql_db/m200000_000000_create_table_fruits.php @@ -0,0 +1,20 @@ +createTable('{{%fruits}}', [ + 'id' => $this->primaryKey(), + 'name' => $this->text()->null(), + ]); + } + + public function down() + { + $this->dropTable('{{%fruits}}'); + } +} diff --git a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/migrations_mysql_db/m200000_000001_create_table_pets.php b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/migrations_mysql_db/m200000_000001_create_table_pets.php new file mode 100644 index 00000000..01df98f3 --- /dev/null +++ b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/migrations_mysql_db/m200000_000001_create_table_pets.php @@ -0,0 +1,36 @@ +createTable('{{%pets}}', [ + 'id' => $this->primaryKey(), + 'name' => $this->text()->notNull(), + 'age' => $this->integer()->null()->defaultValue(null), + 'tags' => $this->text()->null(), + 'tags_arbit' => $this->text()->null(), + 'number_arr' => $this->text()->null(), + 'number_arr_min_uniq' => $this->text()->null(), + 'int_arr' => $this->text()->null(), + 'int_arr_min_uniq' => $this->text()->null(), + 'bool_arr' => $this->text()->null(), + 'arr_arr_int' => $this->text()->null(), + 'arr_arr_str' => $this->text()->null(), + 'arr_arr_arr_str' => $this->text()->null(), + 'arr_of_obj' => $this->text()->null(), + 'user_ref_obj_arr' => $this->string()->null()->defaultValue(null), + 'one_of_arr' => $this->text()->null(), + 'one_of_arr_complex' => $this->text()->null(), + 'one_of_from_multi_ref_arr' => $this->text()->null(), + ]); + } + + public function down() + { + $this->dropTable('{{%pets}}'); + } +} diff --git a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/migrations_mysql_db/m200000_000002_create_table_users.php b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/migrations_mysql_db/m200000_000002_create_table_users.php new file mode 100644 index 00000000..0aa915a3 --- /dev/null +++ b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/migrations_mysql_db/m200000_000002_create_table_users.php @@ -0,0 +1,20 @@ +createTable('{{%users}}', [ + 'id' => $this->primaryKey(), + 'name' => $this->text()->null(), + ]); + } + + public function down() + { + $this->dropTable('{{%users}}'); + } +} diff --git a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/BaseModelFaker.php b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/BaseModelFaker.php new file mode 100644 index 00000000..c367fbb4 --- /dev/null +++ b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/BaseModelFaker.php @@ -0,0 +1,144 @@ +faker = FakerFactory::create(str_replace('-', '_', \Yii::$app->language)); + $this->uniqueFaker = new UniqueGenerator($this->faker); + } + + abstract public function generateModel($attributes = []); + + public function getFaker():Generator + { + return $this->faker; + } + + public function getUniqueFaker():UniqueGenerator + { + return $this->uniqueFaker; + } + + public function setFaker(Generator $faker):void + { + $this->faker = $faker; + } + + public function setUniqueFaker(UniqueGenerator $faker):void + { + $this->uniqueFaker = $faker; + } + + /** + * Generate and return model + * @param array|callable $attributes + * @param UniqueGenerator|null $uniqueFaker + * @return \yii\db\ActiveRecord + * @example MyFaker::makeOne(['user_id' => 1, 'title' => 'foo']); + * @example MyFaker::makeOne( function($model, $faker) { + * $model->scenario = 'create'; + * $model->setAttributes(['user_id' => 1, 'title' => $faker->sentence]); + * return $model; + * }); + */ + public static function makeOne($attributes = [], ?UniqueGenerator $uniqueFaker = null) + { + $fakeBuilder = new static(); + if ($uniqueFaker !== null) { + $fakeBuilder->setUniqueFaker($uniqueFaker); + } + $model = $fakeBuilder->generateModel($attributes); + return $model; + } + + /** + * Generate, save and return model + * @param array|callable $attributes + * @param UniqueGenerator|null $uniqueFaker + * @return \yii\db\ActiveRecord + * @example MyFaker::saveOne(['user_id' => 1, 'title' => 'foo']); + * @example MyFaker::saveOne( function($model, $faker) { + * $model->scenario = 'create'; + * $model->setAttributes(['user_id' => 1, 'title' => $faker->sentence]); + * return $model; + * }); + */ + public static function saveOne($attributes = [], ?UniqueGenerator $uniqueFaker = null) + { + $model = static::makeOne($attributes, $uniqueFaker); + $model->save(); + return $model; + } + + /** + * Generate and return multiple models + * @param int $number + * @param array|callable $commonAttributes + * @return \yii\db\ActiveRecord[]|array + * @example TaskFaker::make(5, ['project_id'=>1, 'user_id' => 2]); + * @example TaskFaker::make(5, function($model, $faker, $uniqueFaker) { + * $model->setAttributes(['name' => $uniqueFaker->username, 'state'=>$faker->boolean(20)]); + * return $model; + * }); + */ + public static function make(int $number, $commonAttributes = [], ?UniqueGenerator $uniqueFaker = null):array + { + if ($number < 1) { + return []; + } + $fakeBuilder = new static(); + if ($uniqueFaker !== null) { + $fakeBuilder->setUniqueFaker($uniqueFaker); + } + return array_map(function () use ($commonAttributes, $fakeBuilder) { + $model = $fakeBuilder->generateModel($commonAttributes); + return $model; + }, range(0, $number -1)); + } + + /** + * Generate, save and return multiple models + * @param int $number + * @param array|callable $commonAttributes + * @return \yii\db\ActiveRecord[]|array + * @example TaskFaker::save(5, ['project_id'=>1, 'user_id' => 2]); + * @example TaskFaker::save(5, function($model, $faker, $uniqueFaker) { + * $model->setAttributes(['name' => $uniqueFaker->username, 'state'=>$faker->boolean(20)]); + * return $model; + * }); + */ + public static function save(int $number, $commonAttributes = [], ?UniqueGenerator $uniqueFaker = null):array + { + if ($number < 1) { + return []; + } + $fakeBuilder = new static(); + if ($uniqueFaker !== null) { + $fakeBuilder->setUniqueFaker($uniqueFaker); + } + return array_map(function () use ($commonAttributes, $fakeBuilder) { + $model = $fakeBuilder->generateModel($commonAttributes); + $model->save(); + return $model; + }, range(0, $number -1)); + } +} diff --git a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/Fruit.php b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/Fruit.php new file mode 100644 index 00000000..c74c53d9 --- /dev/null +++ b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/Fruit.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 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; + } +} diff --git a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/Pet.php b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/Pet.php new file mode 100644 index 00000000..1b9df07c --- /dev/null +++ b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/Pet.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 Pet(); + //$model->id = $uniqueFaker->numberBetween(0, 1000000); + $model->name = $faker->optional(0.92, 'cat')->sentence; + $model->age = $faker->optional(0.92, 2)->numberBetween(0, 1000000); + $model->tags = array_map(function () use ($faker, $uniqueFaker) { + return $faker->sentence; + }, range(1, 4)); + $model->tags_arbit = $faker->optional(0.92, [ + 'long-tail', + 'short-tail', + 'black', + 'white', +])->words(); + $model->number_arr = array_map(function () use ($faker, $uniqueFaker) { + return $faker->randomFloat(); + }, range(1, 4)); + $model->number_arr_min_uniq = array_map(function () use ($faker, $uniqueFaker) { + return $uniqueFaker->randomFloat(); + }, range(1, 6)); + $model->int_arr = array_map(function () use ($faker, $uniqueFaker) { + return $faker->numberBetween(0, 1000000); + }, range(1, 4)); + $model->int_arr_min_uniq = array_map(function () use ($faker, $uniqueFaker) { + return $uniqueFaker->numberBetween(0, 1000000); + }, range(1, 7)); + $model->bool_arr = array_map(function () use ($faker, $uniqueFaker) { + return $faker->boolean; + }, range(1, 4)); + $model->arr_arr_int = array_map(function () use ($faker, $uniqueFaker) { + return array_map(function () use ($faker, $uniqueFaker) { + return $faker->numberBetween(0, 1000000); + }, range(1, 4)); + }, range(1, 4)); + $model->arr_arr_str = array_map(function () use ($faker, $uniqueFaker) { + return array_map(function () use ($faker, $uniqueFaker) { + return $faker->sentence; + }, range(1, 4)); + }, range(1, 4)); + $model->arr_arr_arr_str = array_map(function () use ($faker, $uniqueFaker) { + return array_map(function () use ($faker, $uniqueFaker) { + return array_map(function () use ($faker, $uniqueFaker) { + return $faker->sentence; + }, range(1, 5)); + }, range(1, 4)); + }, 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, +], +], +]; + }, 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)}; + }, 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)}; + }, 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)}; + }, range(1, 4)); + if (!is_callable($attributes)) { + $model->setAttributes($attributes, false); + } else { + $model = $attributes($model, $faker, $uniqueFaker); + } + return $model; + } +} diff --git a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/User.php b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/User.php new file mode 100644 index 00000000..9b837d6e --- /dev/null +++ b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/User.php @@ -0,0 +1,10 @@ +generateModels(['author_id' => 1]); + * $model = (new PostFaker())->generateModels(function($model, $faker, $uniqueFaker) { + * $model->scenario = 'create'; + * $model->author_id = 1; + * return $model; + * }); + **/ + public function generateModel($attributes = []) + { + $faker = $this->faker; + $uniqueFaker = $this->uniqueFaker; + $model = new User(); + //$model->id = $uniqueFaker->numberBetween(0, 1000000); + $model->name = $faker->sentence; + if (!is_callable($attributes)) { + $model->setAttributes($attributes, false); + } else { + $model = $attributes($model, $faker, $uniqueFaker); + } + return $model; + } +} diff --git a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/base/Fruit.php b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/base/Fruit.php new file mode 100644 index 00000000..2106e69d --- /dev/null +++ b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/base/Fruit.php @@ -0,0 +1,26 @@ + [['name'], 'trim'], + 'name_string' => [['name'], 'string'], + ]; + } +} diff --git a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/base/Pet.php b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/base/Pet.php new file mode 100644 index 00000000..73f57ea7 --- /dev/null +++ b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/base/Pet.php @@ -0,0 +1,57 @@ + [['name'], 'trim'], + 'required' => [['name'], 'required'], + 'name_string' => [['name'], 'string'], + 'age_integer' => [['age'], 'integer'], + 'safe' => [['tags', 'tags_arbit', 'number_arr', 'number_arr_min_uniq', 'int_arr', 'int_arr_min_uniq', 'bool_arr', 'arr_arr_int', 'arr_arr_str', 'arr_arr_arr_str', 'arr_of_obj', 'user_ref_obj_arr', 'one_of_arr', 'one_of_arr_complex', 'one_of_from_multi_ref_arr'], 'safe'], + ]; + } + + public function getUserRefObjArrNormal() + { + return $this->hasMany(\app\models\User::class, ['pet_id' => 'id']); + } + + public function getUserRefObjArr() + { + return $this->hasMany(\app\models\User::class, ['pet_id' => 'id']); + } +} diff --git a/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/base/User.php b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/base/User.php new file mode 100644 index 00000000..08e59880 --- /dev/null +++ b/tests/specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql/models/base/User.php @@ -0,0 +1,26 @@ + [['name'], 'trim'], + 'name_string' => [['name'], 'string'], + ]; + } +} diff --git a/tests/specs/menu/models/MenuFaker.php b/tests/specs/menu/models/MenuFaker.php index 8af0e400..6e5bda4d 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 = []; - $model->kwargs = []; + $model->args = $faker->words(); + $model->kwargs = $faker->words(); if (!is_callable($attributes)) { $model->setAttributes($attributes, false); } else { diff --git a/tests/specs/postgres_custom/models/CustomFaker.php b/tests/specs/postgres_custom/models/CustomFaker.php index 3732716d..cc1b2cd0 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 = []; - $model->json2 = []; - $model->json3 = []; - $model->json4 = []; + $model->json1 = $faker->words(); + $model->json2 = $faker->words(); + $model->json3 = $faker->words(); + $model->json4 = $faker->words(); $model->status = $faker->randomElement(['active','draft']); $model->status_x = $faker->randomElement(['active','draft']); if (!is_callable($attributes)) { 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 32934ebe..8a4a3594 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 = []; - $model->json_col_def = []; - $model->json_col_def_2 = []; + $model->json_col = $faker->words(); + $model->json_col_def = $faker->words(); + $model->json_col_def_2 = $faker->words(); $model->text_def = $faker->sentence; - $model->json_def = []; + $model->json_def = $faker->words(); 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 efe765e5..1f183cd5 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 @@ -38,10 +38,10 @@ public function generateModel($attributes = []) $model->dec_col = $faker->randomFloat(); $model->str_col_def = substr($faker->word(3), 0, 3); $model->json_col = $faker->sentence; - $model->json_col_2 = ["a" => "b"]; + $model->json_col_2 = $faker->words(); $model->numeric_col = $faker->randomFloat(); - $model->json_col_def_n = []; - $model->json_col_def_n_2 = []; + $model->json_col_def_n = $faker->words(); + $model->json_col_def_n_2 = $faker->words(); 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 a60800cd..0532b9c1 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 = []; + $model->json_col = $faker->words(); $model->varchar_col = substr($faker->text(5), 0, 5); $model->numeric_col = $faker->randomFloat(); - $model->json_col_def_n = []; + $model->json_col_def_n = $faker->words(); 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 2fdf01e5..293dc2df 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 = []; + $model->col_8 = $faker->words(); $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 9c3e308b..795f4344 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 = []; - $model->json_col_def = []; - $model->json_col_def_2 = []; + $model->json_col = $faker->words(); + $model->json_col_def = $faker->words(); + $model->json_col_def_2 = $faker->words(); $model->text_def = $faker->sentence; - $model->json_def = []; + $model->json_def = $faker->words(); 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 2f60a4e7..6a14b7de 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 @@ -37,10 +37,10 @@ public function generateModel($attributes = []) $model->dec_col = $faker->randomFloat(); $model->str_col_def = substr($faker->word(3), 0, 3); $model->json_col = $faker->sentence; - $model->json_col_2 = ["a" => "b"]; + $model->json_col_2 = $faker->words(); $model->numeric_col = $faker->randomFloat(); - $model->json_col_def_n = []; - $model->json_col_def_n_2 = []; + $model->json_col_def_n = $faker->words(); + $model->json_col_def_n_2 = $faker->words(); 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 87763eb3..fe3319dd 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 = []; + $model->json_col = $faker->words(); $model->varchar_col = substr($faker->text(5), 0, 5); $model->numeric_col = $faker->randomFloat(); - $model->json_col_def_n = []; + $model->json_col_def_n = $faker->words(); 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 41b186d9..ecd7b315 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 = []; + $model->col_8 = $faker->words(); $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 e2af0af0..70c2016c 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 = []; + $model->text_col_array = $faker->words(); $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 = []; - $model->jsonb_col = []; - $model->json_col_def = []; - $model->json_col_def_2 = []; + $model->json_col = $faker->words(); + $model->jsonb_col = $faker->words(); + $model->json_col_def = $faker->words(); + $model->json_col_def_2 = $faker->words(); $model->text_def = $faker->sentence; - $model->json_def = []; - $model->jsonb_def = []; + $model->json_def = $faker->words(); + $model->jsonb_def = $faker->words(); $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 af6d1d77..e32c6d82 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 @@ -38,11 +38,11 @@ public function generateModel($attributes = []) $model->dec_col = $faker->randomFloat(); $model->str_col_def = $faker->sentence; $model->json_col = $faker->sentence; - $model->json_col_2 = ["a" => "b"]; + $model->json_col_2 = $faker->words(); $model->numeric_col = $faker->randomFloat(); - $model->json_col_def_n = []; - $model->json_col_def_n_2 = []; - $model->text_col_array = []; + $model->json_col_def_n = $faker->words(); + $model->json_col_def_n_2 = $faker->words(); + $model->text_col_array = $faker->words(); 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 1b41dae7..64c32ef1 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 = []; + $model->json_col = $faker->words(); $model->varchar_col = $faker->sentence; $model->numeric_col = $faker->randomFloat(); - $model->json_col_def_n = []; - $model->json_col_def_n_2 = []; - $model->text_col_array = []; + $model->json_col_def_n = $faker->words(); + $model->json_col_def_n_2 = $faker->words(); + $model->text_col_array = $faker->words(); 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 aac33ab7..ab484826 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 = []; + $model->col_8 = $faker->words(); $model->col_9 = $faker->sentence; $model->col_10 = $faker->sentence; $model->col_11 = $faker->sentence; diff --git a/tests/unit/IssueFixTest.php b/tests/unit/IssueFixTest.php index f8f2f528..66d794b8 100644 --- a/tests/unit/IssueFixTest.php +++ b/tests/unit/IssueFixTest.php @@ -445,4 +445,18 @@ public function test84HowToGenerateControllerCodeWithDistinctMethodNamesInCaseOf ]); $this->checkFiles($actualFiles, $expectedFiles); } + + // https://github.com/php-openapi/yii2-openapi/issues/20 + public function test20ConsiderOpenApiSpecExamplesInFakeCodeGeneration() + { + $testFile = Yii::getAlias("@specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/index.php"); + $this->runGenerator($testFile); + $actualFiles = FileHelper::findFiles(Yii::getAlias('@app'), [ + 'recursive' => true, + ]); + $expectedFiles = FileHelper::findFiles(Yii::getAlias("@specs/issue_fix/20_consider_openapi_spec_examples_in_faker_code_generation/mysql"), [ + 'recursive' => true, + ]); + $this->checkFiles($actualFiles, $expectedFiles); + } }