1

I have a polymorphic relation where class (Request) can have a relationship with either class (Leave) or class (Overtime).

Each object of class (Request) belongs to a user.

I would like to setup a relationship in the User class to directly get all of their Leave or Overtime objects.


Here is how the code looks:

  • Class Request with:
    • user_id
    • requestable_id
    • requestable_type can be App\Leave or App\Overtime
    class Request extends Model
    {
        public function requestable() {
            return $this->morphTo();
        }

        public function user() {
            return $this->belongsTo('App\User');
        }
    }

  • Class Leave
    class Leave extends Model
    {
        public function request() {
            return $this->morphOne('App\Request', 'requestable');
        }
    }

  • Class Overtime
    class Overtime extends Model
    {
        public function request() {
            return $this->morphOne('App\Request', 'requestable');
        }
    }

  • Class User
    class User extends Authenticatable
    {
        public function requests() {
            return $this->hasMany(Request::class);
        }

        public function leaves() {
            // Need help here
        }

        public function overtimes() {
            // And here
        }
    }


What I would like to do is get all leaves and overtimes a user has, so ultimately I should be able to do this:

    $userLeaves = $user->leaves;
    $userOvertimes = $user->overtimes;
  • Is the hasManyThrough relationship what you're looking for? https://laravel.com/docs/5.8/eloquent-relationships#has-many-through. Or you can use ->hasMany()->where(); – LucyTurtle Apr 09 '19 at 18:49
  • Could you give it a try?, I dont think it works with polimorphic relations – user1717018 Apr 09 '19 at 21:15

3 Answers3

0

Seems that you need a combination of polymorphic relation (that you've already defined) and hasManyThrough. return $this->hasManyThrough(Leave::class, Request::class); and return $this->hasManyThrough(Overtime::class, Request::class); respectively. But check foreign and local keys (see more info here).

Petr
  • 420
  • 3
  • 13
  • Yes, I believe that hasManyThrough is the solution but no matter the how many combinations I try with foregin/local keys I could not get it to work, could maybe give it a try? – user1717018 Apr 09 '19 at 21:11
  • I haven't used this kind of relations ever. Maybe you need to specify type of a related polymorphic model. Try to use [this](https://stackoverflow.com/a/49518036/8373044). – Petr Apr 09 '19 at 21:24
0

you can fetch the user leaves and overtimes through the user requests by using

$requests = $user->requests->with('requestable');

but this will fetch all user requests not depending on the type, however you can fetch them depending on the type by using the leaves and overtimes function if you want and specifying the type there

User Class

public function leaves()
{
    return $this->requests->where('requestable_type', 'App\Leave');
}

public function overTimes()
{
    return $this->requests->where('requestable_type', 'App\OverTime');
}
0

Answering my own question.

Using hasManyThrough:

public function leaves() {
    return $this->hasManyThrough(
        Leave::class, // the class that we want objects from
        Request::class, // the class sitting between this one and the target
        'user_id', // this class's foreign key in the request class
        'id', // foreign key in leave class
        'id', // local key in this class
        'requestable_id' // key of the leave in the request class
        )
        // we have to limit it to only Leave class
        ->where('requestable_type', array_search(Leave::class, Relation::morphMap()) ?: Leave::class);
}

public function overtimes() {
    return $this->hasManyThrough(
        Overtime::class, // the class that we want objects from
        Request::class, // the class sitting between this one and the target
        'user_id', // this class's foreign key in the request class
        'id', // foreign key in overtime class
        'id', // local key in this class
        'requestable_id' // key of the overtime in the request class
        )
        // we have to limit it to only overtime class
        ->where('requestable_type', array_search(Overtime::class, Relation::morphMap()) ?: Overtime::class); 
}