Skip to content

Commit

Permalink
This PR adds support for pivot tables for belongsToMany relationships. (
Browse files Browse the repository at this point in the history
#64)

* Add support for including pivot table for belongsToMany relationships

* Fix the relation key names for the lowest supported versions

* Sort the models to get the same results for laravel 5.6 as for the newer versions

* Make the relations backward compatible with < laravel 5.8

* Catch the reflection exception

* Scrunitizer suggested fixes
  • Loading branch information
daniel-werner authored and jafar690 committed Oct 14, 2019
1 parent d027d5e commit afbe30a
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 23 deletions.
7 changes: 7 additions & 0 deletions src/Edge.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

use phpDocumentor\GraphViz\Node;

/**
* Class Edge
* @package BeyondCode\ErdGenerator
* @method void setLabel(string $name)
* @method void setXLabel(string $name)
*
*/
class Edge extends \phpDocumentor\GraphViz\Edge
{
protected $fromPort = null;
Expand Down
131 changes: 111 additions & 20 deletions src/GraphBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

namespace BeyondCode\ErdGenerator;

use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use phpDocumentor\GraphViz\Graph;
use Illuminate\Support\Collection;
use phpDocumentor\GraphViz\Node;
use \Illuminate\Database\Eloquent\Model as EloquentModel;

class GraphBuilder
{
Expand All @@ -28,10 +30,9 @@ public function buildGraph(Collection $models) : Graph
return $this->graph;
}

protected function getTableColumnsFromModel(string $model)
protected function getTableColumnsFromModel(EloquentModel $model)
{
try {
$model = app($model);

$table = $model->getConnection()->getTablePrefix() . $model->getTable();
$schema = $model->getConnection()->getDoctrineSchemaManager($table);
Expand All @@ -51,7 +52,7 @@ protected function getTableColumnsFromModel(string $model)
return [];
}

protected function getModelLabel(string $model, string $label)
protected function getModelLabel(EloquentModel $model, string $label)
{

$table = '<<table width="100%" height="100%" border="0" margin="0" cellborder="1" cellspacing="0" cellpadding="10">' . PHP_EOL;
Expand All @@ -77,7 +78,8 @@ protected function addModelsToGraph(Collection $models)
{
// Add models to graph
$models->map(function (Model $model) {
$this->addNodeToGraph($model->getModel(), $model->getNodeName(), $model->getLabel());
$eloquentModel = app($model->getModel());
$this->addNodeToGraph($eloquentModel, $model->getNodeName(), $model->getLabel());
});

// Create relations
Expand All @@ -86,10 +88,10 @@ protected function addModelsToGraph(Collection $models)
});
}

protected function addNodeToGraph(string $className, string $nodeName, string $label)
protected function addNodeToGraph(EloquentModel $eloquentModel, string $nodeName, string $label)
{
$node = Node::create($nodeName);
$node->setLabel($this->getModelLabel($className, $label));
$node->setLabel($this->getModelLabel($eloquentModel, $label));

foreach (config('erd-generator.node') as $key => $value) {
$node->{"set${key}"}($value);
Expand All @@ -103,26 +105,115 @@ protected function addRelationToGraph(Model $model)

$modelNode = $this->graph->findNode($model->getNodeName());

/** @var ModelRelation $relation */
foreach ($model->getRelations() as $relation) {
$relatedModelNode = $this->graph->findNode($relation->getModelNodeName());

if ($relatedModelNode !== null) {
$edge = Edge::create($modelNode, $relatedModelNode);
$edge->setFromPort($relation->getLocalKey());
$edge->setToPort($relation->getForeignKey());
$edge->setLabel(' ');
$edge->setXLabel($relation->getType(). PHP_EOL . $relation->getName());

foreach (config('erd-generator.edge') as $key => $value) {
$edge->{"set${key}"}($value);
}
$this->connectByRelation($model, $relation, $modelNode, $relatedModelNode);
}
}
}

foreach (config('erd-generator.relations.' . $relation->getType(), []) as $key => $value) {
$edge->{"set${key}"}($value);
}
/**
* @param Node $modelNode
* @param Node $relatedModelNode
* @param ModelRelation $relation
*/
protected function connectNodes(Node $modelNode, Node $relatedModelNode, ModelRelation $relation): void
{
$edge = Edge::create($modelNode, $relatedModelNode);
$edge->setFromPort($relation->getLocalKey());
$edge->setToPort($relation->getForeignKey());
$edge->setLabel(' ');
$edge->setXLabel($relation->getType() . PHP_EOL . $relation->getName());

foreach (config('erd-generator.edge') as $key => $value) {
$edge->{"set${key}"}($value);
}

$this->graph->link($edge);
}
foreach (config('erd-generator.relations.' . $relation->getType(), []) as $key => $value) {
$edge->{"set${key}"}($value);
}

$this->graph->link($edge);
}

/**
* @param Model $model
* @param ModelRelation $relation
* @param Node $modelNode
* @param Node $relatedModelNode
* @return void
*/
protected function connectBelongsToMany(
Model $model,
ModelRelation $relation,
Node $modelNode,
Node $relatedModelNode
): void {
$relationName = $relation->getName();
$eloquentModel = app($model->getModel());

/** @var BelongsToMany $eloquentRelation */
$eloquentRelation = $eloquentModel->$relationName();

if (!$eloquentRelation instanceof BelongsToMany) {
return;
}

$pivotClass = $eloquentRelation->getPivotClass();

try {
/** @var EloquentModel $relationModel */
$pivotModel = app($pivotClass);
$pivotModel->setTable($eloquentRelation->getTable());
$label = (new \ReflectionClass($pivotClass))->getShortName();
$pivotTable = $eloquentRelation->getTable();
$this->addNodeToGraph($pivotModel, $pivotTable, $label);

$pivotModelNode = $this->graph->findNode($pivotTable);

$relation = new ModelRelation(
$relationName,
'BelongsToMany',
$model->getModel(),
$eloquentRelation->getParent()->getKeyName(),
$eloquentRelation->getForeignPivotKeyName()
);

$this->connectNodes($modelNode, $pivotModelNode, $relation);

$relation = new ModelRelation(
$relationName,
'BelongsToMany',
$model->getModel(),
$eloquentRelation->getRelatedPivotKeyName(),
$eloquentRelation->getRelated()->getKeyName()
);

$this->connectNodes($pivotModelNode, $relatedModelNode, $relation);
} catch (\ReflectionException $e){}
}

/**
* @param Model $model
* @param ModelRelation $relation
* @param Node $modelNode
* @param Node $relatedModelNode
*/
protected function connectByRelation(
Model $model,
ModelRelation $relation,
Node $modelNode,
Node $relatedModelNode
): void {

if ($relation->getType() === 'BelongsToMany') {
$this->connectBelongsToMany($model, $relation, $modelNode, $relatedModelNode);
return;
}

$this->connectNodes($modelNode, $relatedModelNode, $relation);
}
}
6 changes: 6 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ protected function setUpDatabase()
$table->string('body');
$table->morphs('commentable');
});

$this->app['db']->connection()->getSchemaBuilder()->create('comment_user', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('comment_id');
$table->unsignedInteger('user_id');
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,15 @@
arrowhead="tee"
arrowtail="none"
]
beyondcodeerdgeneratortestsmodelsuser -> beyondcodeerdgeneratortestsmodelscomment [
beyondcodeerdgeneratortestsmodelsuser:id -> comment_user:user_id [
label=" "
xlabel="BelongsToMany
comments"
color="#003049"
penwidth="1.8"
fontname="Helvetica Neue"
]
comment_user:comment_id -> beyondcodeerdgeneratortestsmodelscomment:id [
label=" "
xlabel="BelongsToMany
comments"
Expand Down Expand Up @@ -124,5 +132,13 @@
shape="rectangle"
fontname="Helvetica Neue"
]
"comment_user" [
label=<<table width="100%" height="100%" border="0" margin="0" cellborder="1" cellspacing="0" cellpadding="10">
<tr width="100%"><td width="100%" bgcolor="#d3d3d3"><font color="#333333">Pivot</font></td></tr>
</table>>
margin="0"
shape="rectangle"
fontname="Helvetica Neue"
]
}
';
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,15 @@
arrowhead="tee"
arrowtail="none"
]
beyondcodeerdgeneratortestsmodelsuser -> beyondcodeerdgeneratortestsmodelscomment [
beyondcodeerdgeneratortestsmodelsuser:id -> comment_user:user_id [
label=" "
xlabel="BelongsToMany
comments"
color="#003049"
penwidth="1.8"
fontname="Helvetica Neue"
]
comment_user:comment_id -> beyondcodeerdgeneratortestsmodelscomment:id [
label=" "
xlabel="BelongsToMany
comments"
Expand Down Expand Up @@ -145,5 +153,16 @@
shape="rectangle"
fontname="Helvetica Neue"
]
"comment_user" [
label=<<table width="100%" height="100%" border="0" margin="0" cellborder="1" cellspacing="0" cellpadding="10">
<tr width="100%"><td width="100%" bgcolor="#d3d3d3"><font color="#333333">Pivot</font></td></tr>
<tr width="100%"><td port="id" align="left" width="100%" bgcolor="#ffffff"><font color="#333333" >id</font></td></tr>
<tr width="100%"><td port="comment_id" align="left" width="100%" bgcolor="#ffffff"><font color="#333333" >comment_id</font></td></tr>
<tr width="100%"><td port="user_id" align="left" width="100%" bgcolor="#ffffff"><font color="#333333" >user_id</font></td></tr>
</table>>
margin="0"
shape="rectangle"
fontname="Helvetica Neue"
]
}
';
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,15 @@
arrowhead="tee"
arrowtail="none"
]
beyondcodeerdgeneratortestsmodelsuser -> beyondcodeerdgeneratortestsmodelscomment [
beyondcodeerdgeneratortestsmodelsuser:id -> comment_user:user_id [
label=" "
xlabel="BelongsToMany
comments"
color="#003049"
penwidth="1.8"
fontname="Helvetica Neue"
]
comment_user:comment_id -> beyondcodeerdgeneratortestsmodelscomment:id [
label=" "
xlabel="BelongsToMany
comments"
Expand Down Expand Up @@ -145,5 +153,16 @@
shape="rectangle"
fontname="Helvetica Neue"
]
"comment_user" [
label=<<table width="100%" height="100%" border="0" margin="0" cellborder="1" cellspacing="0" cellpadding="10">
<tr width="100%"><td width="100%" bgcolor="#d3d3d3"><font color="#333333">Pivot</font></td></tr>
<tr width="100%"><td port="id" align="left" width="100%" bgcolor="#ffffff"><font color="#333333" >id (integer)</font></td></tr>
<tr width="100%"><td port="comment_id" align="left" width="100%" bgcolor="#ffffff"><font color="#333333" >comment_id (integer)</font></td></tr>
<tr width="100%"><td port="user_id" align="left" width="100%" bgcolor="#ffffff"><font color="#333333" >user_id (integer)</font></td></tr>
</table>>
margin="0"
shape="rectangle"
fontname="Helvetica Neue"
]
}
';

0 comments on commit afbe30a

Please sign in to comment.