1

I used the one-to-many polymorphic relationship like described in the laravel documentation, to be able to relate different parent models to one child model. i have assumed, that i would be able to assign different parent models to the same child model. but this does not work. as soon as i create a new relation with another parent model to the same child, the old relation is replaced.

Example:

A, B and C are parent models, each with one data-record (id=1).

X is the child model with one data-record (id=1)

I can't do something like that with the common methods:

A(id=1) <-> X(id=1)

B(id=1) <-> X(id=1)

C(id=1) <-> X(id=1)

Since the last creation of a relation always overwrites the previous one. In this example one relation would remain (C(id=1) <-> X(id=1))

I am able to do that with a many-to-many polymorphic implementation - but this is actually not what i want, since i do not want the parent models to be able to have more than one relation to the child model. (altough i could rule that out by creating a composite key within the *able table on the corresponding fields)

This is the actual code, that should assign one image to multiple parent models (but only the last save inside the loop remains - if i add a break at the end of the loop, the first one is saved):

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);
        }
    }
}
4

1 回答 1

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]);

于 2021-06-30T09:17:09.277 回答