Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Rikaelus's avatar

Dynamic Multi-Object-Defined Polymorphic Relation

I'm relatively new to Laravel (started dabbling a couple months ago) and I'm about a week into digging into relationship modeling. Still, I'm charged with creating a Laravel implementation over an existing schema that I'm not at liberty to make fundamental changes to.

I've been applying conventional relations with ease but then I came upon a method of polymorphism that seems far beyond what Laravel can support out-of-the-box; a confluence of multiple challenges.

First, as an overview and reference point, here's a simplified and anonymized ERD for demonstration.

ERD-simplified.png

Preliminary Notes

  • In reality we aren't working with something as common as tags, but I figured using that in an example would help express the concept.
  • While object_tag may look like a conventional pivot table in the example, it's not one in practice. There may be additional attributes like "deleted" or "enabled" that would need to be accounted for, or other filterable attributes.
  • In practice we have maybe a dozen or more tables that sit in the position of object_tag.
  • In practice we have object_tag-level tables that have multiple instances of "object_type_id/object_id"

Challenge #1: Table Names instead of Class Name The databased identifier for the Model subclass polymorphic pivot isn't by class name, but by table name. Fortunately this particular challenge is already solved (hopefully) via a custom function that reflectively analyzes Model subclasses to ascertain the classes created for the tables and already accounts for cases where a table might map to multiple classes. This logic could dynamically populate Relation::enforceMorphMap if needed with a full-schema map.

Challenge #2: Separated Instructions The source object we're polymorphically linking to others (ObjectTag per the example above), doesn't provide both the aforementioned destination table name and destination record ID, itself. It provides the record ID (objectId) but the table name is in the parent ObjectType model -- one degree of separation. This requires a kind of inner-process to determine the destination table (and in turn Model subclass) to bring back for the polymorphic relation.

We also have instances in which there may be multiple degrees of separation between the source object and ObjectType, potentially necessitating a BelongsToThrough-like mechanism for that "inner-process."

Challenge #3: Dynamic We're not dealing with a simple scenario where an ObjectTag might be linked to just a post or category. It's designed to be linkable to any other object. And, in practice today, of a schema with over 300 tables, over 100 of them would have ObjectType records. Thus a dynamic approach is much preferred -- one not requiring a relationship-building method for every possibly linked object type in every ObjectTag-like Model.

Goal

In an ideal world we'd like to reach a point to where

  1. ObjectTag would have a generic object magic property that would return the destination object regardless of its Model subclass, defined by an object() method that establishes this dynamic polymorphic relationship.
  2. That dynamic polymorphic relationship would understand how to obtain the destination class type by traversing the "inner" BelongsTo or BelongsToThrough relationship to get the table name from ObjectType, and then querying that class for the given objectId.
  3. Tag would have a generic objects magic property that would return a collection of destination objects regardless of their Model subclass.
  4. (big bonus) The ability to say $objectTag->object->associate($newObject) and have it smart enough to update both the objectId and objectTypeId automatically.

Conclusion

Now I don't expect anybody to write this for me, let alone account for the complexity of our real-life system. I'm mostly just looking for leads. Can some culmination of existing relation types accomplish some or all of this? Third party libraries that add new relation types? Could some or all of this be accomplished via custom query/filters in the relation-building methods? Maybe extend existing relation subclasses or write a custom one to add flexibility, such as Closure support that might encapsulate the "inner process" of identifying the object to relate to?

While I'm on the verge of abandoning the "ideal world" hopes for the short-time, this concept of data linking isn't going away even if we can refactor the schema to be a little more Laravel'esque, and eventually we will want it handled -- even if that means subclassing Relation itself and building up from there.

P.S. I won't pretend like this is easy to understand. I'm posting this here instead of StackOverflow largely because this has a more conversational format and I can revise this post as opportunities for clarification become realized.

0 likes
0 replies

Please or to participate in this conversation.