0

I have the following relationships:

  • Users have polymorphic many to many (morphedByMany) relationships with Customers, Locations, and Vendors.
  • Customers, Locations, and Vendors each have hasMany or hasManyThrough relationships with Datasets.

I'd like to get the Datasets that a given User has access to via its relationships. Also, some of the datasets through the different relationships might be the same, so I want a unique list.

I created the following method on my User model that works correctly (inspired by Laravel get a collection of relationship items):

public function accessibleDatasets()
{
    $datasetsByCustomers = $this->customers()->with('datasets')->get()->pluck('datasets')->flatten();
    $datasetsByLocations = $this->locations()->with('datasets')->get()->pluck('datasets')->flatten();
    $datasetsByVendors   = $this->vendors()->with('datasets')->get()->pluck('datasets')->flatten();

    $datasets            = $datasetsByCustomers;
    $datasets            = $datasets->merge($datasetsByLocations);
    $datasets            = $datasets->merge($datasetsByVendors);

    return $datasets->unique();
}

Is there a "right way" or more efficient way to get this (besides using some sort of a reduce function for the merges)? Is loading the models, then flattening better? The associated values aren't going to change too often, so I can cache the results, but wanted to get some feedback.

jloosli
  • 2,461
  • 2
  • 22
  • 34
  • I wonder if you could query your `datasets` table directly, then using a string of `->whereHas()/orWhereHas()`, constrain it to `customers`, `locations` or `vendors` that have a relationship to the current User (using nested `->whereHas()` queries). Not sure about the efficiency side though. – Tim Lewis Sep 14 '18 at 18:23
  • @TimLewis Yeah...that's a good point...I may play around with that to see if one is more efficient than the other. One thing I do like about my existing method is that it's verbose enough that I think it conveys to a developer what is really happening. – jloosli Sep 14 '18 at 19:53
  • At a glance, yeah, it's pretty easy to see what you're attempting to accomplish. Don't know if I'd concern myself with that point of view too much, but depends on your coding situation. But anyway, if you get stuck with the `->whereHas()` method, feel free to update your question :) – Tim Lewis Sep 14 '18 at 19:56

1 Answers1

1

The most efficient way would be by performing a single database query as Tim already mentioned.

Since you can cache the results and should you want to spare some lines of code I'd rather do:

public function accessibleDatasets()
{
    $this->load('customers.datasets', 'locations.datasets', 'vendors.datasets');

    return $this->customers->flatMap->datasets
        ->merge($this->locations->flatMap->datasets)
        ->merge($this->vendors->flatMap->datasets);
}

The merging operation should take care of duplicate keys but feel free to add a unique call if it doesn't.