Search code examples
eloquentlaravel-5.4

Laravel Eloquent using 'with' and 'where'


Using Laravel 5.4

I am reaching out as I am having a difficult time wrapping my head around this and after searching all day on the internet (and stackoverflow) I have not found a good solution to my problem that works.

Basically, I have a user object, that queries a child object which in turn includes a child object and I need to filter with where on that grandchild object.

It looks like this:

User =>
    Pet(1) =>
        PetServiceItem <= ServiceItem(1)
        PetServiceItem <= ServiceItem(2)
        PetServiceItem <= ServiceItem(3)
    Pet(2) =>
        PetServiceItem <= ServiceItem(1)
        PetServiceItem <= ServiceItem(4)
        PetServiceItem <= ServiceItem(5)

I'll post the relevant parts of the information so someone can tell me how this might be done.

User Model

class User extends Authenticatable
{
    public function pets()
    {
        return $this->hasMany(Pet::class);
    }

}

Pet Model

class Pet extends Model
{
    protected $fillable = [
        'id',
        'user_id',
        ...];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function petServiceItems(){
        return $this->hasMany(PetServiceItem::class);
    }
}

PetServiceItem model

class PetServiceItem extends Model
{
    protected $fillable = [
        'pet_id',
        'service_item_id',
        'approved'
    ];

    protected $table = 'pet_service_item';

    public function pet()
    {
        return $this->belongsTo(Pet::class);
    }

    public function serviceItem()
    {
        return $this->belongsTo(ServiceItem::class);
    }
}

ServiceItem model

class ServiceItem extends Model
{
    protected $fillable = [
        'id',
        ...,
        'start_date',
        'end_date',
        '...',
    ];

    public function pets(){
        return $this->hasMany(PetServiceItem::class);
    }
}

Using Tinker I can do the following: $user->pets()->with(['petServiceItems', 'petServiceItems.service'])->get()

And get this data:

=> Illuminate\Database\Eloquent\Collection {#1118
     all: [
       App\Pet {#1120
         id: 1,
         user_id: 6,
         name: "Coco",
         slug: "carol!coco",
         image: "/dist/images/pets/carol/coco.jpg",
         breed: null,
         color: null,
         gender: "Female",
         birthdate: "2013-07-06 03:58:46",
         fixed: 0,
         weight: "48",
         licensed: "",
         tattoo: "",
         microchip: "",
         created_at: "2017-07-17 17:37:54",
         updated_at: "2017-07-17 17:37:54",
         petServiceItems: Illuminate\Database\Eloquent\Collection {#1126
           all: [
             App\PetServiceItem {#1132
               id: 1,
               provider_id: 2,
               pet_id: 1,
               service_item_id: 1,
               approved: 1,
               created_at: "2017-07-17 17:37:57",
               updated_at: "2017-07-17 17:37:57",
               serviceItem: App\ServiceItem {#1137
                 id: 1,
                 provider_id: 2,
                 type: "WALK",
                 subtype: "",
                 title: "7am 30min Walk between 7am and 10am",
                 desc: "Daily weekday walks between 7am and 10am",
                 day1: 0,
                 day2: 1,
                 day3: 1,
                 day4: 1,
                 day5: 1,
                 day6: 1,
                 day7: 0,
                 needs_approval: 0,
                 start_date: "2017-07-17 00:00:00",
                 end_date: "2017-10-17 00:00:00",
                 all_day: 0,
                 start_time: "07:00:00",
                 end_time: "10:00:00",
                 duration: 30,
                 pricing_one: 2000,
                 pricing_twoplus: 1800,
                 created_at: "2017-07-17 17:37:57",
                 updated_at: "2017-07-17 17:37:57",
                 deleted_at: null,
               },
             },
             App\PetServiceItem {#1134
               id: 3,
               provider_id: 2,
               pet_id: 1,
               service_item_id: 4,
               approved: 0,
               created_at: "2017-07-17 17:37:57",
               updated_at: "2017-07-17 17:37:57",
               serviceItem: App\ServiceItem {#1139
                 id: 4,
                 provider_id: 2,
                 type: "AGILITY",
                 subtype: "",
                 title: "10am Agility Tu/Th",
                 desc: "Agility class @ 10am Tuesdays and Thursdays for 90 minutes",
                 day1: 0,
                 day2: 0,
                 day3: 1,
                 day4: 0,
                 day5: 1,
                 day6: 0,
                 day7: 0,
                 needs_approval: 1,
                 start_date: "2017-07-17 00:00:00",
                 end_date: "2017-09-17 00:00:00",
                 all_day: 0,
                 start_time: "10:00:00",
                 end_time: "11:30:00",
                 duration: 90,
                 pricing_one: 5000,
                 pricing_twoplus: 4500,
                 created_at: "2017-07-17 17:37:57",
                 updated_at: "2017-07-17 17:37:57",
                 deleted_at: null,
               },
             },
           ],
         },
       },
       App\Pet {#1123
         id: 2,
         user_id: 6,
         name: "Ruby",
         slug: "carol!ruby",
         image: "/dist/images/pets/carol/ruby.jpg",
         breed: null,
         color: null,
         gender: "Female",
         birthdate: "2012-06-16 22:47:43",
         fixed: 1,
         weight: "53",
         licensed: "",
         tattoo: "",
         microchip: "",
         created_at: "2017-07-17 17:37:54",
         updated_at: "2017-07-17 17:37:54",
         petServiceItems: Illuminate\Database\Eloquent\Collection {#1119
           all: [
             App\PetServiceItem {#1133
               id: 2,
               provider_id: 2,
               pet_id: 2,
               service_item_id: 1,
               approved: 1,
               created_at: "2017-07-17 17:37:57",
               updated_at: "2017-07-17 17:37:57",
               serviceItem: App\ServiceItem {#1137},
             },
             App\PetServiceItem {#1135
               id: 4,
               provider_id: 2,
               pet_id: 2,
               service_item_id: 4,
               approved: 0,
               created_at: "2017-07-17 17:37:57",
               updated_at: "2017-07-17 17:37:57",
               serviceItem: App\ServiceItem {#1139},
             },
           ],
         },
       },
     ],
   }

Now I need to do a where clause on the ServiceItem for the start_date.

I tried:

$user->pets()->with(['petServiceItems', 'petServiceItems.serviceItem'])->where('service_items.start_date', '>=', '2017-01-01')->get()

But, I get this error:

Illuminate\Database\QueryException with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'service_items.start_date' in 'where clause' (SQL: select * from pets where pets.user_id = 6 and pets.user_id is not null and service_items.start_date >= 2017-01-01)'

How can I use the where clause (or something else if needed) to filter the data I need?

edited: I have figured out that this is the SQL that I want (or close enough approximation):

select * from users 
join (select * from pets) pet on users.id = pet.user_id
join (select * from pet_service_item) psi on psi.pet_id = pet.id
join (select * from service_items) si on si.id = psi.service_item_id
join (select * from providers) prov on prov.id = si.provider_id
where si.start_date >= '2017-07-17' 
  AND si.end_date <= '2017-10-18'
  AND prov.id = 2
  AND users.id = 6

Solution

  • So, I'm posting this as I never really did get an answer and I ended up with a solution, but not exactly the one I was looking for.

    If anyone is interested, in the end, this is how I did it (note: this flattened the data, which for all intents and purposes, worked fine in my scenario).

    $services = App\ServiceItem::where('provider_id', '=', $provider->id)
        ->where('user.user_id', '=', $user->id)
    
        ->where('start_date', '<=', $end_date)
        ->where('end_date', '>=', $start_date)
        ->orWhereNull('end_date')
    
        ->where('pet.pet_user_id', '=', $user->id)
        ->whereNull('service_items.deleted_at')
        ->whereNUll('pet.pet_deleted_at')
    
        ->join(DB::raw('(select pet_id as psi_pet_id, service_item_id as psi_service_item_id from pet_service_items) psi'), function($join) {
            $join->on('psi.psi_service_item_id', '=', 'service_items.id');
        })
    
        ->join(DB::raw('(select id as pet_id, user_id as pet_user_id, name as pet_name, deleted_at as pet_deleted_at from pets) pet'), function($join) {
            $join->on('pet.pet_id', '=', 'psi.psi_pet_id');
        })
    
        ->join(DB::raw('(select id as user_id, name as user_name, deleted_at as user_deleted_at from users) user'), function($join) {
            $join->on('user.user_id', '=', 'pet.pet_user_id');
        });
    
    return ['status' => 200, 'services' => $services->get()];