Hi there. Here is my problem
I have an eloquent model called Notification with a polymorphism relation called notifiable. I have two other models called Event and History which are notifiable, both of them "belongs_to" a User model. History also has a one to one relationship with an Item model.
I need to retrieve the 50 most recents notifications and i want to eager load notifiable object and all depending relations in order to create my view.
So far i've been able to eager load "notifiable', just by using ->with('notifiable') in the query builder.
Ex :
$notifications = Notification::orderBy('created_at')->limit(50)->with('notifiable');
But because the other relations are beyond the polymorphism one i can't name them in the with() call, things like 'notifiable.user' won' t load anything.
If i don't eager load i find myself with a ugly n+1 problem ( real case is 3 sql queries per notifications which is bad). I have managed to do that manually and i finally manage to obtain a very optimised query numbers. But i had to change things in my views. Basically the relations beyond the polymorphism one can't be called using
{{ $notification->notifiable->user->name }}
If you do so you are going to fire a sql query each time. Instead of that i had eager load datas in some arrays myself and get user's name by doing :
{{ $eventUsers[$notification->notifiable->id] }}
or
{{ $historyUsers[$notification->notifiable->id] }}
Is there a way to achieve this in a more Eloquent style ?
Philippe
ps: if something is unclear just ask a question and i will provide more informations.
You are saying that Event and History has a relationship with User, but you are trying to load the relationship on Notification?
Hi Herlevsen, i'm gonna try to explain a little bit deeper
Yes Event and History both 'belongs_to' User.
It starts with :
$notifications = Notification::orderBy('created_at')->limit(50)->with('notifiable');
in a controller. Then I call a view in which there is something like:
<ul>
@foreach($notifications as $notification)
<li>
@include('notifications._show'.get_class($notification->notifiable))
</li>
@endforeach
</ul>
Meaning i have as many partials views as there are models 'notifiable' ( in my case _showEvent and _showHistory ) In a perfect world, inside, for example _showHistory partial i would have liked to write
{{ $notification->notifiable->user->name }} has an item named {{ $notification->notifiable->item->name }}
This actually works but you can't eager load it, it creates too many requests. So i had to eager load it myself, put things in array myself and i do things like :
{{ $userArray[$notification->notifiable->user_id]->name }}
has an item named {{ $itemArray[$notification->notifiable->item_id]->name }}
That works okay but it's not very elegant, plus it makes reusability of my partials highly doubtable. But now it's hell faster .....
Hope this helps
You can segregate each type of notification into their own collections and then eager load on the separate collections. I think that should eager load your original collection because of PHP's object by reference.
Try it out.
I'm curious as to the content of your notifiable() method on the Notification model and how you're able to even get a mixed collection in the first place. Can you post that?
Have you tried looking into using $with on the model?
protected $with = ['user'];
Just throwing out ideas.
Sign in to participate in this thread!
The Laravel portal for problem solving, knowledge sharing and community building.
The community