Search code examples
phplaravelmongodbeloquentphp-mongodb

Laravel eloquent how to eager load and get relations in seperate array from collection of collections


I have a problem with getting parent and child records in a separate array. For instance, I have 2 models :

1) Tournament (parent): Tournament has many events

 public function events(){

    return $this->hasMany(Event::class);
 }

2) Event (child): Events belong to a tournament

  public function tournament(){

      return $this->belongsTo(Tournament::class);
  }

I filter events based on parameters from a request that send to the controller. filters are 'away_team','home_team','tournament_id'. I filter out the events and after that try to eager load the tournament that the event belongs to, alongside with it.

After that, I try to get the tournament and put them into array separately. But it throws memory allowed error.

I know I can increase the memory in php.ini but I want to take the tournaments in the best way - preferably eloquent query - and less memory usage. I already have tournaments in events but the problem is events are in a collection so how can I manage to get both events and unique tournaments in a separate collection based on request input query.

Here is the code I manage to do so far. I know it works but it throws an error because I have 10,000 or more records:

public functions filter(Request $request){

    $query = App\Model\Event::query();

        if ($request->has('away_team')) {
            $query = $query->where('away_team.name', 'like', '%' . $request->away_team . '%');
        }

        if ($request->has('home_team')) {
            $query = $query->where('home_team.name', 'like', '%' . $request->home_team . '%');
        }

        if ($request->has('tournament_id')) {
                $query = $query->where('tournament_id', new ObjectID($request->tournament_id));
        }

        //events collection with tournament
        $events = $query->with('tournament')->get();


        //here I get Allowed memory error
        $tournaments = [];
        foreach ($events as $event) {
            $tournaments[] = $event->tournament;
        }

        $tournaments = array_unique($tournaments);

}

So, what's the solution to get these model records into separate collections based on these conditions:

1) Events must match $request filters

2) Tournaments must be unique and belong to DB events result

PS: I use PHP-mongo and Laravel eloquent for mongo db

PS2: I know mongo is not relational but you can use it relation like somehow

Any help would be appreciated! Thanks in advance.


Solution

  • You can split these requests in two, however I'm not sure if it makes things less memory hungry.

    public functions filter(Request $request)
    {
    
        $eventQuery = App\Model\Event::query();
        $tournamentQuery = App\Model\Tournament::query();
    
            if ($request->has('away_team')) {
                $eventQuery = $eventQuery->where('away_team.name', 'like', '%' . $request->away_team . '%');
            }
    
            if ($request->has('home_team')) {
                $eventQuery = $eventQuery->where('home_team.name', 'like', '%' . $request->home_team . '%');
            }
    
            if ($request->has('tournament_id')) {
                    $eventQuery = $eventQuery->where('tournament_id', new ObjectID($request->tournament_id));
            }
    
            //events collection with tournament
            $events = $eventQuery->get();
    
            // Get only tournaments that belongs to selected events.
            $tournaments = $tournamentQuery->whereIn('id', $events->pluck('tournament_id')->all())->get()
    
    }
    

    By the way, I think, if tournaments own events, you should call hasMany() in your Tournament class event() method and in the Event class the other way around.

    There is also possibility to set limit for the number of tournaments that will be loaded with each event. Just change your last query like this:

    public function filter(Request $request)
    {
        // ... your code
    
        $events = $query->with('tournament', function ($query) {
            $query->limit(10); 
        })->get();
    
       // ... rest of your code
    
    }