mirror of
https://github.com/barryvdh/laravel-ide-helper.git
synced 2026-08-17 17:47:13 +00:00
Make Soft Deleted Relationships Nullable (#1749)
This commit is contained in:
@@ -339,6 +339,29 @@ return [
|
||||
|
||||
'enforce_nullable_relationships' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Make soft deletable relations nullable
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When set to true (default), relationships to models using SoftDeletes trait
|
||||
| will be marked as nullable. This is because soft-deleted records are excluded
|
||||
| from queries by default, meaning even non-nullable foreign keys can return
|
||||
| null when the related model is soft-deleted.
|
||||
|
|
||||
| Default: true
|
||||
| A relationship to a soft-deletable model will include |null in the type:
|
||||
| * @property-read Team|null $team
|
||||
|
|
||||
| Option: false
|
||||
| A relationship to a soft-deletable model will NOT include |null (unless
|
||||
| nullable for other reasons such as nullable foreign key column):
|
||||
| * @property-read Team $team
|
||||
|
|
||||
*/
|
||||
|
||||
'soft_deletes_force_nullable' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Run artisan commands after migrations to generate model helpers
|
||||
|
||||
@@ -911,9 +911,31 @@ class ModelsCommand extends Command
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->relatedModelUsesSoftDeletes($relationObj)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the related model uses the SoftDeletes trait
|
||||
*
|
||||
* @param Relation $relationObj
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function relatedModelUsesSoftDeletes(Relation $relationObj): bool
|
||||
{
|
||||
if (!$this->laravel['config']->get('ide-helper.soft_deletes_force_nullable', true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$relatedModel = $relationObj->getRelated();
|
||||
|
||||
return in_array('Illuminate\\Database\\Eloquent\\SoftDeletes', class_uses_recursive($relatedModel));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the morphTo relation is nullable
|
||||
*
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
|
||||
class ModelWithRelations extends Model
|
||||
{
|
||||
protected $table = 'models_with_relations';
|
||||
|
||||
public function softDeletable(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id');
|
||||
}
|
||||
|
||||
public function nonSoftDeletable(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id');
|
||||
}
|
||||
|
||||
public function softDeletableHasOne(): HasOne
|
||||
{
|
||||
return $this->hasOne(SoftDeletableModel::class, 'model_with_relations_id');
|
||||
}
|
||||
|
||||
public function nonSoftDeletableHasOne(): HasOne
|
||||
{
|
||||
return $this->hasOne(NonSoftDeletableModel::class, 'model_with_relations_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class NonSoftDeletableModel extends Model
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class SoftDeletableModel extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations;
|
||||
|
||||
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
|
||||
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
class Test extends AbstractModelsCommand
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
$command = $this->app->make(ModelsCommand::class);
|
||||
|
||||
$tester = $this->runCommand($command, [
|
||||
'--write' => true,
|
||||
]);
|
||||
|
||||
$this->assertSame(0, $tester->getStatusCode());
|
||||
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
|
||||
$this->assertMatchesMockedSnapshot();
|
||||
}
|
||||
|
||||
public function testSoftDeletesForceNullableDisabled(): void
|
||||
{
|
||||
Config::set('ide-helper.soft_deletes_force_nullable', false);
|
||||
|
||||
$command = $this->app->make(ModelsCommand::class);
|
||||
|
||||
$tester = $this->runCommand($command, [
|
||||
'--write' => true,
|
||||
]);
|
||||
|
||||
$this->assertSame(0, $tester->getStatusCode());
|
||||
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
|
||||
$this->assertMatchesMockedSnapshot();
|
||||
|
||||
Config::set('ide-helper.soft_deletes_force_nullable', true);
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $soft_deletable_model_id
|
||||
* @property int $non_soft_deletable_model_id
|
||||
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\NonSoftDeletableModel $nonSoftDeletable
|
||||
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\NonSoftDeletableModel|null $nonSoftDeletableHasOne
|
||||
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel $softDeletable
|
||||
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableHasOne
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations whereNonSoftDeletableModelId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations whereSoftDeletableModelId($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ModelWithRelations extends Model
|
||||
{
|
||||
protected $table = 'models_with_relations';
|
||||
|
||||
public function softDeletable(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id');
|
||||
}
|
||||
|
||||
public function nonSoftDeletable(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id');
|
||||
}
|
||||
|
||||
public function softDeletableHasOne(): HasOne
|
||||
{
|
||||
return $this->hasOne(SoftDeletableModel::class, 'model_with_relations_id');
|
||||
}
|
||||
|
||||
public function nonSoftDeletableHasOne(): HasOne
|
||||
{
|
||||
return $this->hasOne(NonSoftDeletableModel::class, 'model_with_relations_id');
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int|null $model_with_relations_id
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|NonSoftDeletableModel newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|NonSoftDeletableModel newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|NonSoftDeletableModel query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|NonSoftDeletableModel whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|NonSoftDeletableModel whereModelWithRelationsId($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class NonSoftDeletableModel extends Model
|
||||
{
|
||||
}
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int|null $model_with_relations_id
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel whereDeletedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel whereModelWithRelationsId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel withTrashed(bool $withTrashed = true)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class SoftDeletableModel extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $soft_deletable_model_id
|
||||
* @property int $non_soft_deletable_model_id
|
||||
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\NonSoftDeletableModel $nonSoftDeletable
|
||||
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\NonSoftDeletableModel|null $nonSoftDeletableHasOne
|
||||
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletable
|
||||
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableHasOne
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations whereNonSoftDeletableModelId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations whereSoftDeletableModelId($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ModelWithRelations extends Model
|
||||
{
|
||||
protected $table = 'models_with_relations';
|
||||
|
||||
public function softDeletable(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id');
|
||||
}
|
||||
|
||||
public function nonSoftDeletable(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id');
|
||||
}
|
||||
|
||||
public function softDeletableHasOne(): HasOne
|
||||
{
|
||||
return $this->hasOne(SoftDeletableModel::class, 'model_with_relations_id');
|
||||
}
|
||||
|
||||
public function nonSoftDeletableHasOne(): HasOne
|
||||
{
|
||||
return $this->hasOne(NonSoftDeletableModel::class, 'model_with_relations_id');
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int|null $model_with_relations_id
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|NonSoftDeletableModel newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|NonSoftDeletableModel newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|NonSoftDeletableModel query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|NonSoftDeletableModel whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|NonSoftDeletableModel whereModelWithRelationsId($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class NonSoftDeletableModel extends Model
|
||||
{
|
||||
}
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int|null $model_with_relations_id
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel whereDeletedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel whereModelWithRelationsId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel withTrashed(bool $withTrashed = true)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|SoftDeletableModel withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class SoftDeletableModel extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class SoftDeletesRelationsTable extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('soft_deletable_models', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->unsignedBigInteger('model_with_relations_id')->nullable();
|
||||
$table->softDeletes();
|
||||
});
|
||||
|
||||
Schema::create('non_soft_deletable_models', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->unsignedBigInteger('model_with_relations_id')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('models_with_relations', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->unsignedBigInteger('soft_deletable_model_id');
|
||||
$table->unsignedBigInteger('non_soft_deletable_model_id');
|
||||
|
||||
$table->foreign('soft_deletable_model_id')
|
||||
->references('id')
|
||||
->on('soft_deletable_models');
|
||||
|
||||
$table->foreign('non_soft_deletable_model_id')
|
||||
->references('id')
|
||||
->on('non_soft_deletable_models');
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user