Laravel Eloquent ORM confusion

513 Views Asked by At

I am trying to achieve the following with Eloquent :

I want to query my DB, count the amount of rows where status = 'waiting' and 'inprogress', but I'm running into the following problem. If I run the get() and then try to count, I get told I can't on a non-object. If I try to run the get() after, I get this error : Undefined property: Laravel\Database\Query::$source.

Here are my two attempts :

//get() before
$devs = Dev::todo($user_id)->get(array('id', 'type', 'title', 'source', 'priority', 'status', 'for_user', 'priority', 'desc', 'created_at'));
$devs->num_waiting = $devs->where('status', '=', 'waiting')->count();
$devs->num_inprogress = $devs->where('status', '=', 'inprogress')->count();

//get() after
$devs = Dev::todo($user_id);
$devs->num_waiting = $devs->where('status', '=', 'waiting')->count();
$devs->num_inprogress = $devs->where('status', '=', 'inprogress')->count();
$devs->get(array('id', 'type', 'title', 'source', 'priority', 'status', 'for_user', 'priority', 'desc', 'created_at'));

My todo function :

public static function todo($user_id) {

    $todo = Dev::where('for_user', '=', $user_id)
               ->where(function($query) {
                        $query->where('status', '=', 'active')
                              ->or_where('status', '=', 'inprogress')
                              ->or_where('status', '=', 'waiting');
                })
               ->order_by('priority', 'asc')
               ->order_by('created_at', 'desc');

    return $todo;
}

How can I run get() after counting the data I need to count, why is this happening, and is there a better way to do this?

2

There are 2 best solutions below

2
On

The problem here is get() returns a Collection which does not contain a where() method so you need to do where() before get(). Fortunately, your get() seems to be taking place of a select() so you should use that instead.

Try this...

$devs = Dev::todo($user_id)->select(array('id', 'type', 'title', 'source', 'priority', 'status', 'for_user', 'priority', 'desc', 'created_at'));

Now you should be able to run your counts off it, but doing it twice isn't going to work because the second count time will be counting waiting and inprogress. We can create another object for finding the other count, but it's also not very good on performance because you are running basically the same query 3 times. Two for counts, and one for returning the actual collection. What I would do is get your collection, and count it using php as you iterate over it.

$devs = Dev::todo($user_id)
    ->select(array('id', 'type', 'title', 'source', 'priority', 'status', 'for_user', 'priority', 'desc', 'created_at'))
    ->get();


$nWaiting = 0;
$nProgress = 0;
foreach($devs as $dev) {

    switch($dev->status) {
        case 'waiting':
            $nWaiting++;
            break;

        case 'inprogress':
            $nProgress++;
            break;

        default:
            break;
    }
}
    $devs->num_waiting = $nWaiting;
    $devs->num_inprogress = $nProgress;

This code is untested though because I'm not sure what you are doing with your todo() function.

0
On

Without looking at the Eloquent source code, I'd guess that count() is based on the SQL aggregate function COUNT(). Aggregate functions return, well, aggregate results. They don't return the rows that make up the aggregate.

I'd expect this line (from your code) to give you the count.

$devs->num_waiting = $devs->where('status', '=', 'waiting')->count();

If I needed the rows that make up the count, I'd do one of these things, depending on the application.

  1. Avoid the count() method, get() the result set, and count the items in the set. Off the top of my head, I think get() returns an object of type "collection". Look for a method that returns the size of the collection.
  2. Run a second query with the same where() arguments, and live with the consequences of concurrency and race conditions.