Laravel 中的多态关系

问题描述 投票:0回答:1

当我开始这个项目时,我的 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))",
laravel eloquent foreign-keys
1个回答
0
投票

让我们先看看

$table->foreignId('remito_id')->constrained();
对数据库做了什么:

  1. 创建
    remito_id
    的无符号大整数字段。
  2. 创建引用 remitos 表上的 id 的外键。

现在让我们深入探讨您想要的多态方法:

  1. 多态关系不适用于数据库级别,例如一对一、一对多等。
  2. 相反,它们在 Laravel 级别上工作,这意味着
    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
模型的模型中使用该特征。

© www.soinside.com 2019 - 2024. All rights reserved.