Skip to content
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

feature/3.1 #20

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
2 changes: 2 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@
- config automatically excludes tables to proces (migrations, etc)
- consider relations less verbose if automatically detectable via column names


- HasManyRelations etc ~> ctor arguments are nullable for now but shouldnt be because relations generator expects them present
139 changes: 60 additions & 79 deletions src/Parser/RelationsParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use KitLoong\MigrationsGenerator\Schema\Models\ForeignKey;
use KitLoong\MigrationsGenerator\Schema\Models\Index;
use KitLoong\MigrationsGenerator\Schema\Schema;
use Pepijnolivier\EloquentModelGenerator\Relations\SchemaRelations;
use Pepijnolivier\EloquentModelGenerator\Relations\TableRelations;
use Pepijnolivier\EloquentModelGenerator\Relations\Types\BelongsToManyRelation;
use Pepijnolivier\EloquentModelGenerator\Relations\Types\BelongsToRelation;
Expand All @@ -18,42 +19,38 @@ class RelationsParser
/** @var Schema $schema */
protected Schema $schema;

/** @var TableRelations[] $relationsPerTable */
private array $relationsPerTable;
/** @var String[] $tables */
protected array $tables;

/** @var SchemaRelations $schemaRelations */
protected SchemaRelations $schemaRelations;

use HelperTrait;

public function __construct(Schema $schema)
{
$this->schema = $schema;
$this->relationsPerTable = $this->parse();

$this->init();

$this->parse();
}

public function getRelationsForTable(string $table): ?TableRelations
{
return $this->relationsPerTable[$table] ?? null;
protected function init() {
$this->tables = $this->schema->getTableNames()->toArray();
$this->schemaRelations = new SchemaRelations($this->tables);
}


/** @return TableRelations[] */
public function getRelationsPerTable(): array
public function getSchema(): SchemaRelations
{
return $this->relationsPerTable;
return $this->schemaRelations;
}

private function parse()
{
$tables = $this->schema->getTableNames();

/** @var TableRelations[] $relationsByTable */
$relationsByTable = [];

// first create empty ruleset for each table
foreach ($tables as $tableName) {
$relationsByTable[$tableName] = new TableRelations($tableName);
}

/** @var string $tableName */
foreach ($tables as $tableName) {
foreach ($this->tables as $tableName) {
$table = $this->schema->getTable($tableName);
$foreign = $this->getForeignKeysForTable($tableName);
$primary = $this->getPrimaryKeysForTable($tableName);
Expand All @@ -62,69 +59,69 @@ private function parse()
$isManyToMany = $this->isManyToManyTable($tableName);

if ($isManyToMany === true) {
$this->addManyToManyRelations($tableName, $relationsByTable);
$this->addManyToManyRelations($tableName);
}

foreach ($foreign as $fk) {
$isOneToOne = $this->isOneToOne($fk, $primary);

if ($isOneToOne) {
$this->addOneToOneRules($tableName, $fk, $relationsByTable);
$this->addOneToOneRules($tableName, $fk);
} else {
$this->addOneToManyRules($tableName, $fk, $relationsByTable);
$this->addOneToManyRules($tableName, $fk);
}
}
}

return $relationsByTable;
}

private function addOneToManyRules(string $tableName, ForeignKey $fk, array &$relationsByTable)
/**
* @var TableRelations[] $relationsByTable
*/
private function addOneToManyRules(string $tableName, ForeignKey $fk)
{
// $table belongs to $FK
// $tableName belongs to $FK
// FK hasMany $table

$tableNames = $this->schema->getTableNames()->toArray();
$fkTable = $fk->getForeignTableName();

$localColumns = $fk->getLocalColumns();
$foreignColumns = $fk->getForeignColumns();

if(count($localColumns) > 1) {
// throw new \Exception('Composite local keys are not supported');
return;
}

if(count($foreignColumns) > 1) {
// throw new \Exception('Composite foreign columns are not supported');
// validate: ensure there's exactly 1 local and 1 foreign column
$isComposite = $this->hasCompositeLocalOrForeignColumn($fk);
if(!$isComposite) {
return;
}

$field = $localColumns[0];
$references = $foreignColumns[0];
$fkLocalColumn = $fk->getLocalColumns()[0];
$fkForeignColumn = $fk->getForeignColumns()[0];

if(in_array($fkTable, $tableNames)) {
$hasManyModel = $this->generateModelNameFromTableName($tableName);
$hasManyFunctionName = $this->getPluralFunctionName($hasManyModel);

// @toconsider: if it's a table with only 2 columns, and they are both the FK
// then it's just a pure pivot table. We might not want to add a relation for that.
$relation = HasManyRelation::fromTable($tableName, $fkLocalColumn, $fkForeignColumn);
$this->schemaRelations->addHasManyRelation($fkTable, $relation);
}
if(in_array($tableName, $tableNames)) {
$relation = BelongsToRelation::fromTable($fkTable, $fkLocalColumn, $fkForeignColumn);
$this->schemaRelations->addBelongsToRelation($tableName, $relation);
}
}

$relation = new HasManyRelation($hasManyFunctionName, $hasManyModel, $field, $references);
private function hasCompositeLocalOrForeignColumn(ForeignKey $fk) {

/** @var TableRelations $tableRelations */
$tableRelations = $relationsByTable[$fkTable];
$tableRelations->addHasManyRelation($relation);
$fkLocalColumns = $fk->getLocalColumns();
$fkForeignColumns = $fk->getForeignColumns();

if(count($fkLocalColumns) > 1) {
// throw new \Exception('Composite local keys are not supported');
return true;
}
if(in_array($tableName, $tableNames)) {
$belongsToModel = $this->generateModelNameFromTableName($fkTable);
$belongsToFunctionName = $this->getSingularFunctionName($belongsToModel);
$relation = new BelongsToRelation($belongsToFunctionName, $belongsToModel, $field, $references);

/** @var TableRelations $tableRelations */
$tableRelations = $relationsByTable[$tableName];
$tableRelations->addBelongsToRelation($relation);
if(count($fkForeignColumns) > 1) {
// throw new \Exception('Composite foreign columns are not supported');
return true;
}

return false;
}

private function addOneToOneRules(string $tableName, ForeignKey $fk, array &$relationsByTable)
Expand All @@ -133,43 +130,27 @@ private function addOneToOneRules(string $tableName, ForeignKey $fk, array &$rel
//$FK hasOne $table

$tableNames = $this->schema->getTableNames()->toArray();

$fkTable = $fk->getForeignTableName();
$localColumns = $fk->getLocalColumns();
$foreignColumns = $fk->getForeignColumns();

if(count($localColumns) > 1) {
// throw new \Exception('Composite local keys are not supported');
return;
}

if(count($foreignColumns) > 1) {
// throw new \Exception('Composite foreign columns are not supported');
// validate: ensure there's exactly 1 local and 1 foreign column
$isComposite = $this->hasCompositeLocalOrForeignColumn($fk);
if(!$isComposite) {
return;
}

// switched ????
$field = $localColumns[0];
$references = $foreignColumns[0];
$fkLocalColumn = $fk->getLocalColumns()[0];
$fkForeignColumn = $fk->getForeignColumns()[0];

if(in_array($fkTable, $tableNames)) {
$modelName = $this->generateModelNameFromTableName($tableName);
$functionName = $this->getSingularFunctionName($modelName);

$relation = new HasOneRelation($functionName, $modelName, $field, $references);

/** @var TableRelations $tableRelations */
$tableRelations = $relationsByTable[$fkTable];
$tableRelations->addHasOneRelation($relation);
$relation = HasOneRelation::fromTable($tableName, $fkLocalColumn, $fkForeignColumn);
$this->schemaRelations->addHasOneRelation($fkTable, $relation);
}
if(in_array($tableName, $tableNames)) {
$belongsToModel = $this->generateModelNameFromTableName($fkTable);
$belongsToFunctionName = $this->getSingularFunctionName($belongsToModel);
$relation = new BelongsToRelation($belongsToFunctionName, $belongsToModel, $field, $references);

/** @var TableRelations $tableRelations */
$tableRelations = $relationsByTable[$tableName];
$tableRelations->addBelongsToRelation($relation);
$relation = BelongsToRelation::fromTable($fkTable, $fkLocalColumn, $fkForeignColumn);
$this->schemaRelations->addBelongsToRelation($tableName, $relation);

}
}

Expand Down
64 changes: 64 additions & 0 deletions src/Relations/SchemaRelations.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace Pepijnolivier\EloquentModelGenerator\Relations;

use Illuminate\Support\Arr;
use Pepijnolivier\EloquentModelGenerator\Relations\Types\BelongsToManyRelation;
use Pepijnolivier\EloquentModelGenerator\Relations\Types\BelongsToRelation;
use Pepijnolivier\EloquentModelGenerator\Relations\Types\HasManyRelation;
use Pepijnolivier\EloquentModelGenerator\Relations\Types\HasOneRelation;

class SchemaRelations
{
protected array $relations = [];

/**
* @var String[] $tableNames
*/
public function __construct(
protected array $tableNames
) {
$this->init();
}

protected function init() {
foreach($this->tableNames as $tableName) {
$this->relations[$tableName] = new TableRelations($tableName);
}
}

public function getTableRelations(string $table): TableRelations {
$this->throwIfInValidTable($table);
return $this->relations[$table];
}

public function addBelongsToRelation(string $table, BelongsToRelation $relation)
{
$this->throwIfInValidTable($table);
$this->relations[$table]->addBelongsToRelation($relation);
}

public function addHasOneRelation(string $table, HasOneRelation $relation)
{
$this->throwIfInValidTable($table);
$this->relations[$table]->addHasOneRelation($relation);
}

public function addHasManyRelation(string $table, HasManyRelation $relation)
{
$this->throwIfInValidTable($table);
$this->relations[$table]->addHasManyRelation($relation);
}

public function addBelongsToManyRelation(string $table, BelongsToManyRelation $relation)
{
$this->throwIfInValidTable($table);
$this->relations[$table]->addBelongsToManyRelation($relation);
}

protected function throwIfInvalidTable($table) {
if(!isset($this->relations[$table])) {
throw new \Exception("Unknown table: '$table'");
}
}
}
11 changes: 11 additions & 0 deletions src/Relations/Types/BelongsToRelation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,19 @@

namespace Pepijnolivier\EloquentModelGenerator\Relations\Types;

use Pepijnolivier\EloquentModelGenerator\Traits\HelperTrait;

class BelongsToRelation
{
use HelperTrait;

public static function fromTable(string $fkTable, string $fkLocalColumn, string $fkForeignColumn) {
$belongsToModel = self::generateModelNameFromTableName($fkTable);
$belongsToFunctionName = self::getSingularFunctionName($belongsToModel);

return new self($belongsToFunctionName, $belongsToModel, $fkLocalColumn, $fkForeignColumn);
}

public function __construct(
protected string $functionName,
protected string $entityClass,
Expand Down
15 changes: 15 additions & 0 deletions src/Relations/Types/HasManyRelation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,23 @@

namespace Pepijnolivier\EloquentModelGenerator\Relations\Types;

use Pepijnolivier\EloquentModelGenerator\Traits\HelperTrait;

class HasManyRelation
{
use HelperTrait;

public static function fromTable(string $tableName, string $foreignKey, string $localKey): HasManyRelation {
$hasManyModel = self::generateModelNameFromTableName($tableName);
$hasManyFunctionName = self::getPluralFunctionName($hasManyModel);

// @toconsider: if it's a table with only 2 columns, and they are both the FK
// then it's just a pure pivot table. We might not want to add a relation for that.

return new HasManyRelation($hasManyFunctionName, $hasManyModel, $foreignKey, $localKey);

}

public function __construct(
protected string $functionName,
protected string $entityClass,
Expand Down
10 changes: 10 additions & 0 deletions src/Relations/Types/HasOneRelation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,18 @@

namespace Pepijnolivier\EloquentModelGenerator\Relations\Types;

use Pepijnolivier\EloquentModelGenerator\Traits\HelperTrait;

class HasOneRelation
{
use HelperTrait;

public static function fromTable(string $tableName, string $foreignKey, string $localKey): HasOneRelation {
$modelName = self::generateModelNameFromTableName($tableName);
$functionName = self::getSingularFunctionName($modelName);
return new self($functionName, $modelName, $foreignKey, $localKey);
}

public function __construct(
protected string $functionName,
protected string $entityClass,
Expand Down
6 changes: 3 additions & 3 deletions src/Traits/HelperTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@

trait HelperTrait {

protected function getPluralFunctionName($modelName)
protected static function getPluralFunctionName($modelName)
{
$modelName = lcfirst($modelName);
return Str::plural($modelName);
}

protected function getSingularFunctionName($modelName)
protected static function getSingularFunctionName($modelName)
{
$modelName = lcfirst($modelName);
return Str::singular($modelName);
}

protected function generateModelNameFromTableName($table)
protected static function generateModelNameFromTableName($table)
{
return ucfirst(Str::camel(Str::singular($table)));
}
Expand Down