Skip to content

Commit 5b785c4

Browse files
authored
Merge pull request #61 from php-openapi/60-description-of-a-property-in-spec-must-correspond-to-db-table-column-comment
Resolve: Description of a property in spec must correspond to DB TABLE COLUMN COMMENT #60
2 parents e993ed3 + d5be8ce commit 5b785c4

File tree

64 files changed

+1304
-119
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1304
-119
lines changed

README.md

+54
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,60 @@ Generated URL rules config for above is (in `urls.rest.php` or pertinent file):
465465
```
466466
`x-route` does not support [Yii Modules](https://www.yiiframework.com/doc/guide/2.0/en/structure-modules).
467467

468+
### `x-description-is-comment`
469+
470+
boolean; default: false
471+
472+
When a new database table is created from new OpenAPI component schema, description of a property will be used as
473+
comment of column (of database table).
474+
475+
This extension is used when a description is edited for existing property, and you want to generate migration for its
476+
corresponding column comment changes.
477+
478+
This extension can be used at 3 place:
479+
480+
**1. root level (highest priority)**
481+
482+
```yaml
483+
openapi: 3.0.3
484+
x-description-is-comment: true
485+
info:
486+
title: Description
487+
```
488+
489+
This will create migration of any changed description of component schema property present throughout the spec.
490+
491+
**2. component schema level**
492+
493+
```yaml
494+
components:
495+
schemas:
496+
Fruit:
497+
type: object
498+
x-description-is-comment: true
499+
```
500+
501+
This will create migration of changed description of only properties of component schema which have this extension.
502+
503+
**3. property level (lowest priority)**
504+
505+
```yaml
506+
components:
507+
schemas:
508+
Fruit:
509+
type: object
510+
properties:
511+
id:
512+
type: integer
513+
name:
514+
type: string
515+
nullable: false
516+
x-description-is-comment: true
517+
description: Hi there
518+
```
519+
520+
Migrations will be only generated for changed description of properties having this extension.
521+
468522
## Many-to-Many relation definition
469523

470524
There are two ways for define many-to-many relations:

src/lib/AttributeResolver.php

+2
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ public function resolve(): DbModel
117117
//For valid primary keys for junction tables
118118
'junctionCols' => $this->isJunctionSchema ? $this->junctions->junctionCols($this->schemaName) : [],
119119
'isNotDb' => $this->componentSchema->isNonDb(),
120+
'descriptionIsComment' => !empty(($this->componentSchema->getSchema()->{CustomSpecAttr::DESC_IS_COMMENT}))
120121
],
121122
]);
122123
}
@@ -225,6 +226,7 @@ protected function resolveProperty(
225226
->setDefault($property->guessDefault())
226227
->setXDbType($property->getAttr(CustomSpecAttr::DB_TYPE))
227228
->setXDbDefaultExpression($property->getAttr(CustomSpecAttr::DB_DEFAULT_EXPRESSION))
229+
->setXDescriptionIsComment($property->getAttr(CustomSpecAttr::DESC_IS_COMMENT))
228230
->setNullable($nullableValue)
229231
->setIsPrimary($property->isPrimaryKey())
230232
->setForeignKeyColumnName($property->fkColName)

src/lib/ColumnToCode.php

+63-55
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ class ColumnToCode
8484
*/
8585
private $isPk = false;
8686

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

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

9191
/**
9292
* @var bool
@@ -150,6 +150,60 @@ public function __construct(
150150
$this->resolve();
151151
}
152152

153+
private function resolve():void
154+
{
155+
$dbType = $this->typeWithoutSize(strtolower($this->column->dbType));
156+
$type = $this->column->type;
157+
$this->resolvePosition();
158+
//Primary Keys
159+
if (array_key_exists($type, self::PK_TYPE_MAP)) {
160+
$this->rawParts['type'] = $type;
161+
$this->fluentParts['type'] = self::PK_TYPE_MAP[$type];
162+
$this->isPk = true;
163+
return;
164+
}
165+
if (array_key_exists($dbType, self::PK_TYPE_MAP)) {
166+
$this->rawParts['type'] = $dbType;
167+
$this->fluentParts['type'] = self::PK_TYPE_MAP[$dbType];
168+
$this->isPk = true;
169+
return;
170+
}
171+
172+
if ($dbType === 'varchar') {
173+
$type = $dbType = 'string';
174+
}
175+
$fluentSize = $this->column->size ? '(' . $this->column->size . ')' : '()';
176+
$rawSize = $this->column->size ? '(' . $this->column->size . ')' : '';
177+
$this->rawParts['nullable'] = $this->column->allowNull ? 'NULL' : 'NOT NULL';
178+
$this->fluentParts['nullable'] = $this->column->allowNull === true ? 'null()' : 'notNull()';
179+
180+
$this->fluentParts['comment'] = $this->column->comment ? 'comment('.var_export($this->column->comment, true).')' : $this->fluentParts['comment'];
181+
$this->rawParts['comment'] = $this->column->comment ? 'COMMENT '.var_export($this->column->comment, true) : $this->rawParts['comment'];
182+
183+
if (array_key_exists($dbType, self::INT_TYPE_MAP)) {
184+
$this->fluentParts['type'] = self::INT_TYPE_MAP[$dbType] . $fluentSize;
185+
$this->rawParts['type'] =
186+
$this->column->dbType . (strpos($this->column->dbType, '(') !== false ? '' : $rawSize);
187+
} elseif (array_key_exists($type, self::INT_TYPE_MAP)) {
188+
$this->fluentParts['type'] = self::INT_TYPE_MAP[$type] . $fluentSize;
189+
$this->rawParts['type'] =
190+
$this->column->dbType . (strpos($this->column->dbType, '(') !== false ? '' : $rawSize);
191+
} elseif ($this->isEnum()) {
192+
$this->resolveEnumType();
193+
} elseif ($this->isDecimal()) {
194+
$this->fluentParts['type'] = $this->column->dbType;
195+
$this->rawParts['type'] = $this->column->dbType;
196+
} else {
197+
$this->fluentParts['type'] = $type . $fluentSize;
198+
$this->rawParts['type'] =
199+
$this->column->dbType . (strpos($this->column->dbType, '(') !== false ? '' : $rawSize);
200+
}
201+
202+
$this->isBuiltinType = $this->raw ? false : $this->getIsBuiltinType($type, $dbType);
203+
204+
$this->resolveDefaultValue();
205+
}
206+
153207
public function getCode(bool $quoted = false):string
154208
{
155209
if ($this->isPk) {
@@ -160,7 +214,8 @@ public function getCode(bool $quoted = false):string
160214
$this->fluentParts['type'],
161215
$this->fluentParts['nullable'],
162216
$this->fluentParts['default'],
163-
$this->fluentParts['position']
217+
$this->fluentParts['position'],
218+
$this->fluentParts['comment'],
164219
]);
165220
array_unshift($parts, '$this');
166221
return implode('->', array_filter(array_map('trim', $parts)));
@@ -175,9 +230,12 @@ public function getCode(bool $quoted = false):string
175230
}
176231

177232
$code = $this->rawParts['type'] . ' ' . $this->rawParts['nullable'] . $default;
178-
if ((ApiGenerator::isMysql() || ApiGenerator::isMariaDb()) && $this->rawParts['position']) {
179-
$code .= ' ' . $this->rawParts['position'];
233+
234+
if ((ApiGenerator::isMysql() || ApiGenerator::isMariaDb())) {
235+
$code .= $this->rawParts['position'] ? ' ' . $this->rawParts['position'] : '';
236+
$code .= $this->rawParts['comment'] ? ' '.$this->rawParts['comment'] : '';
180237
}
238+
181239
if (ApiGenerator::isPostgres() && $this->alterByXDbType) {
182240
return $quoted ? VarDumper::export($this->rawParts['type']) : $this->rawParts['type'];
183241
}
@@ -320,56 +378,6 @@ private function defaultValueArray(array $value):string
320378
return "'{" . trim(Json::encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_QUOT), '[]') . "}'";
321379
}
322380

323-
private function resolve():void
324-
{
325-
$dbType = $this->typeWithoutSize(strtolower($this->column->dbType));
326-
$type = $this->column->type;
327-
$this->resolvePosition();
328-
//Primary Keys
329-
if (array_key_exists($type, self::PK_TYPE_MAP)) {
330-
$this->rawParts['type'] = $type;
331-
$this->fluentParts['type'] = self::PK_TYPE_MAP[$type];
332-
$this->isPk = true;
333-
return;
334-
}
335-
if (array_key_exists($dbType, self::PK_TYPE_MAP)) {
336-
$this->rawParts['type'] = $dbType;
337-
$this->fluentParts['type'] = self::PK_TYPE_MAP[$dbType];
338-
$this->isPk = true;
339-
return;
340-
}
341-
342-
if ($dbType === 'varchar') {
343-
$type = $dbType = 'string';
344-
}
345-
$fluentSize = $this->column->size ? '(' . $this->column->size . ')' : '()';
346-
$rawSize = $this->column->size ? '(' . $this->column->size . ')' : '';
347-
$this->rawParts['nullable'] = $this->column->allowNull ? 'NULL' : 'NOT NULL';
348-
$this->fluentParts['nullable'] = $this->column->allowNull === true ? 'null()' : 'notNull()';
349-
if (array_key_exists($dbType, self::INT_TYPE_MAP)) {
350-
$this->fluentParts['type'] = self::INT_TYPE_MAP[$dbType] . $fluentSize;
351-
$this->rawParts['type'] =
352-
$this->column->dbType . (strpos($this->column->dbType, '(') !== false ? '' : $rawSize);
353-
} elseif (array_key_exists($type, self::INT_TYPE_MAP)) {
354-
$this->fluentParts['type'] = self::INT_TYPE_MAP[$type] . $fluentSize;
355-
$this->rawParts['type'] =
356-
$this->column->dbType . (strpos($this->column->dbType, '(') !== false ? '' : $rawSize);
357-
} elseif ($this->isEnum()) {
358-
$this->resolveEnumType();
359-
} elseif ($this->isDecimal()) {
360-
$this->fluentParts['type'] = $this->column->dbType;
361-
$this->rawParts['type'] = $this->column->dbType;
362-
} else {
363-
$this->fluentParts['type'] = $type . $fluentSize;
364-
$this->rawParts['type'] =
365-
$this->column->dbType . (strpos($this->column->dbType, '(') !== false ? '' : $rawSize);
366-
}
367-
368-
$this->isBuiltinType = $this->raw ? false : $this->getIsBuiltinType($type, $dbType);
369-
370-
$this->resolveDefaultValue();
371-
}
372-
373381
/**
374382
* @param $type
375383
* @param $dbType

src/lib/CustomSpecAttr.php

+6
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,10 @@ class CustomSpecAttr
5656
* Custom route (controller ID/action ID) instead of auto-generated. See README for usage docs. https://github.com/cebe/yii2-openapi/issues/144
5757
*/
5858
public const ROUTE = 'x-route';
59+
60+
61+
/**
62+
* Generate migrations for changed description of property. More docs is present in README.md file
63+
*/
64+
public const DESC_IS_COMMENT = 'x-description-is-comment';
5965
}

src/lib/generators/MigrationsGenerator.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
use cebe\yii2openapi\lib\items\DbModel;
1313
use cebe\yii2openapi\lib\items\MigrationModel;
1414
use cebe\yii2openapi\lib\migrations\BaseMigrationBuilder;
15-
use cebe\yii2openapi\lib\migrations\MigrationRecordBuilder;
1615
use cebe\yii2openapi\lib\migrations\MysqlMigrationBuilder;
1716
use cebe\yii2openapi\lib\migrations\PostgresMigrationBuilder;
1817
use Exception;
@@ -139,10 +138,11 @@ public function buildMigrations():array
139138
*/
140139
protected function createBuilder(DbModel $model):BaseMigrationBuilder
141140
{
141+
$params = [$this->db, $model, $this->config];
142142
if ($this->db->getDriverName() === 'pgsql') {
143-
return Yii::createObject(PostgresMigrationBuilder::class, [$this->db, $model]);
143+
return Yii::createObject(PostgresMigrationBuilder::class, $params);
144144
}
145-
return Yii::createObject(MysqlMigrationBuilder::class, [$this->db, $model]);
145+
return Yii::createObject(MysqlMigrationBuilder::class, $params);
146146
}
147147

148148
/**

src/lib/items/Attribute.php

+19-7
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,6 @@ class Attribute extends BaseObject
6161
*/
6262
public $dbType = 'string';
6363

64-
/**
65-
* Custom db type
66-
* string | null | false
67-
* if `false` then this attribute is virtual
68-
*/
69-
public $xDbType;
70-
7164
/**
7265
* nullable
7366
* bool | null
@@ -128,6 +121,18 @@ class Attribute extends BaseObject
128121
**/
129122
public $isVirtual = false;
130123

124+
/**
125+
* Custom db type
126+
* string | null | false
127+
* if `false` then this attribute is virtual
128+
*/
129+
public $xDbType;
130+
131+
/**
132+
* @see \cebe\yii2openapi\lib\CustomSpecAttr::DESC_IS_COMMENT
133+
*/
134+
public ?bool $xDescriptionIsComment = false;
135+
131136
public function __construct(string $propertyName, array $config = [])
132137
{
133138
$this->propertyName = $propertyName;
@@ -322,6 +327,7 @@ public function toColumnSchema(): ColumnSchema
322327
'allowNull' => $this->allowNull(),
323328
'size' => $this->size > 0 ? $this->size : null,
324329
'xDbType' => $this->xDbType,
330+
'comment' => $this->description,
325331
]);
326332
$column->isPrimaryKey = $this->primary;
327333
$column->autoIncrement = $this->primary && $this->phpType === 'int';
@@ -396,4 +402,10 @@ public function handleDecimal(ColumnSchema $columnSchema): void
396402
$columnSchema->dbType = $decimalAttributes['dbType'];
397403
}
398404
}
405+
406+
public function setXDescriptionIsComment($xDescriptionIsComment): Attribute
407+
{
408+
$this->xDescriptionIsComment = $xDescriptionIsComment;
409+
return $this;
410+
}
399411
}

src/lib/items/DbModel.php

+2
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ class DbModel extends BaseObject
8888
*/
8989
public $scenarioDefaultDescription = "Scenario {scenarioName}";
9090

91+
public bool $descriptionIsComment = false;
92+
9193
/**
9294
* @var array Automatically generated scenarios from the model 'x-scenarios'.
9395
*/

0 commit comments

Comments
 (0)