Skip to content

Commit 60881b4

Browse files
authored
Fix chaining of 'nullable' for 'foreignId' in Laravel 7 (#217)
1 parent d2c021b commit 60881b4

File tree

5 files changed

+144
-6
lines changed

5 files changed

+144
-6
lines changed

src/Generators/MigrationGenerator.php

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,24 @@ protected function buildDefinition(Model $model)
131131
$foreign_modifier = $column->isForeignKey();
132132

133133
if ($this->shouldAddForeignKeyConstraint($column)) {
134-
$foreign = $this->buildForeignKey($column->name(), $foreign_modifier === 'foreign' ? null : $foreign_modifier, $column->dataType(), $column->attributes());
134+
$foreign = $this->buildForeignKey(
135+
$column->name(),
136+
$foreign_modifier === 'foreign' ? null : $foreign_modifier,
137+
$column->dataType(),
138+
$column->attributes(),
139+
$column->modifiers()
140+
);
141+
135142
if ($column->dataType() === 'id' && $this->isLaravel7orNewer()) {
136143
$column_definition = $foreign;
137144
$foreign = '';
138145
}
139146

140147
// TODO: unset the proper modifier
141148
$modifiers = collect($modifiers)->reject(function ($modifier) {
142-
return (is_array($modifier) && key($modifier) === 'foreign') || $modifier === 'foreign';
149+
return ((is_array($modifier) && key($modifier) === 'foreign')
150+
|| $modifier === 'foreign'
151+
|| ($modifier === 'nullable' && $this->isLaravel7orNewer()));
143152
});
144153
}
145154

@@ -198,7 +207,7 @@ protected function buildPivotTableDefinition(array $segments)
198207
return trim($definition);
199208
}
200209

201-
protected function buildForeignKey(string $column_name, ?string $on, string $type, array $attributes = [])
210+
protected function buildForeignKey(string $column_name, ?string $on, string $type, array $attributes = [], array $modifiers = [])
202211
{
203212
if (is_null($on)) {
204213
$table = Str::plural(Str::beforeLast($column_name, '_'));
@@ -216,14 +225,18 @@ protected function buildForeignKey(string $column_name, ?string $on, string $typ
216225
}
217226

218227
if ($this->isLaravel7orNewer() && $type === 'id') {
228+
$prefix = in_array('nullable', $modifiers)
229+
? '$table->foreignId' . "('{$column_name}')->nullable()"
230+
: '$table->foreignId' . "('{$column_name}')";
231+
219232
if ($column_name === Str::singular($table) . '_' . $column) {
220-
return self::INDENT . '$table->foreignId' . "('{$column_name}')->constrained()->cascadeOnDelete()";
233+
return self::INDENT . "{$prefix}->constrained()->cascadeOnDelete()";
221234
}
222235
if ($column === 'id') {
223-
return self::INDENT . '$table->foreignId' . "('{$column_name}')->constrained('{$table}')->cascadeOnDelete()";
236+
return self::INDENT . "{$prefix}->constrained('{$table}')->cascadeOnDelete()";
224237
}
225238

226-
return self::INDENT . '$table->foreignId' . "('{$column_name}')->constrained('{$table}', '{$column}')->cascadeOnDelete()";
239+
return self::INDENT . "{$prefix}->constrained('{$table}', '{$column}')->cascadeOnDelete()";
227240
}
228241

229242
return self::INDENT . '$table->foreign' . "('{$column_name}')->references('{$column}')->on('{$table}')->onDelete('cascade')";

tests/Feature/Generator/MigrationGeneratorTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,60 @@ public function output_does_not_duplicate_pivot_table_migration_laravel6()
368368
$this->assertEquals(['created' => [$company_migration, $people_migration, $pivot_migration]], $this->subject->output($tree));
369369
}
370370

371+
/**
372+
* @test
373+
*/
374+
public function output_creates_foreign_keys_with_nullable_chained_correctly()
375+
{
376+
$this->files->expects('stub')
377+
->with('migration.stub')
378+
->andReturn(file_get_contents('stubs/migration.stub'));
379+
380+
$now = Carbon::now();
381+
Carbon::setTestNow($now);
382+
383+
$model_migration = str_replace('timestamp', $now->format('Y_m_d_His'), 'database/migrations/timestamp_create_carts_table.php');
384+
385+
$this->files
386+
->expects('put')
387+
->with($model_migration, $this->fixture('migrations/nullable-chaining.php'));
388+
389+
$tokens = $this->blueprint->parse($this->fixture('definitions/nullable-chaining.bp'));
390+
$tree = $this->blueprint->analyze($tokens);
391+
392+
$this->assertEquals(['created' => [$model_migration]], $this->subject->output($tree));
393+
}
394+
395+
/**
396+
* @test
397+
*/
398+
public function output_creates_foreign_keys_with_nullable_chained_correctly_laravel6()
399+
{
400+
$app = \Mockery::mock();
401+
$app->shouldReceive('version')
402+
->withNoArgs()
403+
->andReturn('6.0.0');
404+
App::swap($app);
405+
406+
$this->files->expects('stub')
407+
->with('migration.stub')
408+
->andReturn(file_get_contents('stubs/migration.stub'));
409+
410+
$now = Carbon::now();
411+
Carbon::setTestNow($now);
412+
413+
$model_migration = str_replace('timestamp', $now->format('Y_m_d_His'), 'database/migrations/timestamp_create_carts_table.php');
414+
415+
$this->files
416+
->expects('put')
417+
->with($model_migration, $this->fixture('migrations/nullable-chaining-laravel6.php'));
418+
419+
$tokens = $this->blueprint->parse($this->fixture('definitions/nullable-chaining.bp'));
420+
$tree = $this->blueprint->analyze($tokens);
421+
422+
$this->assertEquals(['created' => [$model_migration]], $this->subject->output($tree));
423+
}
424+
371425
public function modelTreeDataProvider()
372426
{
373427
return [
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
models:
2+
Cart:
3+
name: string
4+
user_id: id foreign nullable
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class CreateCartsTable extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
Schema::create('carts', function (Blueprint $table) {
17+
$table->bigIncrements('id');
18+
$table->string('name');
19+
$table->unsignedBigInteger('user_id')->nullable();
20+
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
21+
$table->timestamps();
22+
});
23+
}
24+
25+
/**
26+
* Reverse the migrations.
27+
*
28+
* @return void
29+
*/
30+
public function down()
31+
{
32+
Schema::dropIfExists('carts');
33+
}
34+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class CreateCartsTable extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
Schema::create('carts', function (Blueprint $table) {
17+
$table->id();
18+
$table->string('name');
19+
$table->foreignId('user_id')->nullable()->constrained()->cascadeOnDelete();
20+
$table->timestamps();
21+
});
22+
}
23+
24+
/**
25+
* Reverse the migrations.
26+
*
27+
* @return void
28+
*/
29+
public function down()
30+
{
31+
Schema::dropIfExists('carts');
32+
}
33+
}

0 commit comments

Comments
 (0)