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);
}
}
}
Well, it's a bit more clear what you're trying to do now. The thing is, even though you want to enforce that a maximum of one of each parent is attached to the child, you're still actually trying to create a ManyToMany PolyMorphic relationship. Parents can have mutiple images and images can have multiple parents (one of each).
Without knowing the data structure, it could be viable, if the parents all have similar structures to consolidate them into one table, and the look into the relation HasOne ofMany to enforce that an image only have one of each parent.
If you insist on a Polymorphic relationship, I would do the following
The unique constraint on the table enforces that an image can only have one of each parent type.
Model relations
Then it's up to you code to handle that it does not try to attach a parent to an image if it already has a parent of that type.
This could either be done in your controller or if the parent isn't replaceable, you could add it as validation in you Request.
A solution could be installing this package (untested by me). MorphToOne
Old answer
It sounds to me that you have flipped the One To Many relation in the wrong direction.
Your child model should implement
morphTo
and your parent models should implementmorphMany
Child Model
Parent Models
Adding to the relation: