当我开始这个项目时,我的 RemitoEntry 模型与 Remito 模型有共同的关系,它的迁移是这样的:
public function up(): void
{
Schema::create('remito_entries', function (Blueprint $table) {
$table->id();
$table->foreignId('remito_id')->constrained();
$table->foreignId('article_id')->constrained();
$table->foreignId('article_presentation_id')->constrained();
$table->string('article_presentation_flat_name');
$table->decimal('amount', 24, 2);
$table->softDeletes();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('remito_entries');
}
然后我必须调整模型以接受与模型“Remito”和新模型“InternalRemito”的多态关系,并且由于我无法修改原始迁移,所以我必须创建一个新的迁移来修改它:
public function up()
{
Schema::table('remito_entries', function (Blueprint $table) {
$table->renameColumn('remito_id', 'remito_entriable_id');
$table->string('remito_entriable_type')->default(Remito::class);
$table->enum('type', ['internal', 'external']);
});
}
public function down()
{
Schema::table('remito_entries', function (Blueprint $table) {
$table->dropColumn('remito_entriable_type');
$table->renameColumn('remito_entriable_id', 'remito_id');
$table->dropColumn('type');
});
}
我的问题是,如果没有先为“Remito”模型创建 RemitoEntry,我就无法为“InternalRemito”模型创建 RemitoEntry,我不明白为什么,我认为尽管更改了名称,remito_entriable_id 仍需要一个Remito 型号的 id。如何使 remito_entriable_id 的 id 为“Remito”或“InternalRemito”。
这是当我尝试为 InternalRemito 创建 RemitoEntry 时邮递员向我显示的错误:
"message": "SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`default`.`remito_entries`, CONSTRAINT `remito_entries_remito_id_foreign` FOREIGN KEY (`remito_entriable_id`) REFERENCES `remitos` (`id`)) (Connection: mysql, SQL: insert into `remito_entries` (`type`, `article_id`, `article_presentation_id`, `article_presentation_flat_name`, `amount`, `remito_entriable_id`, `remito_entriable_type`, `created_by`, `updated_by`, `updated_at`, `created_at`) values (internal, 1, 1, debitis, 1, 1, App\\Models\\InternalRemito, 1, 1, 2023-12-04 15:33:06, 2023-12-04 15:33:06))",
让我们先看看
$table->foreignId('remito_id')->constrained();
对数据库做了什么:
remito_id
的无符号大整数字段。现在让我们深入探讨您想要的多态方法:
remito_entriable_id
不会被定义到它属于哪个表(关系),它只能从 remito_entriable_type
字段猜测。现在出现错误的原因是您对
remito_entriable_id
进行了 FK 设置,从而阻止了条目。要解决您的问题,您的迁移应如下所示:
// Your new migration should like this
// execute this line if you haven't dropped the FK manually.
$table->dropForeign(['remito_id']);
$table->renameColumn('remito_id', 'remito_entriable_id');
$table->string('remito_entriable_type'); // We can't define the default on the migration as it can be any model, that is why it is called polymorphic
在您的
RemitoEntry
型号上:
use Illuminate\Database\Eloquent\Relations\MorphTo;
public function remitoEntriable(): MorphTo
{
return $this->morphTo();
}
在您的
Remito
型号上:
use Illuminate\Database\Eloquent\Relations\MorphMany;
/**
* Get all of the remito's entries.
*/
public function remitoEntries(): MorphMany
{
return $this->morphMany(RemitoEntry::class, 'remitoEntriable');
}
在您的
InternationlRemito
型号上:
use Illuminate\Database\Eloquent\Relations\MorphMany;
/**
* Get all of the internal remito's entries.
*/
public function remitoEntries(): MorphMany
{
return $this->morphMany(RemitoEntry::class, 'remitoEntriable');
}
您还可以将此方法(对于所有模型都相同)重构为特征:
// HasRemitoEntries.php
namespace App\Traits\Models;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use App\Models\RemitoEntry;
trait HasRemitoEntries
{
/**
* Get all of the remito's entries.
*/
public function remitoEntries(): MorphMany
{
return $this->morphMany(RemitoEntry::class, 'remitoEntriable');
}
}
现在您可以在每个想要变形为
RemitoEntry
模型的模型中使用该特征。