Skip to content

Commit 89b290f

Browse files
authored
Adds support for FQCN to model relationships (#471)
1 parent 526b214 commit 89b290f

File tree

4 files changed

+125
-10
lines changed

4 files changed

+125
-10
lines changed

src/Generators/ModelGenerator.php

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -190,51 +190,64 @@ protected function buildRelationships(Model $model)
190190

191191
foreach ($model->relationships() as $type => $references) {
192192
foreach ($references as $reference) {
193+
// if reference starts with \ , we assume developer is using a fully namespaced model
194+
$is_model_fqn = Str::startsWith($reference, '\\');
195+
193196
$custom_template = $template;
194197
$key = null;
195198
$class = null;
196199

197200
$column_name = $reference;
198-
$method_name = Str::beforeLast($reference, '_id');
201+
$method_name = $is_model_fqn ? Str::afterLast($reference, '\\') : Str::beforeLast($reference, '_id');
199202

200203
if (Str::contains($reference, ':')) {
201204
[$foreign_reference, $column_name] = explode(':', $reference);
202-
$method_name = Str::beforeLast($column_name, '_id');
205+
206+
$method_name = $is_model_fqn ? Str::afterLast($foreign_reference, '\\') : Str::beforeLast($column_name, '_id');
203207

204208
if (Str::contains($foreign_reference, '.')) {
205209
[$class, $key] = explode('.', $foreign_reference);
206210

207211
if ($key === 'id') {
208212
$key = null;
209213
} else {
210-
$method_name = Str::lower($class);
214+
$method_name = $is_model_fqn ? Str::lower(Str::afterLast($class, '\\')) : Str::lower($class);
211215
}
212216
} else {
213217
$class = $foreign_reference;
214218
}
215219
}
216220

217-
$class_name = Str::studly($class ?? $method_name);
218-
$fqcn = $this->fullyQualifyModelReference($class_name) ?? $model->fullyQualifiedNamespace() . '\\' . $class_name;
221+
// if full model namespace is proviced we will not try to infer it,
222+
// we use the namespace as developer gives us
223+
if ($is_model_fqn) {
224+
$fqcn = $class ?? $column_name;
225+
$class_name = Str::afterLast($fqcn, '\\');
226+
}else{
227+
$class_name = Str::studly($class ?? $method_name);
228+
$fqcn = $this->fullyQualifyModelReference($class_name) ?? $model->fullyQualifiedNamespace() . '\\' . $class_name;
229+
}
230+
231+
$fqcn = Str::startsWith($fqcn, '\\') ? $fqcn : '\\'.$fqcn;
219232

220233
if ($type === 'morphTo') {
221234
$relationship = sprintf('$this->%s()', $type);
222235
} elseif ($type === 'morphMany' || $type === 'morphOne') {
223236
$relation = Str::lower(Str::singular($column_name)) . 'able';
224-
$relationship = sprintf('$this->%s(%s::class, \'%s\')', $type, '\\' . $fqcn, $relation);
237+
$relationship = sprintf('$this->%s(%s::class, \'%s\')', $type, $fqcn, $relation);
225238
} elseif (!is_null($key)) {
226-
$relationship = sprintf('$this->%s(%s::class, \'%s\', \'%s\')', $type, '\\' . $fqcn, $column_name, $key);
239+
$relationship = sprintf('$this->%s(%s::class, \'%s\', \'%s\')', $type, $fqcn, $column_name, $key);
227240
} elseif (!is_null($class) && $type === 'belongsToMany') {
228-
$relationship = sprintf('$this->%s(%s::class, \'%s\')', $type, '\\' . $fqcn, $column_name);
241+
$relationship = sprintf('$this->%s(%s::class, \'%s\')', $type, $fqcn, $column_name);
229242
$column_name = $class;
230243
} else {
231-
$relationship = sprintf('$this->%s(%s::class)', $type, '\\' . $fqcn);
244+
$relationship = sprintf('$this->%s(%s::class)', $type, $fqcn);
232245
}
233246

234247
if ($type === 'morphTo') {
235248
$method_name = Str::lower($class_name);
236249
} elseif (in_array($type, ['hasMany', 'belongsToMany', 'morphMany'])) {
237-
$method_name = Str::plural($column_name);
250+
$method_name = Str::plural($is_model_fqn ? Str::afterLast($column_name, '\\') : $column_name);
238251
}
239252

240253
if (Blueprint::supportsReturnTypeHits()) {

tests/Feature/Generators/ModelGeneratorTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,38 @@ public function output_generates_relationships()
268268
$this->assertEquals(['created' => ['app/Subscription.php']], $this->subject->output($tree));
269269
}
270270

271+
/**
272+
* @test
273+
* @environment-setup useLaravel8
274+
*/
275+
public function output_generates_relationships_added_with_full_model_namespace()
276+
{
277+
$this->files->expects('stub')
278+
->with($this->modelStub)
279+
->andReturn($this->stub($this->modelStub));
280+
$this->files->expects('stub')
281+
->with('model.fillable.stub')
282+
->andReturn($this->stub('model.fillable.stub'));
283+
$this->files->expects('stub')
284+
->with('model.casts.stub')
285+
->andReturn($this->stub('model.casts.stub'));
286+
$this->files->expects('stub')
287+
->with('model.method.stub')
288+
->andReturn($this->stub('model.method.stub'));
289+
290+
$this->files->expects('exists')
291+
->with('app')
292+
->andReturnTrue();
293+
294+
$this->files->expects('put')
295+
->with('app/Recurrency.php', $this->fixture('models/model-relationships-with-full-namespace-laravel8.php'));
296+
297+
$tokens = $this->blueprint->parse($this->fixture('drafts/model-relationships-with-full-model-namespaces.yaml'));
298+
$tree = $this->blueprint->analyze($tokens);
299+
300+
$this->assertEquals(['created' => ['app/Recurrency.php']], $this->subject->output($tree));
301+
}
302+
271303
/**
272304
* @test
273305
* @environment-setup useLaravel8
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
models:
2+
Recurrency:
3+
user_id: id
4+
product_id: uuid
5+
relationships:
6+
belongsToMany: \Some\Package\Team
7+
hasMany: \Other\Package\Order
8+
hasOne: \Other\Package\Duration, \App\MyCustom\Folder\Transaction
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
namespace App;
4+
5+
use Illuminate\Database\Eloquent\Factories\HasFactory;
6+
use Illuminate\Database\Eloquent\Model;
7+
8+
class Recurrency extends Model
9+
{
10+
use HasFactory;
11+
12+
/**
13+
* The attributes that are mass assignable.
14+
*
15+
* @var array
16+
*/
17+
protected $fillable = [
18+
'user_id',
19+
'product_id',
20+
];
21+
22+
/**
23+
* The attributes that should be cast to native types.
24+
*
25+
* @var array
26+
*/
27+
protected $casts = [
28+
'id' => 'integer',
29+
'user_id' => 'integer',
30+
];
31+
32+
33+
public function teams()
34+
{
35+
return $this->belongsToMany(\Some\Package\Team::class);
36+
}
37+
38+
public function orders()
39+
{
40+
return $this->hasMany(\Other\Package\Order::class);
41+
}
42+
43+
public function duration()
44+
{
45+
return $this->hasOne(\Other\Package\Duration::class);
46+
}
47+
48+
public function transaction()
49+
{
50+
return $this->hasOne(\App\MyCustom\Folder\Transaction::class);
51+
}
52+
53+
public function user()
54+
{
55+
return $this->belongsTo(\App\User::class);
56+
}
57+
58+
public function product()
59+
{
60+
return $this->belongsTo(\App\Product::class);
61+
}
62+
}

0 commit comments

Comments
 (0)