I’ve been trying to model a relationship in Laravel between a ShapeContainer
entity and different shapes with unique properties, but a common Shape
interface (with getArea
and getPerimeter
methods). I’ve read up on the examples in the laravel documentation (posts
, videos
, comments
), but in that example the child entity (Comment
) can belong to different types of parents. In my case I want the parent entity (ShapeContainer
) to be able to handle different types of children (Circle,
Rectangle
etc).
The end result I’m kind of looking for is something like this:
$shapeContainer = ShapeContainer::find(1);
$shapes = $shapeContainer->shapes()
foreach($shapes as $shape) {
$area = $shape->getArea();
}
The way I managed to achieve with this right now is with adding an entity in between, something like a pivot
class Shape extends Model
{
public function shapeable()
{
return $this->morphTo('shapeable');
}
public function shapeConatiner()
{
return $this->belongsTo(ShapeContainer::class);
}
}
with the corresponding table fields
shape_container_id # Reference to the shape container
shapeable_type # square/circle
shapeble_id # Reference to the ID of the corresponding shape model.
so in code I can do
$shapeContainer = ShapeContainer::find(1);
$shapes = $shapeContainer->shapes()
foreach($shapes as $shape) {
$area = $shape->shapable->getArea();
}
I’m wondering is this the way to go, since I’ve not recognized anything similar to my use case in the laravel documentation and it seems clunky to have a whole model inbetween just to explain the relationship. Is there a possibility to keep that pivot table, but simplify the interface so that developers using the model won’t have to worry about or see the shapeable()
pivot?
2
Answers
It sounds like you need to cast the model to its instant type when it is created, then each instance can have its own version of
getArea
try to implement something like this inside
Shape
:I think you can achieve what you need with a polymorphic many to many relation, and an inverse belongs to relation, since a container can have many shapes and one shape can belong to only one container, documented here: https://laravel.com/docs/11.x/eloquent-relationships#many-to-many-polymorphic-relations
An example implementation for your case:
This uses the same pivot table you have, but it does not require a model for it; but for the inverse relation it uses a
shape_container_id
of each child shape, ‘circles’, ‘rectangles’