From d593a63d614a67780edd1fce405d1b69fd462081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigmar=20K=C3=BChlmann?= Date: Wed, 21 Aug 2024 12:57:13 +0200 Subject: [PATCH 01/13] DbModel with openapiSchema --- src/lib/AttributeResolver.php | 32 +++++++++++++++++--------------- src/lib/items/DbModel.php | 5 +++++ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/lib/AttributeResolver.php b/src/lib/AttributeResolver.php index 0063d8df..906226d4 100644 --- a/src/lib/AttributeResolver.php +++ b/src/lib/AttributeResolver.php @@ -60,7 +60,7 @@ class AttributeResolver /** * @var ComponentSchema */ - private $schema; + private $componentSchema; /** * @var \cebe\yii2openapi\lib\items\JunctionSchemas @@ -79,7 +79,7 @@ class AttributeResolver public function __construct(string $schemaName, ComponentSchema $schema, JunctionSchemas $junctions, ?Config $config = null) { $this->schemaName = $schemaName; - $this->schema = $schema; + $this->componentSchema = $schema; $this->tableName = $schema->resolveTableName($schemaName); $this->junctions = $junctions; $this->isJunctionSchema = $junctions->isJunctionSchema($schemaName); @@ -94,10 +94,10 @@ public function __construct(string $schemaName, ComponentSchema $schema, Junctio */ public function resolve():DbModel { - foreach ($this->schema->getProperties() as $property) { + foreach ($this->componentSchema->getProperties() as $property) { /** @var $property \cebe\yii2openapi\lib\openapi\PropertySchema */ - $isRequired = $this->schema->isRequiredProperty($property->getName()); + $isRequired = $this->componentSchema->isRequiredProperty($property->getName()); $nullableValue = $property->getProperty()->getSerializableData()->nullable ?? null; if ($nullableValue === false) { // see docs in README regarding NOT NULL, required and nullable $isRequired = true; @@ -113,18 +113,20 @@ public function resolve():DbModel } return Yii::createObject(DbModel::class, [ [ - 'pkName' => $this->schema->getPkName(), + /** @see \cebe\openapi\spec\Schema */ + 'openapiSchema' => $this->componentSchema->getSchema(), + 'pkName' => $this->componentSchema->getPkName(), 'name' => $this->schemaName, 'tableName' => $this->tableName, - 'description' => $this->schema->getDescription(), + 'description' => $this->componentSchema->getDescription(), 'attributes' => $this->attributes, 'relations' => $this->relations, 'nonDbRelations' => $this->nonDbRelations, 'many2many' => $this->many2many, - 'indexes' => $this->prepareIndexes($this->schema->getIndexes()), + 'indexes' => $this->prepareIndexes($this->componentSchema->getIndexes()), //For valid primary keys for junction tables 'junctionCols' => $this->isJunctionSchema ? $this->junctions->junctionCols($this->schemaName) : [], - 'isNotDb' => $this->schema->isNonDb(), + 'isNotDb' => $this->componentSchema->isNonDb(), ], ]); } @@ -185,7 +187,7 @@ protected function resolveHasMany2ManyTableProperty(PropertySchema $property, bo 'relatedSchemaName' => $junkAttribute['relatedClassName'], 'tableName' => $this->tableName, 'relatedTableName' => $junkAttribute['relatedTableName'], - 'pkAttribute' => $this->attributes[$this->schema->getPkName()], + 'pkAttribute' => $this->attributes[$this->componentSchema->getPkName()], 'hasViaModel' => true, 'viaModelName' => $viaModel, 'viaRelationName' => Inflector::id2camel($junkRef, '_'), @@ -197,7 +199,7 @@ protected function resolveHasMany2ManyTableProperty(PropertySchema $property, bo $this->relations[Inflector::pluralize($junkRef)] = Yii::createObject(AttributeRelation::class, [$junkRef, $junkAttribute['junctionTable'], $viaModel]) - ->asHasMany([$junkAttribute['pairProperty'] . '_id' => $this->schema->getPkName()]); + ->asHasMany([$junkAttribute['pairProperty'] . '_id' => $this->componentSchema->getPkName()]); return; } @@ -328,7 +330,7 @@ protected function resolveProperty( AttributeRelation::class, [$property->getName(), $relatedTableName, $relatedClassName] ) - ->asHasMany([$foreignPk => $this->schema->getPkName()]); + ->asHasMany([$foreignPk => $this->componentSchema->getPkName()]); return; } $relatedClassName = $property->getRefClassName(); @@ -347,10 +349,10 @@ protected function resolveProperty( AttributeRelation::class, [$property->getName(), $relatedTableName, $relatedClassName] ) - ->asHasMany([Inflector::camel2id($this->schemaName, '_') . '_id' => $this->schema->getPkName()]); + ->asHasMany([Inflector::camel2id($this->schemaName, '_') . '_id' => $this->componentSchema->getPkName()]); return; } - if ($this->schema->isNonDb() && $attribute->isReference()) { + if ($this->componentSchema->isNonDb() && $attribute->isReference()) { $this->attributes[$property->getName()] = $attribute; return; } @@ -398,7 +400,7 @@ protected function catchManyToMany( 'relatedSchemaName' => $relatedSchemaName, 'tableName' => $this->tableName, 'relatedTableName' => $relatedTableName, - 'pkAttribute' => $this->attributes[$this->schema->getPkName()], + 'pkAttribute' => $this->attributes[$this->componentSchema->getPkName()], ], ]); $this->many2many[$propertyName] = $relation; @@ -480,7 +482,7 @@ protected function resolvePropertyRef(PropertySchema $property, Attribute $attri $fkProperty = new PropertySchema( $property->getRefSchema()->getSchema(), $property->getName(), - $this->schema + $this->componentSchema ); [$min, $max] = $fkProperty->guessMinMax(); $attribute->setPhpType($fkProperty->guessPhpType()) diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 4f174370..0ca396b5 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -27,6 +27,11 @@ */ class DbModel extends BaseObject { + /** + * @var \cebe\openapi\spec\Schema + */ + public $openapiSchema; + /** * @var string primary key attribute name */ From 444bfae2b7b390d77a3a50d02511913552e7e0da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigmar=20K=C3=BChlmann?= Date: Thu, 22 Aug 2024 11:19:46 +0200 Subject: [PATCH 02/13] x-scenarios: name, description. --- src/generator/default/dbmodel.php | 10 ++++++ src/lib/items/DbModel.php | 56 +++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/generator/default/dbmodel.php b/src/generator/default/dbmodel.php index d9c60ebc..02d95ab6 100644 --- a/src/generator/default/dbmodel.php +++ b/src/generator/default/dbmodel.php @@ -45,6 +45,16 @@ */ abstract class getClassName() ?> extends \yii\db\ActiveRecord { +getScenarios()): +foreach($scenarios as $scenario): ?> + /** + * + + */ + public const = ''; + + + virtualAttributes())):?> protected $virtualAttributes = ['columnName; diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 0ca396b5..6bd23f21 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -81,6 +81,11 @@ class DbModel extends BaseObject public $isNotDb = false; + /** + * @var array Automatically generated scenarios from the model 'x-scenarios'. + */ + private array $scenarios; + public function getTableAlias():string { return '{{%' . $this->tableName . '}}'; @@ -178,4 +183,55 @@ public function dbAttributes():array return !$attribute->isVirtual; }); } + + /** + * @return array + */ + public function getScenarios(): array + { + if (isset($this->scenarios)) { + return $this->scenarios; + } + $this->scenarios = $this->getScenariosByOpenapiSchema(); + return $this->scenarios; + } + + /** + * @return array + */ + private function getScenariosByOpenapiSchema(): array + { + $x_scenarios = $this->openapiSchema->{'x-scenarios'} ?? []; + if (empty($x_scenarios) || !is_array($x_scenarios)) { + return []; + } + + $uniqueNames = []; + $scenarios = array_filter($x_scenarios, function ($scenario) use (&$uniqueNames) { + $name = $scenario['name'] ?? ''; + + // Check if the name is empty, already used, or does not meet the criteria + if ( + empty($name) || + in_array($name, $uniqueNames) || + !preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $name) + ) { + return false; // Exclude this item + } + + // Add the name to the uniqueNames array and keep the item + $uniqueNames[] = $name; + return true; + }); + + foreach ($scenarios as $key => $scenario) { + $scenarios[$key]['const'] = 'SCENARIO_' . strtoupper($scenario['name']); + if (empty($scenario['description'])) { + $scenarios[$key]['description'] = 'Scenario ' . $scenario['name']; + } + } + + return $scenarios; + } + } From f96257d33e29d06e2def9bdd67fe9433495e1b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigmar=20K=C3=BChlmann?= Date: Thu, 22 Aug 2024 16:34:18 +0200 Subject: [PATCH 03/13] ModelClassDescription --- src/generator/default/dbmodel.php | 8 ++++++-- src/lib/items/DbModel.php | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/generator/default/dbmodel.php b/src/generator/default/dbmodel.php index 02d95ab6..8fff1db1 100644 --- a/src/generator/default/dbmodel.php +++ b/src/generator/default/dbmodel.php @@ -11,10 +11,14 @@ +/** + * This file is generated by Gii, do not change manually! + */ + namespace ; /** - *description) ? '' : str_replace("\n", "\n * ", ' ' . trim($model->description)) ?> + *getModelClassDescription() ?> * dbAttributes() as $attribute): ?> @@ -48,7 +52,7 @@ abstract class getClassName() ?> extends \yii\db\ActiveRecord getScenarios()): foreach($scenarios as $scenario): ?> /** - * + * */ public const = ''; diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 6bd23f21..2d9c3f10 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -234,4 +234,25 @@ private function getScenariosByOpenapiSchema(): array return $scenarios; } + public function getModelClassDescription_() + { + return !empty($this->description) ? + str_replace("\n", "\n *", ' ' . trim($this->description)) + : ' This is the model class for table "'.$this->tableName.'".'; + } + + /** @noinspection PhpParamsInspection */ + public function getModelClassDescription() + { + if (empty($this->description)) { + return ' This is the model class for table "'.$this->tableName.'".'; + } + + $descriptionArr = explode("\n", $this->description); + $descriptionArr = array_map('trim', $descriptionArr); + $descriptionArr = array_map(function($item) { + return $item !== '' ? ' ' . $item : $item; + }, $descriptionArr); + return implode("\n *", $descriptionArr); + } } From b884d80279872f30f24e975f105d27a1cbb3bf73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigmar=20K=C3=BChlmann?= Date: Fri, 23 Aug 2024 06:06:17 +0200 Subject: [PATCH 04/13] getPropertyAnnotation with FormattedDescription --- src/generator/default/dbmodel.php | 2 +- src/lib/helpers/FormatHelper.php | 19 +++++++++++++++++++ src/lib/items/Attribute.php | 14 ++++++++++---- src/lib/items/DbModel.php | 22 ++++++---------------- 4 files changed, 36 insertions(+), 21 deletions(-) create mode 100644 src/lib/helpers/FormatHelper.php diff --git a/src/generator/default/dbmodel.php b/src/generator/default/dbmodel.php index 8fff1db1..665b9e3d 100644 --- a/src/generator/default/dbmodel.php +++ b/src/generator/default/dbmodel.php @@ -22,7 +22,7 @@ * dbAttributes() as $attribute): ?> - * @property getFormattedDescription() ?> + * @property getPropertyAnnotation() ?> * diff --git a/src/lib/helpers/FormatHelper.php b/src/lib/helpers/FormatHelper.php new file mode 100644 index 00000000..20c7b0f4 --- /dev/null +++ b/src/lib/helpers/FormatHelper.php @@ -0,0 +1,19 @@ +limits['minLength']; } - public function getFormattedDescription():string + /** + * @return string + */ + public function getPropertyAnnotation(): string { - $comment = $this->columnName.' '.$this->description; - $type = $this->phpType; - return $type.' $'.str_replace("\n", "\n * ", rtrim($comment)); + $annotation = $this->phpType . ' $' . $this->columnName; + if (!empty($this->description)) { + $annotation .= FormatHelper::getFormattedDescription($this->description); + } + return $annotation; } public function toColumnSchema():ColumnSchema diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 2d9c3f10..87528b07 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -7,6 +7,7 @@ namespace cebe\yii2openapi\lib\items; +use cebe\yii2openapi\lib\helpers\FormatHelper; use cebe\yii2openapi\lib\ValidationRulesBuilder; use Yii; use yii\base\BaseObject; @@ -234,25 +235,14 @@ private function getScenariosByOpenapiSchema(): array return $scenarios; } - public function getModelClassDescription_() - { - return !empty($this->description) ? - str_replace("\n", "\n *", ' ' . trim($this->description)) - : ' This is the model class for table "'.$this->tableName.'".'; - } - - /** @noinspection PhpParamsInspection */ - public function getModelClassDescription() + /** + * @return string + */ + public function getModelClassDescription(): string { if (empty($this->description)) { return ' This is the model class for table "'.$this->tableName.'".'; } - - $descriptionArr = explode("\n", $this->description); - $descriptionArr = array_map('trim', $descriptionArr); - $descriptionArr = array_map(function($item) { - return $item !== '' ? ' ' . $item : $item; - }, $descriptionArr); - return implode("\n *", $descriptionArr); + return FormatHelper::getFormattedDescription($this->description); } } From 5c7fa9ba40634c6e4c0cd3e585edc4fa0e8a5102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigmar=20K=C3=BChlmann?= Date: Sun, 25 Aug 2024 07:30:25 +0200 Subject: [PATCH 05/13] add FormattedDescription to x-scenarios --- src/generator/default/dbmodel.php | 2 +- src/lib/helpers/FormatHelper.php | 11 ++++++----- src/lib/items/DbModel.php | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/generator/default/dbmodel.php b/src/generator/default/dbmodel.php index 665b9e3d..32250030 100644 --- a/src/generator/default/dbmodel.php +++ b/src/generator/default/dbmodel.php @@ -52,7 +52,7 @@ abstract class getClassName() ?> extends \yii\db\ActiveRecord getScenarios()): foreach($scenarios as $scenario): ?> /** - * + * */ public const = ''; diff --git a/src/lib/helpers/FormatHelper.php b/src/lib/helpers/FormatHelper.php index 20c7b0f4..6fc29742 100644 --- a/src/lib/helpers/FormatHelper.php +++ b/src/lib/helpers/FormatHelper.php @@ -5,15 +5,16 @@ class FormatHelper { /** - * @param $description + * @param string $description + * @param int $spacing * @return string */ - public static function getFormattedDescription($description): string + public static function getFormattedDescription(string $description, int $spacing = 1): string { $descriptionArr = explode("\n", trim($description)); - $descriptionArr = array_map(function($item) { - return $item !== '' ? ' ' . $item : $item; + $descriptionArr = array_map(function ($item) { + return $item === '' ? '' : ' ' . $item; }, $descriptionArr); - return implode("\n *", $descriptionArr); + return implode("\n".str_repeat(" ", $spacing)."*", $descriptionArr); } } \ No newline at end of file diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 87528b07..513e8669 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -227,9 +227,9 @@ private function getScenariosByOpenapiSchema(): array foreach ($scenarios as $key => $scenario) { $scenarios[$key]['const'] = 'SCENARIO_' . strtoupper($scenario['name']); - if (empty($scenario['description'])) { - $scenarios[$key]['description'] = 'Scenario ' . $scenario['name']; - } + $scenarios[$key]['description'] = !empty($scenario['description']) ? + FormatHelper::getFormattedDescription($scenario['description'], 5) + : ' Scenario ' . $scenario['name']; } return $scenarios; From 775437fe3a37c760bd9f200cddceab240a7eead0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigmar=20K=C3=BChlmann?= Date: Sun, 25 Aug 2024 11:10:17 +0200 Subject: [PATCH 06/13] custom scenarioDefaultDescription --- src/generator/ApiGenerator.php | 10 ++++++++++ src/lib/Config.php | 9 +++++++++ src/lib/generators/ModelsGenerator.php | 3 +++ src/lib/items/DbModel.php | 16 +++++++++++++--- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/generator/ApiGenerator.php b/src/generator/ApiGenerator.php index f930f949..cb9db6dc 100644 --- a/src/generator/ApiGenerator.php +++ b/src/generator/ApiGenerator.php @@ -7,6 +7,7 @@ namespace cebe\yii2openapi\generator; +use cebe\yii2openapi\lib\items\DbModel; use yii\db\mysql\Schema as MySqlSchema; use SamIT\Yii2\MariaDb\Schema as MariaDbSchema; use yii\db\pgsql\Schema as PgSqlSchema; @@ -133,6 +134,15 @@ class ApiGenerator extends Generator */ public $excludeModels = []; + /** + * @var array Map for custom dbModels + * @example + * 'dbModel' => [ + * 'scenarioDefaultDescription' => " Scenario {name}", @see DbModel::$scenarioDefaultDescription + * ] + */ + public $dbModel = []; + /** * @var array Map for custom controller names not based on model name for exclusive cases * @example diff --git a/src/lib/Config.php b/src/lib/Config.php index 60d15910..09cf3181 100644 --- a/src/lib/Config.php +++ b/src/lib/Config.php @@ -109,6 +109,15 @@ class Config extends BaseObject */ public $excludeModels = []; + /** + * @var array Map for custom dbModels + * @example + * 'dbModel' => [ + * 'scenarioDefaultDescription' => " Scenario {name}", @see DbModel::$scenarioDefaultDescription + * ] + */ + public $dbModel = []; + /** * @var array Map for custom controller names not based on model name for exclusive cases * @example diff --git a/src/lib/generators/ModelsGenerator.php b/src/lib/generators/ModelsGenerator.php index 5f196525..27dc6aea 100644 --- a/src/lib/generators/ModelsGenerator.php +++ b/src/lib/generators/ModelsGenerator.php @@ -56,6 +56,9 @@ public function generate():CodeFiles } foreach ($this->models as $model) { $className = $model->getClassName(); + if (!empty($this->config->dbModel['scenarioDefaultDescription'])) { + $model->scenarioDefaultDescription = $this->config->dbModel['scenarioDefaultDescription']; + } if ($model->isNotDb === false) { $this->files->add(new CodeFile( Yii::getAlias("$modelPath/base/$className.php"), diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 513e8669..b5f5dd69 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -87,6 +87,13 @@ class DbModel extends BaseObject */ private array $scenarios; + /** + * @var string + * Here, you can set your own default description for the scenario. + * You can use the {name} attribute from the schema for the YAML model. + */ + public string $scenarioDefaultDescription = " Scenario {name}"; + public function getTableAlias():string { return '{{%' . $this->tableName . '}}'; @@ -227,9 +234,12 @@ private function getScenariosByOpenapiSchema(): array foreach ($scenarios as $key => $scenario) { $scenarios[$key]['const'] = 'SCENARIO_' . strtoupper($scenario['name']); - $scenarios[$key]['description'] = !empty($scenario['description']) ? - FormatHelper::getFormattedDescription($scenario['description'], 5) - : ' Scenario ' . $scenario['name']; + $scenarios[$key]['description'] = FormatHelper::getFormattedDescription( + !empty($scenario['description']) ? + $scenario['description'] + : str_replace('{name}', $scenario['name'], $this->scenarioDefaultDescription + ), + 5); } return $scenarios; From 19d5c361a2fffa7d31c2c79453ca7ee8203aed80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigmar=20K=C3=BChlmann?= Date: Sun, 25 Aug 2024 11:49:54 +0200 Subject: [PATCH 07/13] generate scenario const -fix --- src/lib/items/DbModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index b5f5dd69..389a92b1 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -233,7 +233,7 @@ private function getScenariosByOpenapiSchema(): array }); foreach ($scenarios as $key => $scenario) { - $scenarios[$key]['const'] = 'SCENARIO_' . strtoupper($scenario['name']); + $scenarios[$key]['const'] = 'SCENARIO_' . strtoupper(implode('_', preg_split('/(?=[A-Z])/', $scenario['name']))); $scenarios[$key]['description'] = FormatHelper::getFormattedDescription( !empty($scenario['description']) ? $scenario['description'] From 69dfc10b0d9a867203c2d48b203491aa5a0c4c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigmar=20K=C3=BChlmann?= Date: Sun, 25 Aug 2024 12:58:23 +0200 Subject: [PATCH 08/13] scenarioDefaultDescription: scenarioName, scenarioConst, modelName --- src/generator/ApiGenerator.php | 3 ++- src/lib/Config.php | 3 ++- src/lib/items/DbModel.php | 15 +++++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/generator/ApiGenerator.php b/src/generator/ApiGenerator.php index cb9db6dc..1852260c 100644 --- a/src/generator/ApiGenerator.php +++ b/src/generator/ApiGenerator.php @@ -138,7 +138,8 @@ class ApiGenerator extends Generator * @var array Map for custom dbModels * @example * 'dbModel' => [ - * 'scenarioDefaultDescription' => " Scenario {name}", @see DbModel::$scenarioDefaultDescription + * AcceptedInputs: {scenarioName}, {scenarioConst}, {modelName}. + * 'scenarioDefaultDescription' => " Scenario {scenarioName}", @see DbModel::$scenarioDefaultDescription * ] */ public $dbModel = []; diff --git a/src/lib/Config.php b/src/lib/Config.php index 09cf3181..e8732c27 100644 --- a/src/lib/Config.php +++ b/src/lib/Config.php @@ -113,7 +113,8 @@ class Config extends BaseObject * @var array Map for custom dbModels * @example * 'dbModel' => [ - * 'scenarioDefaultDescription' => " Scenario {name}", @see DbModel::$scenarioDefaultDescription + * AcceptedInputs: {scenarioName}, {scenarioConst}, {modelName}. + * 'scenarioDefaultDescription' => " Scenario {scenarioName}", @see DbModel::$scenarioDefaultDescription * ] */ public $dbModel = []; diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 389a92b1..91377bad 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -90,9 +90,9 @@ class DbModel extends BaseObject /** * @var string * Here, you can set your own default description for the scenario. - * You can use the {name} attribute from the schema for the YAML model. + * AcceptedInputs: {scenarioName}, {scenarioConst}, {modelName}. */ - public string $scenarioDefaultDescription = " Scenario {name}"; + public string $scenarioDefaultDescription = " Scenario {scenarioName}"; public function getTableAlias():string { @@ -237,8 +237,15 @@ private function getScenariosByOpenapiSchema(): array $scenarios[$key]['description'] = FormatHelper::getFormattedDescription( !empty($scenario['description']) ? $scenario['description'] - : str_replace('{name}', $scenario['name'], $this->scenarioDefaultDescription - ), + : str_replace([ + '{scenarioName}', + '{scenarioConst}', + '{modelName}', + ], [ + $scenario['name'], + $scenarios[$key]['const'], + $this->name, + ], $this->scenarioDefaultDescription), 5); } From d5d1be7a0bbd905d28eba7208d281f1d74f3d5b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigmar=20K=C3=BChlmann?= Date: Mon, 26 Aug 2024 08:48:33 +0200 Subject: [PATCH 09/13] single description: scenarioName, scenarioConst, modelName --- src/lib/items/DbModel.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 91377bad..387107cd 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -92,7 +92,7 @@ class DbModel extends BaseObject * Here, you can set your own default description for the scenario. * AcceptedInputs: {scenarioName}, {scenarioConst}, {modelName}. */ - public string $scenarioDefaultDescription = " Scenario {scenarioName}"; + public string $scenarioDefaultDescription = "Scenario {scenarioName}"; public function getTableAlias():string { @@ -234,10 +234,10 @@ private function getScenariosByOpenapiSchema(): array foreach ($scenarios as $key => $scenario) { $scenarios[$key]['const'] = 'SCENARIO_' . strtoupper(implode('_', preg_split('/(?=[A-Z])/', $scenario['name']))); + $description = !empty($scenario['description']) ? + $scenario['description'] : $this->scenarioDefaultDescription; $scenarios[$key]['description'] = FormatHelper::getFormattedDescription( - !empty($scenario['description']) ? - $scenario['description'] - : str_replace([ + str_replace([ '{scenarioName}', '{scenarioConst}', '{modelName}', @@ -245,8 +245,9 @@ private function getScenariosByOpenapiSchema(): array $scenario['name'], $scenarios[$key]['const'], $this->name, - ], $this->scenarioDefaultDescription), - 5); + ], $description), + 5 + ); } return $scenarios; From a1ac7e3e8a1b23f544ab1224b7d7b535852d1ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigmar=20K=C3=BChlmann?= Date: Tue, 27 Aug 2024 09:48:34 +0200 Subject: [PATCH 10/13] add function scenarios --- src/generator/default/dbmodel.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/generator/default/dbmodel.php b/src/generator/default/dbmodel.php index 32250030..17fa4a84 100644 --- a/src/generator/default/dbmodel.php +++ b/src/generator/default/dbmodel.php @@ -76,6 +76,33 @@ public static function tableName() { return getTableAlias()) ?>; } + + + /** + * Automatically generated scenarios from the model 'x-scenarios'. + * @return array a list of scenarios and the corresponding active attributes. + */ + public function scenarios() + { + /** + * Each scenario is assigned attributes as in the 'default' scenario. + * The advantage is that the scenario can be used immediately. + * This can be overridden in the child model if needed. + */ + $default = parent::scenarios()[self::SCENARIO_DEFAULT]; + + return [ + + self:: => $default, + + /** + * The 'default' scenario and all scenarios mentioned in the rules() using 'on' and 'except' + * are automatically included in the scenarios() function for the model. + */ + ...parent::scenarios(), + ]; + } + virtualAttributes())):?> public function attributes() From e788a21fd2a1ada19e0f9b68e3a858f4a072548f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigmar=20K=C3=BChlmann?= Date: Fri, 13 Sep 2024 09:48:56 +0200 Subject: [PATCH 11/13] cleanUp + getScenarios description --- src/generator/ApiGenerator.php | 5 ++- src/lib/Config.php | 5 ++- src/lib/items/DbModel.php | 74 +++++++++++++++++++++++++++++----- 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/src/generator/ApiGenerator.php b/src/generator/ApiGenerator.php index 1852260c..a7eff510 100644 --- a/src/generator/ApiGenerator.php +++ b/src/generator/ApiGenerator.php @@ -136,10 +136,11 @@ class ApiGenerator extends Generator /** * @var array Map for custom dbModels + * + * @see DbModel::$scenarioDefaultDescription with acceptedInputs: {scenarioName}, {scenarioConst}, {modelName}. * @example * 'dbModel' => [ - * AcceptedInputs: {scenarioName}, {scenarioConst}, {modelName}. - * 'scenarioDefaultDescription' => " Scenario {scenarioName}", @see DbModel::$scenarioDefaultDescription + * 'scenarioDefaultDescription' => "Scenario {scenarioName}", * ] */ public $dbModel = []; diff --git a/src/lib/Config.php b/src/lib/Config.php index e8732c27..bd4934a0 100644 --- a/src/lib/Config.php +++ b/src/lib/Config.php @@ -111,10 +111,11 @@ class Config extends BaseObject /** * @var array Map for custom dbModels + * + * @see DbModel::$scenarioDefaultDescription with acceptedInputs: {scenarioName}, {scenarioConst}, {modelName}. * @example * 'dbModel' => [ - * AcceptedInputs: {scenarioName}, {scenarioConst}, {modelName}. - * 'scenarioDefaultDescription' => " Scenario {scenarioName}", @see DbModel::$scenarioDefaultDescription + * 'scenarioDefaultDescription' => "Scenario {scenarioName}", * ] */ public $dbModel = []; diff --git a/src/lib/items/DbModel.php b/src/lib/items/DbModel.php index 387107cd..5c33c09b 100644 --- a/src/lib/items/DbModel.php +++ b/src/lib/items/DbModel.php @@ -82,17 +82,17 @@ class DbModel extends BaseObject public $isNotDb = false; - /** - * @var array Automatically generated scenarios from the model 'x-scenarios'. - */ - private array $scenarios; - /** * @var string * Here, you can set your own default description for the scenario. * AcceptedInputs: {scenarioName}, {scenarioConst}, {modelName}. */ - public string $scenarioDefaultDescription = "Scenario {scenarioName}"; + public $scenarioDefaultDescription = "Scenario {scenarioName}"; + + /** + * @var array Automatically generated scenarios from the model 'x-scenarios'. + */ + private $_scenarios; public function getTableAlias():string { @@ -193,18 +193,72 @@ public function dbAttributes():array } /** + * Returns a scenarios array based on the 'x-scenarios'. + * Each scenario has the following properties: 'name', 'const', and 'description'. + * + * When the `getScenarios` function is called for the first time on this model, + * the value is stored in `_scenarios` and then returned. + * If the `getScenariosByOpenapiSchema` function is called again on this model, + * the stored value from `_scenarios` is returned. + * * @return array */ public function getScenarios(): array { - if (isset($this->scenarios)) { - return $this->scenarios; + if (isset($this->_scenarios)) { + return $this->_scenarios; } - $this->scenarios = $this->getScenariosByOpenapiSchema(); - return $this->scenarios; + $this->_scenarios = $this->getScenariosByOpenapiSchema(); + return $this->_scenarios; } /** + * Returns a scenarios array based on the 'x-scenarios'. + * Each scenario has the following properties: 'name', 'const', and 'description'. + * + * Example for 'schema.yaml': + * x-scenarios: + * - name: create + * description: My custom description for scenario create + * - name: update + * + * 1) With default @see $scenarioDefaultDescription = "Scenario {scenarioName}" + * + * The resulting array: + * [ + * [ + * 'name' => 'create', + * 'const' => 'SCENARIO_CREATE', + * 'description' => "My custom description for scenario create", + * ], + * [ + * 'name' => 'update', + * 'const' => 'SCENARIO_UPDATE', + * 'description' => "Scenario update", + * ], + * ] + * + * 2) With custom @see $scenarioDefaultDescription = implode("\n", [ + * "This Backend-Scenario \"{scenarioName}\" exist in both the frontend model and the backend model.", + * "@see \common\client\models\{modelName}::{scenarioConst}", + * ]); + * + * For the 'update' scenario, it is an example of a two-line description. + * E.g. your modelName is 'Project'. + * The resulting array: + * [ + * [ + * 'name' => 'create', + * 'const' => 'SCENARIO_CREATE', + * 'description' => "My custom description for scenario create", + * ], + * [ + * 'name' => 'update', + * 'const' => 'SCENARIO_UPDATE', + * 'description' => "This Backend-Scenario \"update\" exist in both the frontend model and the backend model.\n@see \common\client\models\Project::SCENARIO_UPDATE", + * ], + * ] + * * @return array */ private function getScenariosByOpenapiSchema(): array From d7dc5733d8ad18635ad12ea808aebe3de3c002bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigmar=20K=C3=BChlmann?= Date: Fri, 13 Sep 2024 10:17:15 +0200 Subject: [PATCH 12/13] style fix --- src/lib/helpers/FormatHelper.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/helpers/FormatHelper.php b/src/lib/helpers/FormatHelper.php index 6fc29742..12567fe8 100644 --- a/src/lib/helpers/FormatHelper.php +++ b/src/lib/helpers/FormatHelper.php @@ -1,5 +1,10 @@ and contributors + * @license https://github.com/cebe/yii2-openapi/blob/master/LICENSE + */ + namespace cebe\yii2openapi\lib\helpers; class FormatHelper @@ -17,4 +22,4 @@ public static function getFormattedDescription(string $description, int $spacing }, $descriptionArr); return implode("\n".str_repeat(" ", $spacing)."*", $descriptionArr); } -} \ No newline at end of file +} From 9497bf434eef9579ed1e90a06be939cc1669dfe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sigmar=20K=C3=BChlmann?= Date: Fri, 13 Sep 2024 13:22:16 +0200 Subject: [PATCH 13/13] test fix --- tests/specs/blog/models/base/Category.php | 4 ++++ tests/specs/blog/models/base/Comment.php | 6 +++++- tests/specs/blog/models/base/Fakerable.php | 6 +++++- tests/specs/blog/models/base/Post.php | 4 ++++ tests/specs/blog/models/base/User.php | 4 ++++ tests/specs/blog_v2/models/base/Category.php | 4 ++++ tests/specs/blog_v2/models/base/Comment.php | 6 +++++- tests/specs/blog_v2/models/base/Post.php | 4 ++++ tests/specs/blog_v2/models/base/Tag.php | 6 +++++- tests/specs/blog_v2/models/base/User.php | 4 ++++ .../maria/app/models/base/ColumnNameChange.php | 4 ++++ .../mysql/app/models/base/ColumnNameChange.php | 4 ++++ .../pgsql/app/models/base/ColumnNameChange.php | 4 ++++ tests/specs/fk_col_name/app/models/base/Delivery.php | 6 +++++- tests/specs/fk_col_name/app/models/base/User.php | 4 ++++ tests/specs/fk_col_name/app/models/base/Webhook.php | 4 ++++ tests/specs/fk_col_name_index/app/models/base/Delivery.php | 6 +++++- tests/specs/fk_col_name_index/app/models/base/User.php | 4 ++++ tests/specs/fk_col_name_index/app/models/base/Webhook.php | 4 ++++ tests/specs/id_not_in_rules/app/models/base/Fruit.php | 4 ++++ tests/specs/id_not_in_rules/app/models/base/Post.php | 6 +++++- .../app/models/base/Pristine.php | 4 ++++ .../maria/models/base/Mailing.php | 4 ++++ .../maria/models/base/Contact.php | 4 ++++ .../maria/models/base/Mailing.php | 4 ++++ .../app/models/base/Invoice.php | 6 +++++- .../app/models/base/Order.php | 6 +++++- .../pgsql/models/base/Contact.php | 6 +++++- .../pgsql/models/base/Account.php | 4 ++++ .../pgsql/models/base/Contact.php | 4 ++++ .../pgsql/models/base/PaymentMethod.php | 4 ++++ .../app/models/base/Account.php | 4 ++++ .../app/models/base/E123.php | 4 ++++ .../quote_in_alter_table/pgsql/app/models/base/Fruit.php | 4 ++++ tests/specs/many2many/models/base/Photo.php | 6 +++++- tests/specs/many2many/models/base/Photos2Posts.php | 6 +++++- tests/specs/many2many/models/base/Post.php | 4 ++++ tests/specs/many2many/models/base/PostsAttaches.php | 6 +++++- tests/specs/many2many/models/base/PostsGallery.php | 6 +++++- tests/specs/many2many/models/base/Tag.php | 6 +++++- tests/specs/menu/models/base/Menu.php | 6 +++++- tests/specs/petstore/models/base/Pet.php | 4 ++++ tests/specs/petstore/models/base/Store.php | 4 ++++ tests/specs/petstore_arrayref/models/base/Pet.php | 4 ++++ tests/specs/petstore_jsonapi/models/base/Pet.php | 4 ++++ tests/specs/petstore_namespace/mymodels/base/Pet.php | 4 ++++ tests/specs/petstore_namespace/mymodels/base/Store.php | 4 ++++ tests/specs/petstore_wrapped/models/base/Pet.php | 4 ++++ tests/specs/petstore_xtable/models/base/Pet.php | 4 ++++ tests/specs/postgres_custom/models/base/Custom.php | 6 +++++- tests/specs/relations_in_faker/app/models/base/A123.php | 4 ++++ tests/specs/relations_in_faker/app/models/base/Account.php | 4 ++++ tests/specs/relations_in_faker/app/models/base/B123.php | 4 ++++ tests/specs/relations_in_faker/app/models/base/C123.php | 4 ++++ tests/specs/relations_in_faker/app/models/base/D123.php | 4 ++++ tests/specs/relations_in_faker/app/models/base/Domain.php | 4 ++++ tests/specs/relations_in_faker/app/models/base/E123.php | 4 ++++ tests/specs/relations_in_faker/app/models/base/Routing.php | 4 ++++ .../maria/app/models/mariamodel/base/Alldbdatatype.php | 4 ++++ .../maria/app/models/mariamodel/base/Editcolumn.php | 4 ++++ .../maria/app/models/mariamodel/base/Newcolumn.php | 4 ++++ .../maria/app/models/mariamodel/base/Pristine.php | 4 ++++ .../rules_and_more/mysql/app/models/base/Alldbdatatype.php | 4 ++++ .../rules_and_more/mysql/app/models/base/Editcolumn.php | 4 ++++ .../rules_and_more/mysql/app/models/base/Newcolumn.php | 4 ++++ .../rules_and_more/mysql/app/models/base/Pristine.php | 4 ++++ .../pgsql/app/models/pgsqlmodel/base/Alldbdatatype.php | 4 ++++ .../pgsql/app/models/pgsqlmodel/base/Editcolumn.php | 4 ++++ .../pgsql/app/models/pgsqlmodel/base/Newcolumn.php | 4 ++++ .../pgsql/app/models/pgsqlmodel/base/Pristine.php | 4 ++++ tests/unit/AttributeResolverTest.php | 2 ++ 71 files changed, 299 insertions(+), 17 deletions(-) diff --git a/tests/specs/blog/models/base/Category.php b/tests/specs/blog/models/base/Category.php index f22e0014..1c7cdb97 100644 --- a/tests/specs/blog/models/base/Category.php +++ b/tests/specs/blog/models/base/Category.php @@ -1,5 +1,9 @@ resolve(); $fixture = require Yii::getAlias('@fixtures/non-db.php'); $testModel = $fixture['personWatch']; + $testModel->openapiSchema = $model->openapiSchema; self::assertEquals($testModel, $model); } @@ -126,6 +127,7 @@ public function testResolveNonDbModel() $model = $resolver->resolve(); $fixture = require Yii::getAlias('@fixtures/non-db.php'); $testModel = $fixture['PetStatistic']; + $testModel->openapiSchema = $model->openapiSchema; self::assertEquals($testModel, $model); } }