Browsing as a guest. You can post with a name and email, like, and report. Sign in as Maya, Tomas, Priya… to pin, moderate, and see author badges from the other side.

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!

Jonas Weber Jonas Weber

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 *_comments tables drift out of sync on features.

Aisha Bello Aisha Bello

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.

Sam Okafor Sam Okafor

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.

Lukas Novak Lukas Novak

Reading this as someone six weeks into Laravel — is there a downside to just using this pattern for everything user-generated? Attachments, ratings, reactions?

Noor Haddad Noor Haddad

One thing I would add to the "where it bites" section: sorting.

most_replied needs a withCount, 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.