7
votes

I have two models, App\Song (belongsTo App\Host) and App\Host (hasMany App\Song).

I have the following query in my Controller:

$songs = Song::whereHas('host', function($query) {
                $query->where('skip_threshold', '>', \DB::raw('songs.attempts'))
                      ->where('active', 1);
            })
->whereNull('downloaded')
->get();

For reusability I would like to turn into a query scope(s).

I'm quite new to Eloquent so I'm not sure this is the correct way to do this being that its two Models as its not returning any results (where there should be).

Song.php

public function scopeEligable($query)
{
    $query->where('skip_threshold', '>', \DB::raw('songs.attempts'));
}

public function scopeActiveHost($query)
{
    $query->where('active', 1);
}

public function scopeInDownloadQueue($query)
{
    $query->whereNull('downloaded');
}
2
What's the code in your controller that you're using to call those query scopes? (that aren't returning results?)dangel
Song::eligable()->activehost()->indownloadqueue()->get();InvalidSyntax
you should have return in all your scopes.. return $query->whereNull('downloaded');. Scopes should always return a query builder instance. laravel.com/docs/5.1/eloquent#query-scopesdangel
I have updated the scopes to return query but no data is still returnedInvalidSyntax
you probably need to take a look at the sql directly. do you have DebugBar installed? if not try this before your query call... \Event::listen('illuminate.query', function($query, $params, $time, $conn) { dd(array($query, $params, $time, $conn)); });dangel

2 Answers

13
votes

You should put scopes into Models they belong to. Looking at your initial query scopes scopeEligable and scopeActiveHost belongs to Host model, so you should move them into Host model and then you'll be able to use your query using scopes like this:

$songs = Song::whereHas('host', function($query) {
   $query->eligable()->activeHost();
})->inDownloadedQueue()->get();

and as already pointed in comment you should add return to each scope so they could be used as they intended.

EDIT

If you would like to make using it shorter, you could create new relationship in Song model:

public function activeHost() 
{
    return $this->belongsTo(Host:class)->eligable()->activeHost();
}

so now, you could write:

$songs = Song::whereHas('activeHost')->inDownloadedQueue()->get();
1
votes

I think you're mistaken about 2 models. I think this should work

Song.php

public function scopeEligable($query, $active) {
   return $query->whereHas('host', function($q) {
       $q->where('skip_threshold', '>', \DB::raw('songs.attempts'))->where('active', $active);
   })
}

public function scopeInDownloadQueue($query)
{
   $query->whereNull('downloaded');
}

Usage

$songs = Song::eligable(true)->inDownloadQueue()->get();