6

I have 2 models in my app:

1. Customer.php

2. Car.php

Now I would like to run a query that returns all customers that have less than 2 cars. Where 2 is a number that can be changed by the user.

I have tried this but it didn't work, it just returns all customer records:

$customers = Customer::whereHas("cars", function($query) {
    $query->selectRaw("count(*) < ?", [2]);
})
->get();

Edit: The two models are linked in a pivot table, meaning A customer can have more than 1 car and a Car can belong to more than 1 customer.

user3718908x100
  • 7,939
  • 15
  • 64
  • 123

4 Answers4

34

Use this:

$customers = Customer::withCount('cars')
    ->having('cars_count', '<', 2)
    ->get();
Jonas Staudenmeir
  • 24,815
  • 6
  • 63
  • 109
5

So , here is the result.

Relation in model Customer.php

public function cars()
{
    return $this->belongsToMany('App\Car','car_customer','car_id','customer_id');
}

Query to get all customers with N cars:

 $userInput = 2;
 $data = Customer::with('cars')
                ->withCount('cars')
                ->has('cars', '<', $userInput)
                ->orderBy('cars_count', 'desc')
                ->get();

Where the $userInput is your 'N'.

Mahmoud Abdelsattar
  • 1,299
  • 1
  • 15
  • 31
Dorin Musteața
  • 162
  • 1
  • 12
  • 1
    This will work, but Jonas' solution is strictly better since this one executes 1 more subquery. – DevK Apr 29 '18 at 00:24
  • 1
    Jonas' soution will say that there is no column 'cars_count' – Dorin Musteața Apr 29 '18 at 00:36
  • Not if the relation is set up correctly. If it isn't this won't work either. Also, why would you expect `->orderBy('cars_count')` to work if there's no column `cars_count`? As for the explanation, `->withCount('cars')` adds a `cars_count` to the select. It makes a subquery that counts the relation. – DevK Apr 29 '18 at 00:40
  • Thanks, this worked, @devk was right, Jonas's answer didn't work for me initially since my relations were not setup correctly, thanks for your help o/ – user3718908x100 Apr 29 '18 at 13:04
  • 1
    @user3718908 ,**whereHas()** is the same as **has()** , but **whereHas()** allows to specify additional filters on related model , **has()** filters selecting model based on a relation. – Dorin Musteața Apr 29 '18 at 13:07
  • @DevK what do you mean by if the relation is 'set up correctly'? Mine is a simple `belongsToMany` and I am getting the 'undefined column` error. Query looks like `Group::withCount('users')->having('users_count', '<', '2')->get();` – Stetzon Jul 17 '19 at 18:46
  • Note that both withCount() and has() insert the same subquery into the sql and so it is run twice. `withCount()` adds as an alias select: `select (select count(*) ...) as cars_count`. And `has()` adds as where: `where (select count(*) ...) < N`. I would only use `has()` to prevent double sql evaluation. – Ken Sep 09 '20 at 13:31
2

This is the best way:

$customers = Customer::has('cars','<', 2)->get();
0

Have you tried this approach?

$input = 2;
$customers = Customer::whereHas("cars", function($query) use ($input) {
    $query->where(DB::raw("count(cars.id)"), "<", DB::raw($input))
})->get();
Mahmoud Abdelsattar
  • 1,299
  • 1
  • 15
  • 31
Dorin Musteața
  • 162
  • 1
  • 12