Fix: Laravel Entrust Permissions Caching Issue

Entrust is a great Laravel package to quickly add role based permission access controls to your web application. It has a lot of features and is quite flexible with how and where you want to check for correct permissions, be it in the middleware, blade template or any where in your code.

One problem with first time users of the package is that it is not clear on how to set caching for the roles and permissions. It actually uses the default cache drive set in your .env file, and one would assume it would cache the permissions/roles automatically. Unfortunately this was not the case – on every page load, for every permission/role check, a DB query was being run. For example, in my case, I had a sidebar blade template which would check the permissions and display the link accordingly. That resulted in hundreds of the same query to run on the database.

So, digging into Entrust’s code, I found out that it references a config variable (Config::get(‘cache.ttl’)) that is not available/set in the default Laravel installation. This is called in these two files: EntrustUserTrait.php and EntrustRoleTrait.php.

To fix it, in your config/cache.php you will have to add a new config variable ttl and set to to the number of minutes you want the cache to live, below example sets it to 60 minutes:

ttl => 60

I don’t know why it was not placed in their own config file (config/entrust.php), but for the time being, this should work.

Return view with custom status code in Laravel 5

There are times when you want to render a view, but pass along a custom status code. This is not reflected in the official laravel documentation. You need to set the status code with the setStatusCode() method.

In the following example, am returning a view and passing a 403 status code. Following works with Laravel 5.2


return response()->view('myviewname')->setStatusCode(403);

Load Service Providers & Aliases for Local/Development Environment Only in Laravel 5

In laravel, there are times when you need to install packages only for your local or development environment for debugging and testing purposes.

Even if you specify those packages in require-dev section of your composer.json file, you still need to add the service providers and aliases in the config/app.php file. Without that, it won’t work.

With Laravel 5, you can easily load service providers based on your environment. In this example, we’ll install the barryvdh/laravel-debugbar package which displays a pretty debug information during testing of your application.

1. Add the package to your composer.json file

"require-dev": {
    "fzaninotto/faker": "~1.4",
    "mockery/mockery": "0.9.*",
    "phpunit/phpunit": "~4.0",
    "phpspec/phpspec": "~2.1",
    "barryvdh/laravel-debugbar": "^2.0"
}

Save the file, and do a composer update.

composer update

 

2. Create a new ServiceProvider called LocalServiceProvider

// file: app/Providers/LocalServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class LocalServiceProvider extends ServiceProvider
{

    protected $providers = [
        'Barryvdh\Debugbar\ServiceProvider'
    ];
    protected $aliases = [
        'Debugbar' => 'Barryvdh\Debugbar\Facade'
    ];
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //register the service providers for local environment
        if ($this->app->isLocal() && !empty($this->providers)) {
            foreach ($this->providers as $provider) {
                $this->app->register($provider);
            }
            //register the alias
            if (!empty($this->aliases)) {
                foreach ($this->aliases as $alias => $facade) {
                    $this->app->alias($alias, $facade);
                }
            }
        }

    }
}

 

3. Add the new ServiceProvider to the config/app.php service provider list

/*
 * Application Service Providers...
 */
App\Providers\AppServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\LocalServiceProvider::class,

 

To ensure laravel detects the correct environment, you have to make sure you are specifying the correct environment in your .env file.

APP_ENV=local

Now whenever you have packages you want to use only in your local environment, you can append their service providers to the $providers array and their aliases to the $aliases array. This way, they don’t get loaded unnecessarily in your production environment.

Redirect all Laravel 5 FormRequest Authorize Failures to a Route/URL

Form validation and other checks have become very easy to implement in Laravel 5 with the help of Form Requests.

Every Form Request should have an authorize() and a rules() method.

You can do ownership and other access checks in the authorize() method, and in the rules() method, you return an array with the rules for form validation. By default, if the method authorize fails/returns false, Laravel sends a 403 forbidden response.

Instead of that, when the authorize fails, you can redirect to an error page with a custom error message.

<?php namespace App\Http\Requests;

use App\Post;

class PostFormRequest extends Request {

    protected $post;
    protected $error;

    /**
     * Check if post belongs to user
     */
    public function authorize()
    {
        $this->post = Post::findOrFail($this->input('id', 0); //default ID to 0, if not sent through form.
        if ($this->user->id !== $this->post->user_id) {
             $this->error = 'Sorry, you do not have permission to edit this post';
             return false;
        }
        return true;
    }
    /**
     * This method will be invoked if authorize() fails
     */
    public function forbiddenResponse()
    {
        return redirect('error')->with('error_message', $this->error);
    }
    /**
     * Validation rules
     */
    public function rules()
    {
        return [
            'title' => 'required',
            'content' => 'required'
        ];
    }
}

With the above code, if a request fails the authorize() method, it’ll be redirected to www.domain.com/error with an error message.

However, if you have multiple Form Requests, its better to put all the code in the parent Request (app/Http/Requests/Request.php) class.

<?php namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

abstract class Request extends FormRequest {
    //A generic error message, can be overridden in the sub class
    protected $error = 'An error occurred';

    public function forbiddenResponse()
    {
        return redirect('dashboard')->with('msg_error', $this->error);
    }
}