Home > Back-end >  Laravel user model conditional eager loading (with)
Laravel user model conditional eager loading (with)

Time:10-27

I am working on a hybrid app build with Laravel and Vue. I have a use case where not all users have certain relations. For example a client can have a Domain and Multiple Business Units.

Currently i have set it up like this:

<?php

namespace App\Models;

use Laravel\Sanctum\HasApiTokens;
use Spatie\MediaLibrary\HasMedia;
use Illuminate\Notifications\Notifiable;
use Lab404\Impersonate\Models\Impersonate;
use Spatie\MediaLibrary\InteractsWithMedia;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements HasMedia
{
  use Traits\BaseModelTrait;
  use Traits\ActiveTrait;

  use InteractsWithMedia;
  use Impersonate;
  use HasApiTokens;
  use Notifiable;
  use HasFactory;

  protected $hidden = [
    'password', 'remember_token',
  ];

  protected $fillable = [
    'name', 'email', 'password', 'avatar',
  ];

  protected $casts = [
    'settings' => AsArrayObject::class,
    'is_admin' => 'boolean',
  ];

  protected $with = [
    'domain',
    'BusinessUnits'
  ];

  public function scopeAdmin($query)
  {
    return $query->where('is_admin', true);
  }

  public function scopeEmployee($query)
  {
    return $query->whereNull('domain_id');
  }

  public function scopeClient($query)
  {
    return $query->whereNotNull('domain_id');
  }

  public function BusinessUnits()
  {
    return $this->belongsToMany(BusinessUnit::class, 'users_business_units_pivot');
  }

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

The "problem" with this approach is that for every request 2 queries are executed for each user. I want the relations eager loaded only if the "domain_id" is not null (scopeClient). For normal "models" i can select per page what models should be loaded etc., but for the authenticated user this is not really possible as i know.

I think i am looking for something like this:

  protected $with = [
    (!$this->domain_id) ? 'domain' : null,
    (!$this->domain_id) ? 'BusinessUnits' : null
  ];

This currently generates an error: "Constant expression contains invalid operations."

Any advice and or ideas to tackle this would be appreciated!

CodePudding user response:

You can try using events:

// this code should be inside your model
public static function boot()
{
    parent::boot();

    self::retrieved(function($model){
        if($model->domain_id !== null)
        {
             $model->load('domain', 'BusinessUnits');
        }
    });
}

and obviously, you have to remove those relations from $with

CodePudding user response:

To get all the user that has domains, use whereHas()

$users = User::whereHas('Domain')->with(['Domain', 'BusinessUnits'])->get();

it will lauch 3 queries, one for the users, one for the domains and one for the business units.

  • Related