Skip to content

Resolve: Description of a property in spec must correspond to DB TABLE COLUMN COMMENT #60 #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9ec5b1b
Create PR
SOHELAHMED7 Sep 28, 2024
87b01da
Add failing test
SOHELAHMED7 Sep 28, 2024
1e408b7
Fix this issue
SOHELAHMED7 Sep 28, 2024
2fb2d7b
Move `resolve()` method
SOHELAHMED7 Sep 28, 2024
f998279
Add test to ensure existing comments are preserved
SOHELAHMED7 Sep 28, 2024
4a11785
Fix failing tests
SOHELAHMED7 Sep 28, 2024
d4dd074
Add comments handling for PgSQL as it is different from MySQL - WIP
SOHELAHMED7 Sep 28, 2024
b18c521
Add comments handling for PgSQL as it is different from MySQL - 2
SOHELAHMED7 Sep 29, 2024
72a7a6a
Add comments handling for PgSQL as it is different from MySQL - done
SOHELAHMED7 Sep 29, 2024
7d557c4
Complete the test
SOHELAHMED7 Sep 29, 2024
d0415fb
Fix bug and failing tests
SOHELAHMED7 Sep 30, 2024
fa47121
Rename function and constant
SOHELAHMED7 Sep 30, 2024
3fbf3c7
Handle quoting of values
SOHELAHMED7 Oct 1, 2024
6ea5f1d
Resolve TODOs
SOHELAHMED7 Oct 1, 2024
2647645
Merge branches 'master' and '60-description-of-a-property-in-spec-mus…
SOHELAHMED7 Nov 15, 2024
7fa9cde
Fix failing test
SOHELAHMED7 Nov 15, 2024
af961aa
Make this feature optional - WIP - https://github.com/php-openapi/yii…
SOHELAHMED7 Nov 15, 2024
d549831
Implement for table/compo schema - WIP
SOHELAHMED7 Nov 18, 2024
53416d2
WIP
SOHELAHMED7 Nov 19, 2024
3815cb3
Implement for property level extension
SOHELAHMED7 Nov 20, 2024
8fd5b8b
Add more tests
SOHELAHMED7 Nov 21, 2024
051900a
Fix failing tests
SOHELAHMED7 Nov 21, 2024
9d4dc38
Add doc
SOHELAHMED7 Nov 21, 2024
20fad45
Resolve TODO
SOHELAHMED7 Nov 21, 2024
692c4aa
Implement for PgSQL
SOHELAHMED7 Nov 21, 2024
d5be8ce
Refactor
SOHELAHMED7 Nov 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,60 @@ Generated URL rules config for above is (in `urls.rest.php` or pertinent file):
```
`x-route` does not support [Yii Modules](https://www.yiiframework.com/doc/guide/2.0/en/structure-modules).

### `x-description-is-comment`

boolean; default: false

When a new database table is created from new OpenAPI component schema, description of a property will be used as
comment of column (of database table).

This extension is used when a description is edited for existing property, and you want to generate migration for its
corresponding column comment changes.

This extension can be used at 3 place:

**1. root level (highest priority)**

```yaml
openapi: 3.0.3
x-description-is-comment: true
info:
title: Description
```

This will create migration of any changed description of component schema property present throughout the spec.

**2. component schema level**

```yaml
components:
schemas:
Fruit:
type: object
x-description-is-comment: true
```

This will create migration of changed description of only properties of component schema which have this extension.

**3. property level (lowest priority)**

```yaml
components:
schemas:
Fruit:
type: object
properties:
id:
type: integer
name:
type: string
nullable: false
x-description-is-comment: true
description: Hi there
```

Migrations will be only generated for changed description of properties having this extension.

## Many-to-Many relation definition

There are two ways for define many-to-many relations:
Expand Down
2 changes: 2 additions & 0 deletions src/lib/AttributeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ public function resolve(): DbModel
//For valid primary keys for junction tables
'junctionCols' => $this->isJunctionSchema ? $this->junctions->junctionCols($this->schemaName) : [],
'isNotDb' => $this->componentSchema->isNonDb(),
'descriptionIsComment' => !empty(($this->componentSchema->getSchema()->{CustomSpecAttr::DESC_IS_COMMENT}))
],
]);
}
Expand Down Expand Up @@ -225,6 +226,7 @@ protected function resolveProperty(
->setDefault($property->guessDefault())
->setXDbType($property->getAttr(CustomSpecAttr::DB_TYPE))
->setXDbDefaultExpression($property->getAttr(CustomSpecAttr::DB_DEFAULT_EXPRESSION))
->setXDescriptionIsComment($property->getAttr(CustomSpecAttr::DESC_IS_COMMENT))
->setNullable($nullableValue)
->setIsPrimary($property->isPrimaryKey())
->setForeignKeyColumnName($property->fkColName)
Expand Down
118 changes: 63 additions & 55 deletions src/lib/ColumnToCode.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ class ColumnToCode
*/
private $isPk = false;

private $rawParts = ['type' => null, 'nullable' => null, 'default' => null, 'position' => null];
private $rawParts = ['type' => null, 'nullable' => null, 'default' => null, 'position' => null, 'comment' => null];

private $fluentParts = ['type' => null, 'nullable' => null, 'default' => null, 'position' => null];
private $fluentParts = ['type' => null, 'nullable' => null, 'default' => null, 'position' => null, 'comment' => null];

/**
* @var bool
Expand Down Expand Up @@ -150,6 +150,60 @@ public function __construct(
$this->resolve();
}

private function resolve():void
{
$dbType = $this->typeWithoutSize(strtolower($this->column->dbType));
$type = $this->column->type;
$this->resolvePosition();
//Primary Keys
if (array_key_exists($type, self::PK_TYPE_MAP)) {
$this->rawParts['type'] = $type;
$this->fluentParts['type'] = self::PK_TYPE_MAP[$type];
$this->isPk = true;
return;
}
if (array_key_exists($dbType, self::PK_TYPE_MAP)) {
$this->rawParts['type'] = $dbType;
$this->fluentParts['type'] = self::PK_TYPE_MAP[$dbType];
$this->isPk = true;
return;
}

if ($dbType === 'varchar') {
$type = $dbType = 'string';
}
$fluentSize = $this->column->size ? '(' . $this->column->size . ')' : '()';
$rawSize = $this->column->size ? '(' . $this->column->size . ')' : '';
$this->rawParts['nullable'] = $this->column->allowNull ? 'NULL' : 'NOT NULL';
$this->fluentParts['nullable'] = $this->column->allowNull === true ? 'null()' : 'notNull()';

$this->fluentParts['comment'] = $this->column->comment ? 'comment('.var_export($this->column->comment, true).')' : $this->fluentParts['comment'];
$this->rawParts['comment'] = $this->column->comment ? 'COMMENT '.var_export($this->column->comment, true) : $this->rawParts['comment'];

if (array_key_exists($dbType, self::INT_TYPE_MAP)) {
$this->fluentParts['type'] = self::INT_TYPE_MAP[$dbType] . $fluentSize;
$this->rawParts['type'] =
$this->column->dbType . (strpos($this->column->dbType, '(') !== false ? '' : $rawSize);
} elseif (array_key_exists($type, self::INT_TYPE_MAP)) {
$this->fluentParts['type'] = self::INT_TYPE_MAP[$type] . $fluentSize;
$this->rawParts['type'] =
$this->column->dbType . (strpos($this->column->dbType, '(') !== false ? '' : $rawSize);
} elseif ($this->isEnum()) {
$this->resolveEnumType();
} elseif ($this->isDecimal()) {
$this->fluentParts['type'] = $this->column->dbType;
$this->rawParts['type'] = $this->column->dbType;
} else {
$this->fluentParts['type'] = $type . $fluentSize;
$this->rawParts['type'] =
$this->column->dbType . (strpos($this->column->dbType, '(') !== false ? '' : $rawSize);
}

$this->isBuiltinType = $this->raw ? false : $this->getIsBuiltinType($type, $dbType);

$this->resolveDefaultValue();
}

public function getCode(bool $quoted = false):string
{
if ($this->isPk) {
Expand All @@ -160,7 +214,8 @@ public function getCode(bool $quoted = false):string
$this->fluentParts['type'],
$this->fluentParts['nullable'],
$this->fluentParts['default'],
$this->fluentParts['position']
$this->fluentParts['position'],
$this->fluentParts['comment'],
]);
array_unshift($parts, '$this');
return implode('->', array_filter(array_map('trim', $parts)));
Expand All @@ -175,9 +230,12 @@ public function getCode(bool $quoted = false):string
}

$code = $this->rawParts['type'] . ' ' . $this->rawParts['nullable'] . $default;
if ((ApiGenerator::isMysql() || ApiGenerator::isMariaDb()) && $this->rawParts['position']) {
$code .= ' ' . $this->rawParts['position'];

if ((ApiGenerator::isMysql() || ApiGenerator::isMariaDb())) {
$code .= $this->rawParts['position'] ? ' ' . $this->rawParts['position'] : '';
$code .= $this->rawParts['comment'] ? ' '.$this->rawParts['comment'] : '';
}

if (ApiGenerator::isPostgres() && $this->alterByXDbType) {
return $quoted ? VarDumper::export($this->rawParts['type']) : $this->rawParts['type'];
}
Expand Down Expand Up @@ -320,56 +378,6 @@ private function defaultValueArray(array $value):string
return "'{" . trim(Json::encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_QUOT), '[]') . "}'";
}

private function resolve():void
{
$dbType = $this->typeWithoutSize(strtolower($this->column->dbType));
$type = $this->column->type;
$this->resolvePosition();
//Primary Keys
if (array_key_exists($type, self::PK_TYPE_MAP)) {
$this->rawParts['type'] = $type;
$this->fluentParts['type'] = self::PK_TYPE_MAP[$type];
$this->isPk = true;
return;
}
if (array_key_exists($dbType, self::PK_TYPE_MAP)) {
$this->rawParts['type'] = $dbType;
$this->fluentParts['type'] = self::PK_TYPE_MAP[$dbType];
$this->isPk = true;
return;
}

if ($dbType === 'varchar') {
$type = $dbType = 'string';
}
$fluentSize = $this->column->size ? '(' . $this->column->size . ')' : '()';
$rawSize = $this->column->size ? '(' . $this->column->size . ')' : '';
$this->rawParts['nullable'] = $this->column->allowNull ? 'NULL' : 'NOT NULL';
$this->fluentParts['nullable'] = $this->column->allowNull === true ? 'null()' : 'notNull()';
if (array_key_exists($dbType, self::INT_TYPE_MAP)) {
$this->fluentParts['type'] = self::INT_TYPE_MAP[$dbType] . $fluentSize;
$this->rawParts['type'] =
$this->column->dbType . (strpos($this->column->dbType, '(') !== false ? '' : $rawSize);
} elseif (array_key_exists($type, self::INT_TYPE_MAP)) {
$this->fluentParts['type'] = self::INT_TYPE_MAP[$type] . $fluentSize;
$this->rawParts['type'] =
$this->column->dbType . (strpos($this->column->dbType, '(') !== false ? '' : $rawSize);
} elseif ($this->isEnum()) {
$this->resolveEnumType();
} elseif ($this->isDecimal()) {
$this->fluentParts['type'] = $this->column->dbType;
$this->rawParts['type'] = $this->column->dbType;
} else {
$this->fluentParts['type'] = $type . $fluentSize;
$this->rawParts['type'] =
$this->column->dbType . (strpos($this->column->dbType, '(') !== false ? '' : $rawSize);
}

$this->isBuiltinType = $this->raw ? false : $this->getIsBuiltinType($type, $dbType);

$this->resolveDefaultValue();
}

/**
* @param $type
* @param $dbType
Expand Down
6 changes: 6 additions & 0 deletions src/lib/CustomSpecAttr.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,10 @@ class CustomSpecAttr
* Custom route (controller ID/action ID) instead of auto-generated. See README for usage docs. https://github.com/cebe/yii2-openapi/issues/144
*/
public const ROUTE = 'x-route';


/**
* Generate migrations for changed description of property. More docs is present in README.md file
*/
public const DESC_IS_COMMENT = 'x-description-is-comment';
}
6 changes: 3 additions & 3 deletions src/lib/generators/MigrationsGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use cebe\yii2openapi\lib\items\DbModel;
use cebe\yii2openapi\lib\items\MigrationModel;
use cebe\yii2openapi\lib\migrations\BaseMigrationBuilder;
use cebe\yii2openapi\lib\migrations\MigrationRecordBuilder;
use cebe\yii2openapi\lib\migrations\MysqlMigrationBuilder;
use cebe\yii2openapi\lib\migrations\PostgresMigrationBuilder;
use Exception;
Expand Down Expand Up @@ -139,10 +138,11 @@ public function buildMigrations():array
*/
protected function createBuilder(DbModel $model):BaseMigrationBuilder
{
$params = [$this->db, $model, $this->config];
if ($this->db->getDriverName() === 'pgsql') {
return Yii::createObject(PostgresMigrationBuilder::class, [$this->db, $model]);
return Yii::createObject(PostgresMigrationBuilder::class, $params);
}
return Yii::createObject(MysqlMigrationBuilder::class, [$this->db, $model]);
return Yii::createObject(MysqlMigrationBuilder::class, $params);
}

/**
Expand Down
26 changes: 19 additions & 7 deletions src/lib/items/Attribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,6 @@ class Attribute extends BaseObject
*/
public $dbType = 'string';

/**
* Custom db type
* string | null | false
* if `false` then this attribute is virtual
*/
public $xDbType;

/**
* nullable
* bool | null
Expand Down Expand Up @@ -128,6 +121,18 @@ class Attribute extends BaseObject
**/
public $isVirtual = false;

/**
* Custom db type
* string | null | false
* if `false` then this attribute is virtual
*/
public $xDbType;

/**
* @see \cebe\yii2openapi\lib\CustomSpecAttr::DESC_IS_COMMENT
*/
public ?bool $xDescriptionIsComment = false;

public function __construct(string $propertyName, array $config = [])
{
$this->propertyName = $propertyName;
Expand Down Expand Up @@ -322,6 +327,7 @@ public function toColumnSchema(): ColumnSchema
'allowNull' => $this->allowNull(),
'size' => $this->size > 0 ? $this->size : null,
'xDbType' => $this->xDbType,
'comment' => $this->description,
]);
$column->isPrimaryKey = $this->primary;
$column->autoIncrement = $this->primary && $this->phpType === 'int';
Expand Down Expand Up @@ -396,4 +402,10 @@ public function handleDecimal(ColumnSchema $columnSchema): void
$columnSchema->dbType = $decimalAttributes['dbType'];
}
}

public function setXDescriptionIsComment($xDescriptionIsComment): Attribute
{
$this->xDescriptionIsComment = $xDescriptionIsComment;
return $this;
}
}
2 changes: 2 additions & 0 deletions src/lib/items/DbModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ class DbModel extends BaseObject
*/
public $scenarioDefaultDescription = "Scenario {scenarioName}";

public bool $descriptionIsComment = false;

/**
* @var array Automatically generated scenarios from the model 'x-scenarios'.
*/
Expand Down
Loading
Loading