Search code examples
phplaraveleloquenteloquent-relationshiphas-many-polymorphs

Is it possible to define a polymorphic relationship through different paths (intermediate model) in Laravel models?


I have the models "Payment", "Estimate" and "Quote", a payment has a polymorphic relationship where it can belong either to a quote or to an estimate, simultaneously a quote can have many estimates, so an estimate is always going to belong to a quote.

Basically, all payments are always going to belong to a quote, sometimes directly and sometimes through an estimate:

quotes:
    id 

estimates:
    id
    quote_id 

payments:
    id 
    paymentable_id 
    paymentable_type ("App\Models\Quote" or "App\Models\Estimate")

My question is, how can I define the relationships so when I use $quote->payments, it would not only return the payments that are directly related to the quote but also the payments that are related to the estimates that belong to that quote.

Any suggestions? Thanks in advance.


Solution

  • yes it is possible, you can try something like bellow:

    define three kind of relation:

    first for direct payment as follow:

    public function directPayments(){
        return $this->morphMany(Payment::class,"paymentable");
    }
    

    and another to retrieve indirects with hasManyThrough:

    /**
    * returns models, not relation 
    */
    public function indirectPayments(){
        return  $this->hasManyThrough(
                                        Payment::class,
                                        Estimate::class,
                                        'quote_id',
                                        'paymentable_id',
                                        'id',
                                        'id')
                      ->where('paymentable_type',"App\Models\Estimate");
    }
    

    and you can have a function to aggregate them

    public function getPaymentsAttribute(){
    //merge or union in collections
        return $this->directPayments->union($this->indirectPayments()->get());
    }