staudenmeir / eloquent-has-many-deep

Laravel Eloquent HasManyThrough relationships with unlimited levels
MIT License
2.67k stars 157 forks source link

Help: HasManyDeep for Polymorphic many-to-many #172

Closed Aniket-IN closed 2 years ago

Aniket-IN commented 2 years ago

Hi,

First of all thank you so much for this awesome package. I need a lil help.

I've Order model and Order hasMany OrderItem and Shipment like this:

    // Order model
    public function items()
    {
        return $this->hasMany(OrderItem::class);
    }
    public function shipments()
    {
        return $this->hasMany(Shipment::class);
    }

And items and shipments has Cancel whcih is a polymorphic many to many relation.

    // Item and Shipment model
    public function cancels()
    {
        return $this->morphToMany(Cancel::class, 'cancelable');
    }

Now, I want to aceess all cancels directly from Order. Is that possible?


I've tried something like this:

    // Order model
    public function cancels()
    {
        return $this->hasManyDeep(
            Cancel::class,
            [OrderItem::class, 'cancelables'],
            [null, ['cancelable_type', 'cancelable_id'], 'id'],
            [null, null, 'cancel_id']
        );
    }

It works but returns cancels those are through OrderItem, but I want to get all cancels via OrderItem and Shipment together.

In addition, the current setup returns same Cancel multiple times, cause same Cancel belongs to many OrderItem.

Thank you so much in advance :)

staudenmeir commented 2 years ago

Hi @Aniket-IN,

This is actually not something the package can do: It (only) allows you to concatenate/chain relationships (A -> B -> C) but not to merge them – that's a different (and more complicated) topic. I also created a package for this: https://github.com/staudenmeir/laravel-merged-relations

Combine both packages:

use Staudenmeir\LaravelMergedRelations\Facades\Schema;

Schema::createMergeView(
    'all_cancels',
    [(new Order)->itemCancels(), (new Order)->shipmentCancels()]
);
class Order extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
    use \Staudenmeir\LaravelMergedRelations\Eloquent\HasMergedRelationships;

    public function itemCancels()
    {
        return $this->hasManyDeep(
            Cancel::class,
            [OrderItem::class, 'cancelables'],
            [null, ['cancelable_type', 'cancelable_id'], 'id'],
            [null, null, 'cancel_id']
        );
    }

    public function shipmentCancels()
    {
        return $this->hasManyDeep(
            Cancel::class,
            [Shipment::class, 'cancelables'],
            [null, ['cancelable_type', 'cancelable_id'], 'id'],
            [null, null, 'cancel_id']
        );
    }

    public function allCancels()
    {
        return $this->mergedRelationWithModel(Cancel::class, 'all_cancels');
    }
}
Order::find($id)->allCancels;

In addition, the current setup returns same Cancel multiple times, cause same Cancel belongs to many OrderItem.

If the allCancels relationship itself works, try creating the view with createMergeViewWithoutDuplicates() instead of createMergeView(). I'm not totally sure if that works in your case, but we'll find another solution if it doesn't.

Aniket-IN commented 2 years ago

Thank you @staudenmeir ,

This is exactly what I wanted, Honestly I'm a noob and had no idea about SQL Views.

Now I gotta learn more about SQL Views :)

So, just to confirm here's what I did:

as you suggested using createMergeViewWithoutDuplicates is not returning and duplicated results :)

And it works as expected. I can not thank you enough.