Skip to content

Commit f811634

Browse files
authored
Merge pull request #4205 from Laravel-Backpack/fix-hasmany-fallbacks-and-defaults
Delete HasMany subentry when relation column is not nullable
2 parents bf44be0 + 3c15e72 commit f811634

File tree

6 files changed

+131
-3
lines changed

6 files changed

+131
-3
lines changed

src/app/Library/CrudPanel/Traits/Create.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,19 +256,28 @@ private function handleManyRelationItemRemoval($modelInstance, $removedEntries,
256256
$forceDelete = $relationDetails['force_delete'] ?? false;
257257
$fallbackId = $relationDetails['fallback_id'] ?? false;
258258

259+
// developer provided a fallback_id he knows what he's doing, just use it.
259260
if ($fallbackId) {
260261
return $removedEntries->update([$relationForeignKey => $fallbackId]);
261262
}
262263

264+
// developer set force_delete => true, so we don't care if it's nullable or not,
265+
// we just follow developer's will
263266
if ($forceDelete) {
264267
return $removedEntries->delete();
265268
}
266269

267-
if (! $relationColumnIsNullable && $modelInstance->dbColumnHasDefault($relationForeignKey)) {
268-
return $removedEntries->update([$relationForeignKey => $modelInstance->getDbColumnDefault($relationForeignKey)]);
270+
// get the default that could be set at database level.
271+
$dbColumnDefault = $modelInstance->getDbColumnDefault($relationForeignKey);
272+
273+
// if column is not nullable in database, and there is no column default (null),
274+
// we will delete the entry from the database, otherwise it will throw and ugly DB error.
275+
if (! $relationColumnIsNullable && $dbColumnDefault === null) {
276+
return $removedEntries->delete();
269277
}
270278

271-
return $removedEntries->update([$relationForeignKey => null]);
279+
// if column is nullable we just set it to the column default (null when it does exist, or the default value when it does).
280+
return $removedEntries->update([$relationForeignKey => $dbColumnDefault]);
272281
}
273282

274283
/**

tests/Unit/CrudPanel/CrudPanelCreateTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Backpack\CRUD\Tests\Unit\Models\Bang;
77
use Backpack\CRUD\Tests\Unit\Models\Comet;
88
use Backpack\CRUD\Tests\Unit\Models\Planet;
9+
use Backpack\CRUD\Tests\Unit\Models\PlanetNonNullable;
910
use Backpack\CRUD\Tests\Unit\Models\Universe;
1011
use Backpack\CRUD\Tests\Unit\Models\User;
1112
use Faker\Factory;
@@ -912,6 +913,13 @@ public function testHasManyCreatableRelationship()
912913
$this->assertEquals($inputData['universes'][0]['title'], $entry->fresh()->universes->first()->title);
913914
$this->assertEquals(3, $entry->fresh()->universes->first()->id);
914915
$this->assertEquals(1, Universe::all()->count());
916+
917+
$inputData['universes'] = null;
918+
919+
$this->crudPanel->update($entry->id, $inputData);
920+
921+
$this->assertEquals(0, count($entry->fresh()->universes));
922+
$this->assertEquals(0, Universe::all()->count());
915923
}
916924

917925
public function testHasManySelectableRelationshipWithoutForceDelete()
@@ -1082,4 +1090,37 @@ public function testHasManySelectableRelationshipNonNullableForeignKeyAndDefault
10821090
$this->assertCount(2, $comets);
10831091
$this->assertEquals(0, $comets->first()->user_id);
10841092
}
1093+
1094+
public function testHasManySelectableRelationshipNonNullable()
1095+
{
1096+
$this->crudPanel->setModel(User::class);
1097+
$this->crudPanel->addFields($this->userInputFieldsNoRelationships, 'both');
1098+
$this->crudPanel->addField([
1099+
'name' => 'planetsNonNullable',
1100+
'force_delete' => false,
1101+
'fallback_id' => false,
1102+
], 'both');
1103+
1104+
$faker = Factory::create();
1105+
$inputData = [
1106+
'name' => $faker->name,
1107+
'email' => $faker->safeEmail,
1108+
'password' => bcrypt($faker->password()),
1109+
'remember_token' => null,
1110+
'planetsNonNullable' => [1, 2],
1111+
];
1112+
1113+
$entry = $this->crudPanel->create($inputData);
1114+
1115+
$this->assertCount(2, $entry->planetsNonNullable);
1116+
1117+
$inputData['planetsNonNullable'] = null;
1118+
1119+
$this->crudPanel->update($entry->id, $inputData);
1120+
1121+
$this->assertCount(0, $entry->fresh()->planetsNonNullable);
1122+
1123+
$planets = PlanetNonNullable::all();
1124+
$this->assertCount(0, $planets);
1125+
}
10851126
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace Backpack\CRUD\Tests\Unit\Models;
4+
5+
use Backpack\CRUD\app\Models\Traits\CrudTrait;
6+
use Illuminate\Database\Eloquent\Model;
7+
8+
class PlanetNonNullable extends Model
9+
{
10+
use CrudTrait;
11+
12+
/*
13+
|--------------------------------------------------------------------------
14+
| GLOBAL VARIABLES
15+
|--------------------------------------------------------------------------
16+
*/
17+
18+
protected $table = 'planets_non_nullable';
19+
protected $primaryKey = 'id';
20+
public $timestamps = false;
21+
protected $fillable = ['title'];
22+
// protected $hidden = [];
23+
// protected $dates = [];
24+
25+
/*
26+
|--------------------------------------------------------------------------
27+
| FUNCTIONS
28+
|--------------------------------------------------------------------------
29+
*/
30+
31+
/*
32+
|--------------------------------------------------------------------------
33+
| RELATIONS
34+
|--------------------------------------------------------------------------
35+
*/
36+
37+
public function user()
38+
{
39+
return $this->belongsTo('Backpack\CRUD\Tests\Unit\Models\User');
40+
}
41+
42+
/*
43+
|--------------------------------------------------------------------------
44+
| SCOPES
45+
|--------------------------------------------------------------------------
46+
*/
47+
48+
/*
49+
|--------------------------------------------------------------------------
50+
| ACCESORS
51+
|--------------------------------------------------------------------------
52+
*/
53+
54+
/*
55+
|--------------------------------------------------------------------------
56+
| MUTATORS
57+
|--------------------------------------------------------------------------
58+
*/
59+
}

tests/Unit/Models/User.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ public function planets()
7676
return $this->hasMany('Backpack\CRUD\Tests\Unit\Models\Planet');
7777
}
7878

79+
public function planetsNonNullable()
80+
{
81+
return $this->hasMany('Backpack\CRUD\Tests\Unit\Models\PlanetNonNullable');
82+
}
83+
7984
public function comets()
8085
{
8186
return $this->hasMany('Backpack\CRUD\Tests\Unit\Models\Comet');

tests/config/database/migrations/2020_03_31_114745_create_pivotable_relations_tables.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ public function up()
7575
$table->string('title')->nullable();
7676
});
7777

78+
Schema::create('planets_non_nullable', function (Blueprint $table) {
79+
$table->bigIncrements('id');
80+
$table->bigInteger('user_id');
81+
$table->string('title')->nullable();
82+
});
83+
7884
Schema::create('comets', function (Blueprint $table) {
7985
$table->bigIncrements('id');
8086
$table->bigInteger('user_id')->default(0);

tests/config/database/seeds/MorphableSeeders.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ public function run()
4343
'title' => $faker->title,
4444
]]);
4545

46+
DB::table('planets_non_nullable')->insert([[
47+
'title' => $faker->title,
48+
'user_id' => '',
49+
], [
50+
'title' => $faker->title,
51+
'user_id' => '',
52+
]]);
53+
4654
DB::table('comets')->insert([['user_id' => ''], ['user_id' => '']]);
4755

4856
DB::table('bangs')->insert([['name' => 'bang1'], ['name' => 'bang2']]);

0 commit comments

Comments
 (0)