Home > Software engineering >  Laravel (Lumen) Eloquent querying with WHERE on a relation (Multi-level)
Laravel (Lumen) Eloquent querying with WHERE on a relation (Multi-level)

Time:02-11

I have the following DB tables:

Purchase
-id
-workplace_id

Workplace
-id
-client_id

(and obviously some more fields, but for the example these are all the needed ones). I would like to make a query like this:

SELECT * FROM
  purchase
  INNER JOIN workplace ON (purchase.workplace_id = workplace.id)
WHERE
  (workplace.client_id = 1)

I'm trying to make this work with the Eloquent models, but I can't figure out how to filter on a joined table. I tried:

    $purchases = Purchase::query()
        -> workplace()
        -> where('client_id', '=', Auth::user() -> client_id)
        -> get();

But apparently workplace() is undefined for some reason.

My Purchase.php model file looks like this:

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Purchase extends Model
{
    public function workplace(): \Illuminate\Database\Eloquent\Relations\BelongsTo
    {
        return $this->belongsTo(Workplace::class);
    }
}

Any pointers on how to make this simple select work?

Thanks!

====EDIT=====

I found a possible solution:

    $purchases = Purchase::with('workplace')
        -> whereHas('workplace', function($q) {
            return $q -> where('client_id', '=', Auth::user() -> client_id);
        })
        -> get();

But this generates an SQL that seems more complicated and is probably also slower:

select * from `purchases` where exists (select * from `workplaces` where `purchases`.`workplace_id` =
`workplaces`.`id` and `client_id` = ? and `workplaces`.`deleted_at` is null)

So I'm still looking for better alternatives

CodePudding user response:

If you want to do a join, you need to build it with the join method, you can't use a relationship. See here for the docs:

https://laravel.com/docs/9.x/queries#joins

So you should be able to do something like this:

$purchases = Purchase::query()
    ->join('workplace', 'purchase.workplace_id', '=', 'workplace.id')
    ->where('workplace.client_id', '=', Auth::user()->client_id)
    ->get();

The generated SQL that you show in your edit is a sub-query, and you may still want to consider that. Sure, sub-queries are often slower than joins, but unless you're dealing with a massive dataset the performance difference might be negligible, and it allows you to use native Eloquent relationships.

See here for a discussion on this: https://stackoverflow.com/a/2577188/660694

  • Related