Skip to content

Commit ae12e10

Browse files
committed
Refactor model relationships into property
1 parent b47acad commit ae12e10

File tree

9 files changed

+205
-80
lines changed

9 files changed

+205
-80
lines changed

src/Generators/FactoryGenerator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ protected function buildDefinition(Model $model)
6363

6464
/** @var \Blueprint\Models\Column $column */
6565
foreach ($model->columns() as $column) {
66-
if ($column->name() === 'id' || $column->name() === 'relationships') {
66+
if ($column->name() === 'id') {
6767
continue;
6868
}
6969

src/Generators/MigrationGenerator.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ protected function buildDefinition(Model $model)
6161
$dataType = 'bigIncrements';
6262
} elseif ($column->dataType() === 'id') {
6363
$dataType = 'unsignedBigInteger';
64-
} elseif ($column->name() === 'relationships') {
65-
continue;
6664
}
6765

6866
$definition .= self::INDENT . '$table->' . $dataType . "('{$column->name()}'";

src/Generators/ModelGenerator.php

Lines changed: 13 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ protected function populateStub(string $stub, Model $model)
4646

4747
$body = $this->buildProperties($model);
4848
$body .= PHP_EOL . PHP_EOL;
49-
$body .= $this->buildBelongsTo($model);
5049
$body .= $this->buildRelationships($model);
5150

5251
$stub = str_replace('// ...', trim($body), $stub);
@@ -111,62 +110,27 @@ private function buildProperties(Model $model)
111110
return trim($properties);
112111
}
113112

114-
private function buildBelongsTo(Model $model)
115-
{
116-
$columns = array_filter($model->columns(), function (Column $column) {
117-
return $column->name() !== 'id' && $column->dataType() === 'id';
118-
});
119-
120-
if (empty($columns)) {
121-
return '';
122-
}
123-
124-
$methods = '';
125-
$template = $this->files->stub('model/method.stub');
126-
127-
/** @var Column $column */
128-
foreach ($columns as $column) {
129-
$name = Str::beforeLast($column->name(), '_id');
130-
$class = Str::studly($column->attributes()[0] ?? $name);
131-
$relationship = sprintf("\$this->belongsTo(%s::class)", '\\' . $model->fullyQualifiedNamespace() . '\\' . $class);
132-
133-
$method = str_replace('DummyName', Str::camel($name), $template);
134-
$method = str_replace('null', $relationship, $method);
135-
136-
$methods .= PHP_EOL . $method;
137-
}
138-
139-
return $methods;
140-
}
141-
142113
private function buildRelationships(Model $model)
143114
{
144-
$columns = array_filter($model->columns(), function (Column $column) {
145-
return $column->name() === 'relationships';
146-
});
147-
148-
if (empty($columns)) {
149-
return '';
150-
}
151-
152115
$methods = '';
153116
$template = $this->files->stub('model/method.stub');
154117

155-
/** @var Column $column */
156-
foreach ($columns as $column) {
157-
foreach ($column->attributes() as $methodName => $modelName) {
158-
if ('belongsTo' === $methodName) {
159-
throw new \Exception('The belongsTo relationship for the '.$modelName.' model on the '.$model->name().' model should be defined using the '.$modelName.'_id: id syntax');
118+
foreach ($model->relationships() as $type => $references) {
119+
foreach ($references as $reference) {
120+
if (Str::contains($reference, ':')) {
121+
[$class, $name] = explode(':', $reference);
122+
} else {
123+
$name = $reference;
124+
$class = null;
160125
}
161-
$class = Str::studly($column->attributes()[0] ?? $modelName);
162-
$relationship = sprintf("\$this->%s(%s::class)",
163-
$methodName,
164-
'\\' . $model->fullyQualifiedNamespace() . '\\' . $class);
165126

166-
$modelNameForMethod = Str::contains($methodName, 'Many') ? Str::plural($modelName) : $modelName;
167-
$method = str_replace('DummyName', Str::camel($modelNameForMethod), $template);
168-
$method = str_replace('null', $relationship, $method);
127+
$name = Str::beforeLast($name, '_id');
128+
$class = Str::studly($class ?? $name);
129+
$relationship = sprintf("\$this->%s(%s::class)", $type, '\\' . $model->fullyQualifiedNamespace() . '\\' . $class);
169130

131+
$method_name = $type === 'hasMany' ? Str::plural($name) : $name;
132+
$method = str_replace('DummyName', Str::camel($method_name), $template);
133+
$method = str_replace('null', $relationship, $method);
170134
$methods .= PHP_EOL . $method;
171135
}
172136
}
@@ -189,7 +153,6 @@ private function fillableColumns(array $columns)
189153
'deleted_at',
190154
'created_at',
191155
'updated_at',
192-
'relationships',
193156
]);
194157
}
195158

src/Lexers/ModelLexer.php

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88

99
class ModelLexer implements Lexer
1010
{
11+
private static $relationships = [
12+
'belongsto' => 'belongsTo',
13+
'hasone' => 'hasOne',
14+
'hasmany' => 'hasMany',
15+
];
16+
1117
private static $dataTypes = [
1218
'bigincrements' => 'bigIncrements',
1319
'biginteger' => 'bigInteger',
@@ -122,6 +128,18 @@ private function buildModel(string $name, array $columns)
122128
unset($columns['softdeletestz']);
123129
}
124130

131+
if (isset($columns['relationships'])) {
132+
if (is_array($columns['relationships'])) {
133+
foreach ($columns['relationships'] as $type => $relationships) {
134+
foreach (explode(',', $relationships) as $reference) {
135+
$model->addRelationship(self::$relationships[strtolower($type)], trim($reference));
136+
}
137+
}
138+
}
139+
140+
unset($columns['relationships']);
141+
}
142+
125143
if (!isset($columns['id'])) {
126144
$column = $this->buildColumn('id', 'id');
127145
$model->addColumn($column);
@@ -130,44 +148,48 @@ private function buildModel(string $name, array $columns)
130148
foreach ($columns as $name => $definition) {
131149
$column = $this->buildColumn($name, $definition);
132150
$model->addColumn($column);
151+
152+
if ($column->name() !== 'id' && $column->dataType() === 'id') {
153+
if ($column->attributes()) {
154+
$model->addRelationship('belongsTo', $column->attributes()[0] . ':' . $column->name());
155+
} else {
156+
$model->addRelationship('belongsTo', $column->name());
157+
}
158+
}
133159
}
134160

135161
return $model;
136162
}
137163

138-
private function buildColumn(string $name, $definition)
164+
private function buildColumn(string $name, string $definition)
139165
{
140166
$data_type = 'string';
141167
$modifiers = [];
142168

143-
if ($name === 'relationships' && is_array($definition)) {
144-
$attributes = $definition;
145-
} else {
146-
$tokens = explode(' ', $definition);
147-
foreach ($tokens as $token) {
148-
$parts = explode(':', $token);
149-
$value = $parts[0];
150-
151-
if ($value === 'id') {
152-
$data_type = 'id';
153-
if (isset($parts[1])) {
154-
$attributes = [$parts[1]];
155-
}
156-
} elseif (isset(self::$dataTypes[strtolower($value)])) {
157-
$attributes = $parts[1] ?? null;
158-
$data_type = self::$dataTypes[strtolower($value)];
159-
if (!empty($attributes)) {
160-
$attributes = explode(',', $attributes);
161-
}
169+
$tokens = explode(' ', $definition);
170+
foreach ($tokens as $token) {
171+
$parts = explode(':', $token);
172+
$value = $parts[0];
173+
174+
if ($value === 'id') {
175+
$data_type = 'id';
176+
if (isset($parts[1])) {
177+
$attributes = [$parts[1]];
178+
}
179+
} elseif (isset(self::$dataTypes[strtolower($value)])) {
180+
$attributes = $parts[1] ?? null;
181+
$data_type = self::$dataTypes[strtolower($value)];
182+
if (!empty($attributes)) {
183+
$attributes = explode(',', $attributes);
162184
}
185+
}
163186

164-
if (isset(self::$modifiers[strtolower($value)])) {
165-
$modifierAttributes = $parts[1] ?? null;
166-
if (empty($modifierAttributes)) {
167-
$modifiers[] = self::$modifiers[strtolower($value)];
168-
} else {
169-
$modifiers[] = [self::$modifiers[strtolower($value)] => $modifierAttributes];
170-
}
187+
if (isset(self::$modifiers[strtolower($value)])) {
188+
$modifierAttributes = $parts[1] ?? null;
189+
if (empty($modifierAttributes)) {
190+
$modifiers[] = self::$modifiers[strtolower($value)];
191+
} else {
192+
$modifiers[] = [self::$modifiers[strtolower($value)] => $modifierAttributes];
171193
}
172194
}
173195
}

src/Models/Model.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class Model
1111
private $timestamps = 'timestamps';
1212
private $softDeletes = false;
1313
private $columns = [];
14+
private $relationships = [];
1415

1516
/**
1617
* @param $name
@@ -65,6 +66,11 @@ public function columns(): array
6566
return $this->columns;
6667
}
6768

69+
public function relationships(): array
70+
{
71+
return $this->relationships;
72+
}
73+
6874
public function primaryKey()
6975
{
7076
return 'id';
@@ -119,4 +125,15 @@ public function column(string $name)
119125
{
120126
return $this->columns[$name];
121127
}
128+
129+
public function addRelationship(string $type, string $reference)
130+
{
131+
if (!isset($this->relationships[$type])) {
132+
$this->relationships[$type] = [];
133+
}
134+
135+
$this->relationships[$type][] = $reference;
136+
}
137+
138+
122139
}

tests/Feature/Generator/ModelGeneratorTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,36 @@ public function output_generates_models($definition, $path, $model)
7878
$this->assertEquals(['created' => [$path]], $this->subject->output($tree));
7979
}
8080

81+
/**
82+
* @test
83+
*/
84+
public function output_generates_relationships()
85+
{
86+
$this->files->expects('stub')
87+
->with('model/class.stub')
88+
->andReturn(file_get_contents('stubs/model/class.stub'));
89+
90+
$this->files->expects('stub')
91+
->with('model/fillable.stub')
92+
->andReturn(file_get_contents('stubs/model/fillable.stub'));
93+
94+
$this->files->expects('stub')
95+
->with('model/casts.stub')
96+
->andReturn(file_get_contents('stubs/model/casts.stub'));
97+
98+
$this->files->expects('stub')
99+
->with('model/method.stub')
100+
->andReturn(file_get_contents('stubs/model/method.stub'));
101+
102+
$this->files->expects('put')
103+
->with('app/Subscription.php', $this->fixture('models/model-relationships.php'));
104+
105+
$tokens = $this->blueprint->parse($this->fixture('definitions/model-relationships.bp'));
106+
$tree = $this->blueprint->analyze($tokens);
107+
108+
$this->assertEquals(['created' => ['app/Subscription.php']], $this->subject->output($tree));
109+
}
110+
81111
/**
82112
* @test
83113
*/

tests/Feature/Lexers/ModelLexerTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,47 @@ public function it_enables_soft_deletes()
341341
$this->assertEquals([], $columns['id']->modifiers());
342342
}
343343

344+
/**
345+
* @test
346+
*/
347+
public function it_stores_relationships()
348+
{
349+
$tokens = [
350+
'models' => [
351+
'Subscription' => [
352+
'different_id' => 'id:user',
353+
'title' => 'string',
354+
'price' => 'float',
355+
'relationships' => [
356+
'hasmany' => 'Order',
357+
'hasOne' => 'Duration, Transaction:tid',
358+
],
359+
]
360+
],
361+
];
362+
363+
$actual = $this->subject->analyze($tokens);
364+
365+
$this->assertIsArray($actual['models']);
366+
$this->assertCount(1, $actual['models']);
367+
368+
$model = $actual['models']['Subscription'];
369+
$this->assertEquals('Subscription', $model->name());
370+
371+
$columns = $model->columns();
372+
$this->assertCount(4, $columns);
373+
$this->assertArrayHasKey('id', $columns);
374+
$this->assertArrayHasKey('different_id', $columns);
375+
$this->assertArrayHasKey('title', $columns);
376+
$this->assertArrayHasKey('price', $columns);
377+
378+
$relationships = $model->relationships();
379+
$this->assertCount(3, $relationships);
380+
$this->assertEquals(['user:different_id'], $relationships['belongsTo']);
381+
$this->assertEquals(['Order'], $relationships['hasMany']);
382+
$this->assertEquals(['Duration', 'Transaction:tid'], $relationships['hasOne']);
383+
}
384+
344385
public function dataTypeAttributesDataProvider()
345386
{
346387
return [
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
models:
2+
Subscription:
3+
user_id: id
4+
relationships:
5+
hasMany: Order
6+
hasOne: Duration, Transaction
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace App;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
7+
class Subscription extends Model
8+
{
9+
/**
10+
* The attributes that are mass assignable.
11+
*
12+
* @var array
13+
*/
14+
protected $fillable = [
15+
'user_id',
16+
];
17+
18+
/**
19+
* The attributes that should be cast to native types.
20+
*
21+
* @var array
22+
*/
23+
protected $casts = [
24+
'id' => 'integer',
25+
'user_id' => 'integer',
26+
];
27+
28+
29+
public function orders()
30+
{
31+
return $this->hasMany(\App\Order::class);
32+
}
33+
34+
public function duration()
35+
{
36+
return $this->hasOne(\App\Duration::class);
37+
}
38+
39+
public function transaction()
40+
{
41+
return $this->hasOne(\App\Transaction::class);
42+
}
43+
44+
public function user()
45+
{
46+
return $this->belongsTo(\App\User::class);
47+
}
48+
}

0 commit comments

Comments
 (0)