Skip to content

Resolve - Prevent _id suffix in column name on reference properties #21

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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,30 @@ Allow to set foreign key constraint in migrations for ON DELETE event of row in

Allow to set foreign key constraint in migrations for ON UPDATE event of row in database table. For example, see above section for `x-fk-on-delete`.

### `x-fk-column-name`

Provide custom column name in case of relationship column. Example:

```yaml
components:
schemas:
Webhook:
type: object
description: example for x-fk-column-name
properties:
id:
type: integer
name:
type: string
user:
$ref: '../openapi.yaml#/components/schemas/User' # this will automatically create `user_id` column
redelivery_of:
allOf:
- $ref: '../openapi.yaml#/components/schemas/Delivery'
# this will automatically create `redelivery_of_id` column, but to avoid that use below extension
- x-fk-column-name: redelivery_of # this will create `redelivery_of` column instead of `redelivery_of_id`
```

## Many-to-Many relation definition

There are two ways for define many-to-many relations:
Expand Down Expand Up @@ -525,3 +549,4 @@ Professional support, consulting as well as software development services are av
https://www.cebe.cc/en/contact

Development of this library is sponsored by [cebe.:cloud: "Your Professional Deployment Platform"](https://cebe.cloud).

6 changes: 4 additions & 2 deletions src/lib/AttributeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ protected function resolveJunctionTableProperty(PropertySchema $property, bool $
->setIsPrimary($property->isPrimaryKey())
->asReference($junkAttribute['relatedClassName'])
->setPhpType($junkAttribute['phpType'])
->setDbType($junkAttribute['dbType']);
->setDbType($junkAttribute['dbType'])
->setForeignKeyColumnName($property->fkColName);
$relation = Yii::createObject(AttributeRelation::class, [
$property->getName(),
$junkAttribute['relatedTableName'],
Expand Down Expand Up @@ -214,7 +215,8 @@ protected function resolveProperty(PropertySchema $property, bool $isRequired):v
->setXDbType($property->getAttr(CustomSpecAttr::DB_TYPE))
->setXDbDefaultExpression($property->getAttr(CustomSpecAttr::DB_DEFAULT_EXPRESSION))
->setNullable($property->getProperty()->getSerializableData()->nullable ?? null)
->setIsPrimary($property->isPrimaryKey());
->setIsPrimary($property->isPrimaryKey())
->setForeignKeyColumnName($property->fkColName);
if ($property->isReference()) {
if ($property->isVirtual()) {
throw new InvalidDefinitionException('References not supported for virtual attributes');
Expand Down
5 changes: 5 additions & 0 deletions src/lib/CustomSpecAttr.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,9 @@ class CustomSpecAttr
*/
public const FK_ON_DELETE = 'x-fk-on-delete';
public const FK_ON_UPDATE = 'x-fk-on-update';

/**
* Foreign key column name. See README for usage docs
*/
public const FK_COLUMN_NAME = 'x-fk-column-name';
}
20 changes: 19 additions & 1 deletion src/lib/items/Attribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ class Attribute extends BaseObject
*/
public $columnName;

/**
* @var string
* Contains foreign key column name
* @example 'redelivery_of'
* See usage docs in README for more info
*/
public $fkColName;

/**
* should be one of \yii\db\Schema types or complete db column definition
* @var string
Expand Down Expand Up @@ -197,6 +205,14 @@ public function setEnumValues(array $values):Attribute
return $this;
}

public function setForeignKeyColumnName(?string $name):Attribute
{
if ($name) {
$this->fkColName = $name;
}
return $this;
}

/**
* @param int|float|null $min
* @param int|float|null $max
Expand Down Expand Up @@ -231,7 +247,9 @@ public function setIsVirtual(bool $isVirtual = true): Attribute
public function asReference(string $relatedClass):Attribute
{
$this->reference = $relatedClass;
$this->columnName = $this->propertyName . '_id';
$this->columnName = $this->fkColName ?
$this->fkColName :
$this->propertyName . '_id';
return $this;
}

Expand Down
24 changes: 23 additions & 1 deletion src/lib/openapi/PropertySchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ class PropertySchema
public const REFERENCE_PATH = '/components/schemas/';
public const REFERENCE_PATH_LEN = 20;

/**
* @var string
* Contains foreign key column name
* @example 'redelivery_of'
* See usage docs in README for more info
*/
public $fkColName;

/**
* @var \cebe\openapi\SpecObjectInterface
*/
Expand Down Expand Up @@ -85,9 +93,10 @@ public function __construct(SpecObjectInterface $property, string $name, Compone
$this->schema = $schema;
$this->isPk = $name === $schema->getPkName();

$onUpdate = $onDelete = $reference = null;
$onUpdate = $onDelete = $reference = $fkColName = null;

foreach ($property->allOf ?? [] as $element) {
// x-fk-on-delete | x-fk-on-update
if (!empty($element->{CustomSpecAttr::FK_ON_UPDATE})) {
$onUpdate = $element->{CustomSpecAttr::FK_ON_UPDATE};
}
Expand All @@ -97,7 +106,13 @@ public function __construct(SpecObjectInterface $property, string $name, Compone
if ($element instanceof Reference) {
$reference = $element;
}

// x-fk-column-name
if (!empty($element->{CustomSpecAttr::FK_COLUMN_NAME})) {
$fkColName = $element->{CustomSpecAttr::FK_COLUMN_NAME};
}
}

if (
($onUpdate !== null || $onDelete !== null) &&
($reference instanceof Reference)
Expand All @@ -106,6 +121,13 @@ public function __construct(SpecObjectInterface $property, string $name, Compone
$this->onDeleteFkConstraint = $onDelete;
$this->property = $reference;
$property = $this->property;
} elseif (
($fkColName !== null) &&
($reference instanceof Reference)
) {
$this->fkColName = $fkColName;
$this->property = $reference;
$property = $this->property;
}

if ($property instanceof Reference) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/**
* Table for Delivery
*/
class m200000_000000_create_table_deliveries extends \yii\db\Migration
{
public function up()
{
$this->createTable('{{%deliveries}}', [
'id' => $this->primaryKey(),
'title' => $this->text()->null(),
]);
}

public function down()
{
$this->dropTable('{{%deliveries}}');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/**
* Table for User
*/
class m200000_000001_create_table_users extends \yii\db\Migration
{
public function up()
{
$this->createTable('{{%users}}', [
'id' => $this->primaryKey(),
'name' => $this->text()->notNull(),
]);
}

public function down()
{
$this->dropTable('{{%users}}');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

/**
* Table for Webhook
*/
class m200000_000002_create_table_webhooks extends \yii\db\Migration
{
public function up()
{
$this->createTable('{{%webhooks}}', [
'id' => $this->primaryKey(),
'name' => $this->text()->null(),
'user_id' => $this->integer()->null()->defaultValue(null),
'redelivery_of' => $this->integer()->null()->defaultValue(null),
]);
$this->addForeignKey('fk_webhooks_user_id_users_id', '{{%webhooks}}', 'user_id', '{{%users}}', 'id');
$this->addForeignKey('fk_webhooks_redelivery_of_deliveries_id', '{{%webhooks}}', 'redelivery_of', '{{%deliveries}}', 'id');
}

public function down()
{
$this->dropForeignKey('fk_webhooks_redelivery_of_deliveries_id', '{{%webhooks}}');
$this->dropForeignKey('fk_webhooks_user_id_users_id', '{{%webhooks}}');
$this->dropTable('{{%webhooks}}');
}
}
144 changes: 144 additions & 0 deletions tests/specs/fk_col_name/app/models/BaseModelFaker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php

namespace app\models;

use Faker\Factory as FakerFactory;
use Faker\Generator;
use Faker\UniqueGenerator;

/**
* Base fake data generator
*/
abstract class BaseModelFaker
{
/**
* @var Generator
*/
protected $faker;
/**
* @var UniqueGenerator
*/
protected $uniqueFaker;

public function __construct()
{
$this->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));
}
}
10 changes: 10 additions & 0 deletions tests/specs/fk_col_name/app/models/Delivery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace app\models;

class Delivery extends \app\models\base\Delivery
{


}

Loading