Skip to content

Commit

Permalink
Merge pull request #61 from php-openapi/60-description-of-a-property-…
Browse files Browse the repository at this point in the history
…in-spec-must-correspond-to-db-table-column-comment

Resolve: Description of a property in spec must correspond to DB TABLE COLUMN COMMENT #60
  • Loading branch information
cebe authored Nov 21, 2024
2 parents e993ed3 + d5be8ce commit 5b785c4
Show file tree
Hide file tree
Showing 64 changed files with 1,304 additions and 119 deletions.
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

0 comments on commit 5b785c4

Please sign in to comment.