phplaraveleloquenteloquent-relationshiplaravel-10

Initialize One To Many Polymorphic relationship from json data in Laravel


I have an items json array column in a Catalog eloquent model that contains itemable_type and itemable_id properties, it looks like this [{"itemable_id": 1, "qty": 1, "itemable_type":"\\App\\Models\\Food"}], can I use this information to manually initialize a One To Many Polymorphic relationships?

Basically my database looks like this:

ID items ...
Int Json (Eg. [{"itemable_id": 1, "qty": 1, "itemable_type":"\App\Models\Food"}]) ...

I am able a make a regular Eloquent Collection with something like this:


class VendorCatalog extends Model
{
   public function catalogable(): \Illuminate\Database\Eloquent\Builder
   {
       $items = \Illuminate\Database\Eloquent\Collection::make();

       $models = $this->items
           ->filter(fn ($i) => !empty($i['type']))
           ->filter(fn ($i) => class_exists(str("\\App\\Models\\")->append($i['type'])->toString()))
           ->groupBy('type')->map(function ($items, $group) {
              $class = str("\\App\\Models\\")->append($group)->toString();
              return $class::whereIn('id', $items->pluck('id'))->get();
           });

       return $items->merge($models->flatten())->toQuery();
   }
}

This works, but considering that the relationship might contain unrelated models, a One To Many Polymorphic relationship would be better suited.

So it possible or should I go ahead and just add every item as a distinct record instead of storing IDs and types?

I know this is a complex requirement, but hey! Who doesn't want a little challenge? Also, this will make my database structure a little less cluttered and more manageable.

EDIT

Also, I realised that if I call the getQuery() method on the catalogable I get this error Unable to create query for collection with mixed types

$catalogable = VendorCatalog::first()->catalogable()->getQuery();

Solution

  • I would recommend you normalize your table and separate that json into 2 columns as it should be. This will greatly benefit you in performance and keeping things clean.

    Avoid as much as possible using json columns in a relational DB, especially when used in relational queries. It can really hurt you in performance as records grow. If you just need to return the whole json without making relationships with it there are some use cases for it, but mostly avoid jsons.

    Also, this will make my database structure a little less cluttered and more manageable.

    Actually separating the columns will keep the DB more manageable and clean.

    In your example above imagine your table contains 100,000 items. Your query will first have to pull those 100,000 records from the DB and store that in memory on your server (probably causing an out of memory exception) and then filter the records on php side (high CPU usage). If you normalize the table all the filtering and querying is done on MySQL side, which should be more performant, clean and you can use the full power of relational DBs queries.

    Most of my experiences with json columns in DBs (and I have had a lot of them) are bad ones and usually cause more harm than good.