Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

smarko's avatar

Help Required With Querying a Relation

Hey everyone. I've encountered an issue, and I can't get my head around it, so I am seeking some help. First, a bit of info.

I have 3 tables: schedules, aircraft_types and aircraft.

Schedules and aircraft types are in a manyToMany relationship. Each schedule can have as many aircraft types, and each aircraft type can belong to as many schedules as required. Aircraft belong to an aircraft type - so, a many to one relationship.

Aircraft table contains aircraft_type_id And there is a schedule_aircrafttypes table containing aircraft_type_id and schedule_id

Here is the relevant code:

...
//Schedule.php model

    public function aircraftTypes() {
        return $this->belongsToMany(AircraftType::class, 'schedule_aircrafttypes');
   
 }
...
...
//AircraftType.php model

    public function aircraft() {
        return $this->hasMany(Aircraft::class);
    }

    public function schedules() {
        return $this->belongsToMany(Schedule::class, 'schedule_aircrafttypes', 'aircraft_type_id');
    }
   
 }
...
//Aircraft.php model
    public function type() {
        return $this->belongsTo(AircraftType::class, 'aircraft_type_id');
    }

In a way, a single aircraft can belong to as many schedules through its aircraft_type. What I'm trying to achieve is to get all the schedules which have aircraft which satisfy a few conditions. Here is my current code, which works, but I'd like to improve on it, and actually get the correct result by a query/Eloquent instead of going through the "partially prepared" results and taking out the ones that do not meet the conditions.

Current code:

//Schedule.php model

    public function getAvailableAircraft() {
        $ids = [];
        $pilotRankOrder = Auth::user()->rank->rank_order;
        $types = $this->aircraftTypes()->whereHas('minrank', function ($query) use ($pilotRankOrder) {
                $query->where('rank_order', '<=', $pilotRankOrder);
        })->get();
        foreach($types as $type) {
            $ids[] = $type->id;
        }
        $a = Aircraft::where('location_id', $this->departure_id)->where('booked', false)->whereIn('aircraft_type_id', $ids)->with('type')->get();
        return $a;
    }

and inside my view:

@foreach($schedules as $schedule) 
                            <?php if ($schedule->getAvailableAircraft()->count() == 0) continue; ?>

$schedules is defined by $schedules = Schedule::getAllBasedOnSettings();

    public static function getAllBasedOnSettings($paginate = false) {
        $schedules = self::getBasedOnSettings()->with('departure', 'arrival','airline');
        if($paginate) 
            return $schedules->paginate(50);
        return $schedules->get();       
    }

    public static function getBasedOnSettings() {
        $schedules = self::select();
        if(Setting::get('LIMIT_SCHEDULES_BY_DAYS')) {
            $schedules = $schedules->where(date('l'), true);
        }

        if(Setting::get('LIMIT_SCHEDULES_BY_RANK')) {
            $pilotRankOrder = Auth::user()->rank->rank_order;
            $schedules = $schedules->whereHas('minrank', function ($query) use ($pilotRankOrder) {
                $query->where('rank_order', '<=', $pilotRankOrder);
            });
        }
        if(Setting::get('LIMIT_SCHEDULES_BY_LAST_LOCATION')) {
            $schedules = $schedules->where('departure_id', '=', Auth::user()->location_id);
        }       

        return $schedules;
    }

Question ultimately is what is the best way to optimize the code, so I don't do n*numberOfSchedules queries extra in the view, but rather get the proper query result in the first place.

I've tried a few things but didn't get far.

Sorry if I'm too confusing. I'd be happy to clarify, as English is not my first language. :)

Thanks a bunch!

0 likes
1 reply
smarko's avatar

I've managed to do it in Query Builder, but does not return the model instance, making me require to revwirte all of my view, and I don't think it is as elegant of a solution.

Here is the raw query. Any help still appreciated.

select distinct `schedules`, TIME_TO_SEC(TIMEDIFF( arrivaltime, departuretime)) as duration from `schedules` 
left join `schedule_aircrafttypes` on `schedules`.`id` = `schedule_aircrafttypes`.`schedule_id` 
left join `aircraft_types` on `schedule_aircrafttypes`.`aircraft_type_id` = `aircraft_types`.`id` 
left join `aircraft` on `aircraft_types`.`id` = `aircraft`.`aircraft_type_id` 
where `departure_id` = 29 and `aircraft`.`location_id` = schedules.departure_id and `aircraft`.`booked` = false and aircraft_types.id in (SELECT aircraft_type_id FROM schedule_aircrafttypes
                                                                            JOIN aircraft_types ON schedule_aircrafttypes.aircraft_type_id = aircraft_types.id \n
                                                                            JOIN ranks ON aircraft_types.minrank_id = ranks.id 
                                                                            WHERE rank_order <= 1)

Please or to participate in this conversation.