Architecture · 7 min read
Polymorphic relations are underrated
Maya Chen · Staff Engineer · 4 days ago
One comments table, every model in your app. A look at why morphMany is the right shape for anything users attach to things.
Most apps grow a post_comments table, then a product_reviews table, then
ticket_replies. Three tables, three sets of queries, three moderation tools.
A single polymorphic table collapses all of it. commentable_type plus
commentable_id is enough to hang a thread off literally anything, and every
feature you build — sorting, likes, reporting, moderation — is built once.
The trade you are making
You give up database-level foreign keys on the morph columns. In return you get one query surface and one admin screen. For user-generated content, where you are going to want bulk moderation and a spam pipeline anyway, that trade pays for itself in about a week.
Where it bites
Eager loading. Comment::with('commentable') fans out into one query per
distinct type, so keep that in mind on aggregate screens. Scope by type when
you can, and cache the counts you show in lists.
Discussion (5)
Or Log in to comment!
The point about losing foreign keys is the one that always comes up in review, and I think it gets overweighted. In five years I have never had an orphaned comment row cause a production incident. I have had three separate
*_commentstables drift out of sync on features.Moderator note: the comment that used to sit here with two scam URLs was denied by the heuristic checker. That is the spam pipeline, not a person at 2am.
Shipped this pattern on my side project last month. The bit I underestimated was moderation — one table means one queue, which turned out to be the actual win.
Reading this as someone six weeks into Laravel — is there a downside to just using this pattern for everything user-generated? Attachments, ratings, reactions?
One thing I would add to the "where it bites" section: sorting.
most_repliedneeds awithCount, and if you are also filtering by approval status the counts have to respect that filter or your numbers lie. Took me an embarrassingly long afternoon to work that out.