Skip to content

Commit afbe30a

Browse files
daniel-wernerjafar690
authored andcommitted
This PR adds support for pivot tables for belongsToMany relationships. (#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
1 parent d027d5e commit afbe30a

6 files changed

+181
-23
lines changed

src/Edge.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44

55
use phpDocumentor\GraphViz\Node;
66

7+
/**
8+
* Class Edge
9+
* @package BeyondCode\ErdGenerator
10+
* @method void setLabel(string $name)
11+
* @method void setXLabel(string $name)
12+
*
13+
*/
714
class Edge extends \phpDocumentor\GraphViz\Edge
815
{
916
protected $fromPort = null;

src/GraphBuilder.php

Lines changed: 111 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
namespace BeyondCode\ErdGenerator;
44

5+
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
56
use phpDocumentor\GraphViz\Graph;
67
use Illuminate\Support\Collection;
78
use phpDocumentor\GraphViz\Node;
9+
use \Illuminate\Database\Eloquent\Model as EloquentModel;
810

911
class GraphBuilder
1012
{
@@ -28,10 +30,9 @@ public function buildGraph(Collection $models) : Graph
2830
return $this->graph;
2931
}
3032

31-
protected function getTableColumnsFromModel(string $model)
33+
protected function getTableColumnsFromModel(EloquentModel $model)
3234
{
3335
try {
34-
$model = app($model);
3536

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

54-
protected function getModelLabel(string $model, string $label)
55+
protected function getModelLabel(EloquentModel $model, string $label)
5556
{
5657

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

8385
// Create relations
@@ -86,10 +88,10 @@ protected function addModelsToGraph(Collection $models)
8688
});
8789
}
8890

89-
protected function addNodeToGraph(string $className, string $nodeName, string $label)
91+
protected function addNodeToGraph(EloquentModel $eloquentModel, string $nodeName, string $label)
9092
{
9193
$node = Node::create($nodeName);
92-
$node->setLabel($this->getModelLabel($className, $label));
94+
$node->setLabel($this->getModelLabel($eloquentModel, $label));
9395

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

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

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

109112
if ($relatedModelNode !== null) {
110-
$edge = Edge::create($modelNode, $relatedModelNode);
111-
$edge->setFromPort($relation->getLocalKey());
112-
$edge->setToPort($relation->getForeignKey());
113-
$edge->setLabel(' ');
114-
$edge->setXLabel($relation->getType(). PHP_EOL . $relation->getName());
115-
116-
foreach (config('erd-generator.edge') as $key => $value) {
117-
$edge->{"set${key}"}($value);
118-
}
113+
$this->connectByRelation($model, $relation, $modelNode, $relatedModelNode);
114+
}
115+
}
116+
}
119117

120-
foreach (config('erd-generator.relations.' . $relation->getType(), []) as $key => $value) {
121-
$edge->{"set${key}"}($value);
122-
}
118+
/**
119+
* @param Node $modelNode
120+
* @param Node $relatedModelNode
121+
* @param ModelRelation $relation
122+
*/
123+
protected function connectNodes(Node $modelNode, Node $relatedModelNode, ModelRelation $relation): void
124+
{
125+
$edge = Edge::create($modelNode, $relatedModelNode);
126+
$edge->setFromPort($relation->getLocalKey());
127+
$edge->setToPort($relation->getForeignKey());
128+
$edge->setLabel(' ');
129+
$edge->setXLabel($relation->getType() . PHP_EOL . $relation->getName());
130+
131+
foreach (config('erd-generator.edge') as $key => $value) {
132+
$edge->{"set${key}"}($value);
133+
}
123134

124-
$this->graph->link($edge);
125-
}
135+
foreach (config('erd-generator.relations.' . $relation->getType(), []) as $key => $value) {
136+
$edge->{"set${key}"}($value);
126137
}
138+
139+
$this->graph->link($edge);
140+
}
141+
142+
/**
143+
* @param Model $model
144+
* @param ModelRelation $relation
145+
* @param Node $modelNode
146+
* @param Node $relatedModelNode
147+
* @return void
148+
*/
149+
protected function connectBelongsToMany(
150+
Model $model,
151+
ModelRelation $relation,
152+
Node $modelNode,
153+
Node $relatedModelNode
154+
): void {
155+
$relationName = $relation->getName();
156+
$eloquentModel = app($model->getModel());
157+
158+
/** @var BelongsToMany $eloquentRelation */
159+
$eloquentRelation = $eloquentModel->$relationName();
160+
161+
if (!$eloquentRelation instanceof BelongsToMany) {
162+
return;
163+
}
164+
165+
$pivotClass = $eloquentRelation->getPivotClass();
166+
167+
try {
168+
/** @var EloquentModel $relationModel */
169+
$pivotModel = app($pivotClass);
170+
$pivotModel->setTable($eloquentRelation->getTable());
171+
$label = (new \ReflectionClass($pivotClass))->getShortName();
172+
$pivotTable = $eloquentRelation->getTable();
173+
$this->addNodeToGraph($pivotModel, $pivotTable, $label);
174+
175+
$pivotModelNode = $this->graph->findNode($pivotTable);
176+
177+
$relation = new ModelRelation(
178+
$relationName,
179+
'BelongsToMany',
180+
$model->getModel(),
181+
$eloquentRelation->getParent()->getKeyName(),
182+
$eloquentRelation->getForeignPivotKeyName()
183+
);
184+
185+
$this->connectNodes($modelNode, $pivotModelNode, $relation);
186+
187+
$relation = new ModelRelation(
188+
$relationName,
189+
'BelongsToMany',
190+
$model->getModel(),
191+
$eloquentRelation->getRelatedPivotKeyName(),
192+
$eloquentRelation->getRelated()->getKeyName()
193+
);
194+
195+
$this->connectNodes($pivotModelNode, $relatedModelNode, $relation);
196+
} catch (\ReflectionException $e){}
197+
}
198+
199+
/**
200+
* @param Model $model
201+
* @param ModelRelation $relation
202+
* @param Node $modelNode
203+
* @param Node $relatedModelNode
204+
*/
205+
protected function connectByRelation(
206+
Model $model,
207+
ModelRelation $relation,
208+
Node $modelNode,
209+
Node $relatedModelNode
210+
): void {
211+
212+
if ($relation->getType() === 'BelongsToMany') {
213+
$this->connectBelongsToMany($model, $relation, $modelNode, $relatedModelNode);
214+
return;
215+
}
216+
217+
$this->connectNodes($modelNode, $relatedModelNode, $relation);
127218
}
128219
}

tests/TestCase.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ protected function setUpDatabase()
6060
$table->string('body');
6161
$table->morphs('commentable');
6262
});
63+
64+
$this->app['db']->connection()->getSchemaBuilder()->create('comment_user', function (Blueprint $table) {
65+
$table->increments('id');
66+
$table->unsignedInteger('comment_id');
67+
$table->unsignedInteger('user_id');
68+
});
6369
}
6470

6571
}

tests/__snapshots__/GenerationTest__it_generated_graphviz_for_test_models__1.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,15 @@
8484
arrowhead="tee"
8585
arrowtail="none"
8686
]
87-
beyondcodeerdgeneratortestsmodelsuser -> beyondcodeerdgeneratortestsmodelscomment [
87+
beyondcodeerdgeneratortestsmodelsuser:id -> comment_user:user_id [
88+
label=" "
89+
xlabel="BelongsToMany
90+
comments"
91+
color="#003049"
92+
penwidth="1.8"
93+
fontname="Helvetica Neue"
94+
]
95+
comment_user:comment_id -> beyondcodeerdgeneratortestsmodelscomment:id [
8896
label=" "
8997
xlabel="BelongsToMany
9098
comments"
@@ -124,5 +132,13 @@
124132
shape="rectangle"
125133
fontname="Helvetica Neue"
126134
]
135+
"comment_user" [
136+
label=<<table width="100%" height="100%" border="0" margin="0" cellborder="1" cellspacing="0" cellpadding="10">
137+
<tr width="100%"><td width="100%" bgcolor="#d3d3d3"><font color="#333333">Pivot</font></td></tr>
138+
</table>>
139+
margin="0"
140+
shape="rectangle"
141+
fontname="Helvetica Neue"
142+
]
127143
}
128144
';

tests/__snapshots__/GenerationTest__it_generated_graphviz_for_test_models_with_db_columns__1.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,15 @@
8484
arrowhead="tee"
8585
arrowtail="none"
8686
]
87-
beyondcodeerdgeneratortestsmodelsuser -> beyondcodeerdgeneratortestsmodelscomment [
87+
beyondcodeerdgeneratortestsmodelsuser:id -> comment_user:user_id [
88+
label=" "
89+
xlabel="BelongsToMany
90+
comments"
91+
color="#003049"
92+
penwidth="1.8"
93+
fontname="Helvetica Neue"
94+
]
95+
comment_user:comment_id -> beyondcodeerdgeneratortestsmodelscomment:id [
8896
label=" "
8997
xlabel="BelongsToMany
9098
comments"
@@ -145,5 +153,16 @@
145153
shape="rectangle"
146154
fontname="Helvetica Neue"
147155
]
156+
"comment_user" [
157+
label=<<table width="100%" height="100%" border="0" margin="0" cellborder="1" cellspacing="0" cellpadding="10">
158+
<tr width="100%"><td width="100%" bgcolor="#d3d3d3"><font color="#333333">Pivot</font></td></tr>
159+
<tr width="100%"><td port="id" align="left" width="100%" bgcolor="#ffffff"><font color="#333333" >id</font></td></tr>
160+
<tr width="100%"><td port="comment_id" align="left" width="100%" bgcolor="#ffffff"><font color="#333333" >comment_id</font></td></tr>
161+
<tr width="100%"><td port="user_id" align="left" width="100%" bgcolor="#ffffff"><font color="#333333" >user_id</font></td></tr>
162+
</table>>
163+
margin="0"
164+
shape="rectangle"
165+
fontname="Helvetica Neue"
166+
]
148167
}
149168
';

tests/__snapshots__/GenerationTest__it_generated_graphviz_for_test_models_with_db_columns_and_types__1.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,15 @@
8484
arrowhead="tee"
8585
arrowtail="none"
8686
]
87-
beyondcodeerdgeneratortestsmodelsuser -> beyondcodeerdgeneratortestsmodelscomment [
87+
beyondcodeerdgeneratortestsmodelsuser:id -> comment_user:user_id [
88+
label=" "
89+
xlabel="BelongsToMany
90+
comments"
91+
color="#003049"
92+
penwidth="1.8"
93+
fontname="Helvetica Neue"
94+
]
95+
comment_user:comment_id -> beyondcodeerdgeneratortestsmodelscomment:id [
8896
label=" "
8997
xlabel="BelongsToMany
9098
comments"
@@ -145,5 +153,16 @@
145153
shape="rectangle"
146154
fontname="Helvetica Neue"
147155
]
156+
"comment_user" [
157+
label=<<table width="100%" height="100%" border="0" margin="0" cellborder="1" cellspacing="0" cellpadding="10">
158+
<tr width="100%"><td width="100%" bgcolor="#d3d3d3"><font color="#333333">Pivot</font></td></tr>
159+
<tr width="100%"><td port="id" align="left" width="100%" bgcolor="#ffffff"><font color="#333333" >id (integer)</font></td></tr>
160+
<tr width="100%"><td port="comment_id" align="left" width="100%" bgcolor="#ffffff"><font color="#333333" >comment_id (integer)</font></td></tr>
161+
<tr width="100%"><td port="user_id" align="left" width="100%" bgcolor="#ffffff"><font color="#333333" >user_id (integer)</font></td></tr>
162+
</table>>
163+
margin="0"
164+
shape="rectangle"
165+
fontname="Helvetica Neue"
166+
]
148167
}
149168
';

0 commit comments

Comments
 (0)