1

I can paginate notifications and subnotifications for user notifiable_id 5 individually without any issues. However, I am trying to have the results paginated together in one instance.

1) DB table name/data

notifications

enter image description here

subnotifications

enter image description here

2) Pagination

I can paginate each relation I have individually like this:

$notifs = $user->notifications()->where('type', 'UserWasFollowed')->paginate(10);
$subnotifications = $user->subnotifications()->with('notification')->paginate(10);

I need to be able to merge them to get back only one paginate(10) instance which has both notifications and subnotifications, so something like this for example (pseudocode):

$allNotifs = $user->(notifications()->where('type', 'UserWasFollowed'))
                  ->(subnotifications()->with('notification'))
                  ->paginate(10);

How can this be done with one pagination instance efficiently?

Update 1:

User Model

class User extends Authenticatable {
    use Notifiable;
    use HasSubnotifications;
}

Subnotification Model

class Subnotification extends Model {
    protected $table = 'subnotifications';

    // set up relation to the parent notification
    public function notification() {
        return $this->belongsTo(DatabaseNotification::class);
    }

    // Get the notifiable entity that the notification belongs to.
    public function notifiable() {
        return $this->morphTo();
    }
}

Query for user's:

a. Notifications of UserWasFollowed type from notifications table.

b. Subnotifications from subnotifications table with the related notification from notifications table.

$allNotifications = $user->subnotifications()->whereHas('notifications',function($query){
  $query->where('type', 'UserWasFollowed');
})->with('notification')->get();
Wonka
  • 8,244
  • 21
  • 73
  • 121
  • Need to have a look at the HasSubnotifications trait – Rezrazi May 15 '17 at 21:01
  • Step 5 of http://stackoverflow.com/questions/41274477/laravel-5-3-single-notification-for-user-collection-followers – Wonka May 15 '17 at 21:03
  • Replace 'notifications' with 'notification' in whereHas and with. I think it's just a naming issue, your relation in Subnotification is referred by notification method, and not notifications (typo). With and whereHas take the name of the method in arg – Rezrazi May 15 '17 at 21:10
  • Replaced both 'notifications' to 'notification' instead, but returns an empty array `[]` even though all the data is there. Not sure why it's so complicated for such a simple joining of both notifications/subnotifications on the user model, but not issues at all when done individually.. Also, if you look at https://laravel.com/docs/5.4/notifications under Accessing The Notifications, you'll see you call it as `notifications` off the user, we only do `notification` for the subnotification's related notification – Wonka May 15 '17 at 21:30
  • It should be empty, judging from your screenshots, you have no notifications typed UserWasFollowed for the user, try another type. There's no row for notififiable_id 5 and notification_id 1 – Rezrazi May 15 '17 at 21:32
  • The very first notification is typed UserWasFollowed – Wonka May 15 '17 at 21:34
  • But is there a row on subnotifications for notifiable 5 and notification 1 ? That was my question – Rezrazi May 15 '17 at 21:37

1 Answers1

2

Having not much information about the purpose of the subnotification table or what it does, you can try the following method :

public function your_method_name()
{
    /* IMPORTANT : Purpose of subnotification must be known to have a more structured query */
    $collection = $user->notifications()->where('type', 'UserWasFollowed')->get()->merge($user->subnotifications()->with('notification')->get());

    $currentPage = LengthAwarePaginator::resolveCurrentPage();

    $perPage = 10;

    $currentPageData = $collection->slice($currentPage * $perPage, $perPage)->all();

    $paginatedFinalCollection = new LengthAwarePaginator($currentPageData, count($collection), $perPage);

    return dd($paginatedFinalCollection);
}

Note Talking about efficiency, the intent of subnotification must be known, why do you need it and how are you going to use the data retrieved by your second query. Having an answer to that might make a difference to $collection


EDIT
The simple way I can think of, is using a closure within your eager loading with Like so :

$sn = $user->subnotifications()->with(['notification'=>function($query){
      $query->where('type','UserWasFollowed');
}])->paginate(10);

You can learn more about it at Laravel Eloquent Relationships under Constraining Eager Loads


UPDATE 2
try this

$user->subnotifications()->whereHas('notifications',function($query){
    $query->where('notification_type','UserWasFollowed');
})->with('notifications')->get();

Using this with a similar setup worked fine for me.
Note Be sure to change the relations names to their proper ones if they don't match

UPDATE 3
When using your exact same setup for subnotifications provided in the related question, with the following queries :

Notifications\TestNotification.php is similar to the SomethingCoolHappen.php in the example.

The model, channel and migrations are the same. So you can use them as they are.
What I did to get what you wanted is the following :

Route::get('/step1', function () {
    // example - my followers
    $followers = App\User::first();
    // notify them
    $x = Notification::send($followers, new TestNotification(['arg1' => 1, 'arg2' => 2]));
    dd($x);
});

Route::get('/step2', function () {
    // my follower
    $user = App\User::find(1);

    $subnotifications = $user->subnotifications()->whereHas('notification',function($query){$query->where('type','App\Notifications\TestNotification');})->with('notification')->get();

//this gives you the subnotifications collection with the notification included
dd($subnotifications);

//get the notification linked to one entry of the collection
dd($subnotifications->first()->notification()->first());

});
Rezrazi
  • 865
  • 1
  • 7
  • 18
  • 1
    Here is the subnotification intent: http://stackoverflow.com/questions/41274477/laravel-5-3-single-notification-for-user-collection-followers What are your thoughts on the efficiency after knowing the intent? Would anything in the query change to make it the most efficient way for a single paginator? – Wonka May 14 '17 at 19:23
  • Edited, test required – Rezrazi May 14 '17 at 19:56
  • Tested out the edit, didn't work due to syntax error, since `with` should enclose its content in `[]` (same doc you linked). But even with the correct syntax, it only pulls out the `subnotifications` with the subnotifications table fields, and adds a key of `notification` which is `null` for the subnotifications (so notification data is not added). It also does not add the regular `notification` of `UserWasFollowed` at all in the query results. Any other ideas that may make it work? – Wonka May 15 '17 at 17:55
  • Updated. Tested with a similar setup and worked for me. You can have access to the notification object normally. – Rezrazi May 15 '17 at 18:53
  • Hmmm... I added an update to my question showing the code with the correct relations, but get this error `Call to undefined method Illuminate\\Database\\Query\\Builder::notifications()` I am sure it's the whereHas `notifications` it is referring to in the error. I don't have that method on anything, since you just add notifiable on the user model, which I have and works individually when called `$user->notifications()->where('type', 'UserWasFollowed')->paginate(10);` but errors out when called using the updated syntax. – Wonka May 15 '17 at 19:26
  • The ´notification' in whereHas and with refer to the relation method, replace it with what you have in your model – Rezrazi May 15 '17 at 20:19
  • I updated with the question with the query/models. Notifiable on the User model is what allows us to do `$user->notifications()...` but it does not work from `whereHas` as it is added after the subnotifications. Changing the order doesn't work either. If you refer to 2) Pagination, I can paginate each relation I have individually like this: part, you will see they both access off the user model and work individually, but not when grouped like the update query syntax (as I think only one of them is able to pull of the data from the user model directly, but not when chained off the other model) – Wonka May 15 '17 at 20:49
  • Try to add a method to your User model as subnotifications() which refers to the relationship with the subnotification table and with notifiable_id as the foreign key, switch between BelongsTo and HasMany for the sake of trial. **edit: looking the trait the model has that method** – Rezrazi May 15 '17 at 21:04
  • Updated, test it – Rezrazi May 16 '17 at 12:54
  • Hello again, still didn't work, but I created the exact setup very minimally. I put it in chat last night, let me know if you got it or when you're on again :) – Wonka May 16 '17 at 16:29
  • check the discussion – Rezrazi May 16 '17 at 17:23
  • I am in chat now – Wonka May 16 '17 at 17:31