Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

BasV's avatar
Level 1

Combining multiple hasManyThrough outputs

I have an application with multiple types of services which can be resold by a reseller to an end customer. For example I have the following models:

  • DomainRegistration - domain registration services which can be resold to a customer by a reseller.
  • WebhostingAccount - webhosting services which can be resold to a customer by a reseller.
  • Reseller - the resellers.
  • Customer - the end customer.

Both the DomainRegistration and WebhostingAccount models have the reseller relationship and a customer relationship:

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

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

The Reseller model has it's reverse (hasMany) defined for both the DomainRegistration and WebhostingAccounts models.

Now I would like to add a function to the Reseller model to show all end customers the reseller has services for. So it should look for end customers with webhosting accounts and domain registrations, combined into one response without double customers.

It would in the ideal situation probably look something like this, but of course not with 2 return statements but one return statement returning the combined output:

public function customers()
{
    return $this->hasManyThrough('App\Customer', 'App\DomainRegistration');
    return $this->hasManyThrough('App\Customer', 'App\WebhostingAccount');
}

How can I do this?

1 like
6 replies
kevinbui's avatar

Can you post your database structure for those tables?

BasV's avatar
Level 1

Does that matter?

Hereby part of the structure though, those are just the parts you probably need for the answer.

domain_registrations:
- id
- domain
- reseller_id
- customer_id
- ...
- ...

webhosting_accounts:
- id
- domain
- reseller_id
- customer_id
- ...
- ...

resellers:
- id
- name
- ...
- ...

customers:
- id
- name
- ...
- ...
BasV's avatar
BasV
OP
Best Answer
Level 1

I found a way to do this, however, I don't think this is the best way. I created the reseller->customers function using this custom attribute:

public function getCustomersAttribute()
{
    return $this->domainRegistrations()->whereHas('customer')->with('customer')->get()->pluck('customer')
    ->union($this->webhostingAccounts()->whereHas('customer')->with('customer')->get()->pluck('customer'))->unique();
}
kevinbui's avatar

Cool, that definitely works. I want to propose a few other alternatives that could be a bit cleaner and more performant.

And I don't think hasManyThrough is gonna work though. There are not multiple layers of hasMany relationships in this scenario.

SOLUTION 1:

You might create a Many To Many relationships between resellers and customers, taking advantage of the domain_registrations and webhosting_accounts table. Then

class Reseller extends Model
{
    public function drCustomers()
    {
        return $this->belongsToMany(Customer::class, 'domain_registrations');
    }

    public function waCustomers()
    {
        return $this->belongsToMany(Customer::class, 'webhosting_accounts');
    }

    public function getCustomers()
    {
        return $this->drCustomers
            ->merge($this->waCustomers)
            ->unique();
    }
}

SOLUTION 2:

You might use Many To Many polymorphic relationships. For this, the database got to be redesigned:

domain_registrations:
- id
- domain
- ...

webhosting_accounts:
- id
- domain
- ...

serviceables:
- id
- reseller_id
- customer_id
- serviceable_id
- serviceable_type
- ...

customers:
- id
- name
- ...
// This is not important, just to give an idea how your models will work with the new design.
class DomainRegistration extends Model
{
    public function customers()
    {
        return $this->morphToMany(Customer::class, 'serviceable');
    }
}

class WebhostingAccount extends Model
{
    public function customers()
    {
        return $this->morphToMany(Customer::class, 'serviceable');
    }
}

class Customer extends Model
{
    public function domainRegistrations()
    {
        return $this->morphedByMany(DomainRegistration::class, 'serviceable');
    }
    public function webhostingAccounts()
    {
        return $this->morphedByMany(WebhostingAccount::class, 'serviceable');
    }
}

Then from the reseller, you can simply say:

class Reseller extends Model
{
    public function customers()
    {
        // Got to call distinct() because serviceables table will contain duplicate pairs of reseller_id and customer_id.
        return $this->belongsToMany(Customer::class, 'serviceables')->distinct();
    }
}

Please or to participate in this conversation.