Search code examples
eloquentlaravel-5.4has-and-belongs-to-many

Laravel 5.4 How to get all relations if user is an admin


E.G. $user->relatedModel (without parentheses) returns the entire RelatedModel collection if the user is an admin or the typical filtered collection if they are not.

I am trying to alter a number of user relationships so that if the logged in user is an admin, then they can see all models in that relationship without having to write the same conditions every time I need to use it, and without creating all the relationships in the property_user table for every admin on every related model that I need this on.

There is an answer here which explains fairly clearly what I want to achieve https://stackoverflow.com/a/34910225/1080341 which is exactly the idea I am looking for except that when I try this in Laravel 5.4 it throws an exception when the user is an admin.

Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation

If the user is a standard user the relationship returns the appropriate filtered collection.

Here is the code example from that answer

public function properties()
    {
    if ($this->isAdmin()) {
        return Property::query();
    } elseif ($this->isManager() || $this->isBroker()) {
        return $this->belongsToMany('App\Property');
    }

    return null;
}

This is already a large site with many relationship calls already written so I do not wish to have to refactor all those code instances when I believe I can simply modify the relationship definition.

How can I return a relationship from this function that includes all models when the user is an admin, and keep it so the use of $user->properties around the site will not need to be modified?

[2018-06-02 12:58:23] local.ERROR: LogicException: Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation in /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:403
Stack trace:
#0 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php(386): Illuminate\Database\Eloquent\Model->getRelationshipFromMethod('sites')
#1 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php(316): Illuminate\Database\Eloquent\Model->getRelationValue('sites')
#2 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(1279): Illuminate\Database\Eloquent\Model->getAttribute('sites')
#3 /home/vagrant/Leadgen/app/Http/Controllers/Backend/SiteController.php(29): Illuminate\Database\Eloquent\Model->__get('sites')
#4 [internal function]: App\Http\Controllers\Backend\SiteController->index(Object(Illuminate\Http\Request))
#5 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(55): call_user_func_array(Array, Array)
#6 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(44): Illuminate\Routing\Controller->callAction('index', Array)
#7 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Route.php(203): Illuminate\Routing\ControllerDispatcher->dispatch(Object(Illuminate\Routing\Route), Object(App\Http\Controllers\Backend\SiteController), 'index')
#8 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Route.php(160): Illuminate\Routing\Route->runController()
#9 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Router.php(572): Illuminate\Routing\Route->run()
#10 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\Routing\Router->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#11 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(41): Illuminate\Routing\Pipeline->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#12 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(148): Illuminate\Routing\Middleware\SubstituteBindings->handle(Object(Illuminate\Http\Request), Object(Closure))
#13 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#14 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php(43): Illuminate\Routing\Pipeline->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#15 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(148): Illuminate\Auth\Middleware\Authenticate->handle(Object(Illuminate\Http\Request), Object(Closure))
#16 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#17 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(65): Illuminate\Routing\Pipeline->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#18 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(148): Illuminate\Foundation\Http\Middleware\VerifyCsrfToken->handle(Object(Illuminate\Http\Request), Object(Closure))
#19 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#20 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\Routing\Pipeline->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#21 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(148): Illuminate\View\Middleware\ShareErrorsFromSession->handle(Object(Illuminate\Http\Request), Object(Closure))
#22 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#23 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(64): Illuminate\Routing\Pipeline->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#24 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(148): Illuminate\Session\Middleware\StartSession->handle(Object(Illuminate\Http\Request), Object(Closure))
#25 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#26 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\Routing\Pipeline->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#27 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(148): Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse->handle(Object(Illuminate\Http\Request), Object(Closure))
#28 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#29 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(59): Illuminate\Routing\Pipeline->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#30 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(148): Illuminate\Cookie\Middleware\EncryptCookies->handle(Object(Illuminate\Http\Request), Object(Closure))
#31 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#32 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(102): Illuminate\Routing\Pipeline->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#33 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Router.php(574): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#34 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Router.php(533): Illuminate\Routing\Router->runRouteWithinStack(Object(Illuminate\Routing\Route), Object(Illuminate\Http\Request))
#35 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Router.php(511): Illuminate\Routing\Router->dispatchToRoute(Object(Illuminate\Http\Request))
#36 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\Routing\Router->dispatch(Object(Illuminate\Http\Request))
#37 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\Foundation\Http\Kernel->Illuminate\Foundation\Http\{closure}(Object(Illuminate\Http\Request))
#38 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(30): Illuminate\Routing\Pipeline->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#39 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(148): Illuminate\Foundation\Http\Middleware\TransformsRequest->handle(Object(Illuminate\Http\Request), Object(Closure))
#40 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#41 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(30): Illuminate\Routing\Pipeline->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#42 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(148): Illuminate\Foundation\Http\Middleware\TransformsRequest->handle(Object(Illuminate\Http\Request), Object(Closure))
#43 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#44 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\Routing\Pipeline->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#45 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(148): Illuminate\Foundation\Http\Middleware\ValidatePostSize->handle(Object(Illuminate\Http\Request), Object(Closure))
#46 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#47 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php(46): Illuminate\Routing\Pipeline->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#48 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(148): Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode->handle(Object(Illuminate\Http\Request), Object(Closure))
#49 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#50 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(102): Illuminate\Routing\Pipeline->Illuminate\Routing\{closure}(Object(Illuminate\Http\Request))
#51 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(151): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#52 /home/vagrant/Leadgen/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(116): Illuminate\Foundation\Http\Kernel->sendRequestThroughRouter(Object(Illuminate\Http\Request))
#53 /home/vagrant/Leadgen/public/index.php(53): Illuminate\Foundation\Http\Kernel->handle(Object(Illuminate

Solution

  • Since it isn't a real relationship, you can't access it as one. Use this:

    $sites = $user->sites()->get();
    

    If you want to access it as a property and don't need the query object (e.g. to add other constraints), you can use an accessor:

    public function getPropertiesAttribute()
    {
        if ($this->isAdmin()) {
            return Property::all();
        } elseif ($this->isManager() || $this->isBroker()) {
            return $this->belongsToMany('App\Property')->get();
        }
    
        return null;
    }
    

    A dirty hack:

    public function sites()
    {
        if ($this->isAdmin()) {
            return $this->hasMany('App\Site', 'id')->orWhereRaw(1);
        }
        return $this->belongsToMany('App\Site');
    }