Reputation: 3620
I am trying to get my head around my first polymorphic relationship in Laravel 5.3.
The relationship i am trying to achieve is that an 'Editorial' will have many 'Elements' each element will be its own Model and have an order.
So for example i will have an 'ElementText', 'ElementImage', 'ElementButton' models then an 'Editorial' will have various elements assigned to it.
This is why i think i need a polymorphic relationship. I don't want multiple relationships for each element type in the 'Editorials' model, i want to be able to just get all 'Elements' regardless of its type and get it in order.
I think the idea solution would be to have an 'editorial_element' pivot table with columns like
editorial_id - integer
order - integer
element_id - integer
element_type - string
similar to the morphToMany
example in the laravel docs, but in that example you need to specify the model to morph.
So i also wondered if i should be using the morphTo() method in my Editorials model.
public function elements() {
return $this->morphTo();
}
But i think that means i would have to add _type
and _id
columns to my editorials table which would be backwards (allowing one element per editorial)
Any idea if i can set the relationship i require up or am i approaching it wrong?
Upvotes: 2
Views: 321
Reputation: 1060
You should setup your relationship as below.
In your Editorial
model you should have methods for each Element*
model that it's morphed by. E.g.
public function textElements() {
return $this->morphedByMany(ElementText::class, 'element');
}
public function buttonElements() {
return $this->morphedByMany(ElementButton::class, 'element');
}
In your Element*
models you should have the below method to get all the Editorials
that it's morphed to.
public function editorials()
return $this->morphToMany(Editorial::class, 'element');
}
note: There are some issues with using polymorphic relationships that is worth noting. The most important one is that yes you get some simplicity in your database structure but then at the same time you lose foreign key constraints. This means that if an Element*
is deleted, you need to delete any Editorials
that it's morphed to manually, whereas in a normal Many to Many
relationship you could set a foreign key constraint and the RDBMS does this for you.
note 2: First of all I would try to see if those Element tables can be unified into a single table. If they can your work would get a lot easier. But if you have to use many Element tables, I think both ways are fine. Each has an advantage and a disadvantage.It's many relationship tables or polymorphic relationships which you have to handle database consistency yourself, And in either way you can't do $editorial->elements()
with a relationship.
Upvotes: 0
Reputation: 1974
I don't think polymorphic relations are going to fit well here. I might approach it like so.
editorials
- id
- title
- etc...
elements
- editorial_id
- elements_id
- elements_type
elements_image
- id
- url
elements_text
- id
- text
Then your models can be.
class Editorial extends Model {
// ...
public function elements() {
return $this->hasMany(\Element::class);
}
// ...
}
class Element extends Model {
// ...
public function editorial() {
return $this->belongsTo(\Editorial::class);
}
public function image() {
return $this->belongsTo(\ElementImage::class, 'elements_id');
}
public function text() {
return $this->belongsTo(\ElementText::class, 'elements_id');
}
public function content() {
$type = $this->elements_type;
return $this->{$type}();
}
// ...
}
class ElementImage extends Model {
// ...
public function element() {
return $this->hasOne(\Element::class, 'elements_id');
}
// ...
}
class ElementText extends Model {
// ...
public function element() {
return $this->hasOne(\Element::class, 'elements_id');
}
// ...
}
Perhaps then you could make all elements implement an interface which forces a render()
method on all elements and then you can do this in the view:
@foreach($editorial->elements as $element)
{{ $element->content->render() }}
@endforeach
The render method can then be responsible for formatting and outputting the content.
This is all just theory, I'd be interested to see if you could get this to work because it's a problem I've tried to tackle before also. Please let me know of any corrections or if you think my idea is just dumb.
Upvotes: 1