【问题标题】:Laravel 8: One To Many Polymorphic: Relate same child to different parentsLaravel 8:一对多多态:将同一个孩子与不同的父母联系起来
【发布时间】:2021-06-30 07:09:15
【问题描述】:

我使用了 laravel documentation 中描述的一对多多态关系,以便能够将不同的父模型关联到一个子模型。我假设,我可以将不同的父模型分配给同一个子模型。但这不起作用。一旦我与另一个父模型与同一个子模型创建新关系,旧关系就会被替换。

例子:

ABC 是父模型,每个模型都有一个数据记录 (id=1)。

X 是具有一条数据记录的子模型 (id=1)

我不能用常用的方法做这样的事情:

A(id=1) X(id=1)

B(id=1) X(id=1)

C(id=1) X(id=1)

由于最后一次创建关系总是会覆盖前一个。在此示例中,将保留一个关系 (C(id=1) X(id=1))

我可以通过多对多多态实现来做到这一点——但这实际上不是我想要的,因为我不希望父模型能够与子模型有多个关系。 (尽管我可以通过在相应字段的 *able 表中创建复合键来排除这种情况)

这是实际代码,应该将一个图像分配给多个父模型(但只保留循环内的最后一个保存 - 如果我在循环结束时添加一个中断,则保存第一个):

public function store(StoreImageRequest $request)
{
    $validated = $request->validated();

    $image = $validated['image'];
    $name = isset($validated['clientName']) ? $image->getClientOriginalName() : $validated['name'];
    $fileFormat = FileFormat::where('mimetype','=',$image->getClientMimeType())->first();

    $path = $image->store('images');

    $imageModel = Image::make(['name' => $name, 'path' => $path])->fileFormat()->associate($fileFormat);
    $imageModel->save();

    $relatedModels = Image::getRelatedModels();

    foreach($relatedModels as $fqcn => $cn) {
        if(isset($validated['model'.$cn])) {
            $id = $validated['model'.$cn];
            $models[$fqcn] = call_user_func([$fqcn, 'find'], [$id])->first();
            $models[$fqcn]->images()->save($imageModel);
        }
    }
}

【问题讨论】:

  • 也许添加 getRelatedModels 的实现以真正完全理解您的代码是有意义的。乍一看,我会说$models[$fqcn]->images()->save($imageModel); 行是问题所在。我猜想 imageModel 在保存后会更新,并且多次调用它也会更新引用。也许将您的关系定义也添加到代码中。您可以快速尝试$models[$fqcn]->images()->sync([$imageModel->id]);
  • @Frnak 我不认为这应该是一个问题 - 因为我这样做并且想要保存不同的参考。因此 imageModel 应该正确处理。但我会试一试。您确定该关系类型的预期行为 - 我认为它是否应该能够将特定孩子与多个父母联系起来?

标签: laravel one-to-many relation polymorphic-relationship


【解决方案1】:

嗯,你现在想要做什么更清楚了。 问题是,即使您想强制每个父母最多有一个附加到孩子,您实际上仍在尝试创建多对多 PolyMorphic 关系。父母可以有多个图像,图像可以有多个父母(每个父母一个)。

在不了解数据结构的情况下,如果父级都具有相似的结构将它们合并到一个表中,并且查看 HasOne ofMany 关系以强制图像只有每个父级中的一个,那么它可能是可行的。

如果你坚持多态关系,我会这样做

    // child
    Schema::create('images', function (Blueprint $table) {
        $table->id();
        $table->timestamps();
    });

    // parents
    Schema::create('parent_ones', function (Blueprint $table) {
        $table->id();
        $table->timestamps();
    });


    Schema::create('parent_twos', function (Blueprint $table) {
        $table->id();
        $table->timestamps();
    });

    Schema::create('parent_trees', function (Blueprint $table) {
        $table->id();
        $table->timestamps();
    });

    // morph table
    Schema::create('parentables', function (Blueprint $table) {
        $table->id();
        $table->unsignedBigInteger('image_id');
        $table->foreign('image_id')->references('id')->on('images');
        $table->string('parentable_type');
        $table->unsignedBigInteger('parentable_id');
        $table->timestamps();
        $table->unique(['image_id', 'parentable_type', 'parentable_id'], 'uniq_parents');
    });

表上的唯一约束强制图像只能具有每个父类型之一。

模型关系

   // Child

   public function parent_one(): MorphToMany
    {
        return $this->morphedByMany(ParentOne::class, 'parentable');
    }

    public function parent_two(): MorphToMany
    {
        return $this->morphedByMany(ParentTwo::class, 'parentable');
    }

    public function parent_tree(): MorphToMany
    {
        return $this->morphedByMany(ParentTree::class, 'parentable');
    }


    // parents
    public function images(): MorphToMany
    {
        return $this->morphToMany(Image::class, 'parentable')->withTimestamps();
    }

然后由您的代码来处理它不会尝试将父级附加到图像,如果它已经具有该类型的父级。

这可以在您的控制器中完成,或者如果父级不可替换,您可以将其作为验证添加到您的请求中。

解决方案可能是安装此软件包(未经我测试)。 MorphToOne


旧答案

在我看来,您将一对多关系翻转到错误的方向。

你的子模型应该实现morphTo,你的父模型应该实现morphMany

a 
    id - integer
    title - string
    body - text

b
    id - integer
    title - string
    url - string

c
    id - integer
    title - string
    url - string

x
    id - integer
    body - text
    commentable_id - integer
    commentable_type - string

儿童模型

public class X extends Model
{
    public function commentable(): MorphTo
    {
        return $this->morphTo();
    }
}

父模型

public class A extends Model
{
    public function comments(): MorphMany
    {
        return $this->morphMany(X::class, 'commentable');
    }
}

添加到关系中:

$a = A::firstOrFail();
$x = X::firstOrFail();

// Attaches
$a->comments()->attach($x);

// Sync, removes the existing attached comments
$a->comments()->sync([$x]);

// Sync, but do not remove  existing
$a->comments()->sync([$x], false);
$a->comments()->syncWithoutDetaching([$x]);

【讨论】:

  • 嗨,Steffen,不,情况并非如此。我像你一样建造它。但是我不是通过使用 attach() 或 sync() 添加关系(请参阅我的更新问题和实际代码)。也许它的行为类似于删除同步 - 我会尝试的。
  • 嗨史蒂芬,谢谢。但我的问题是相反的。我想将不同的父 ID 分配给一个特定的子 ID - 在我的情况下,这对于一对多的多态关系是行不通的。
  • 根据您提供的信息,我会说我的答案是正确的。一个父母可以有多个孩子,一个孩子只能有一个父母。这是一个多对多关系,仅限于从孩子到每个父母的关系。如果不是这种情况,您需要使用您的模型结构扩展最初的问题。
猜你喜欢
  • 2020-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-10
相关资源
最近更新 更多