Права в Laravel 5. Часть 1.

Хочу разобрать случай когда нужно выставлять права определенному пользователю. Например, Вы разрабатываете CRM-систему у владельца компании есть сотрудники которые имеют доступ к системе и Вам необходимо чтобы несколько сотрудников выполняли разные функции или запретить экспорт базы данных клиентов.

Подготовка базы данных.

Создание миграции.

php artisan make:migration create_permissions_table --create=permissions

 

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePermissionsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('permissions', function (Blueprint $table) {
            $table->increments('id'); // идентификатор
            $table->string('name')->unique(); // имя на анг.
            $table->integer('parent'); // родитель
            $table->string('display_name')->nullable(); // Отображаемое имя
            $table->string('description')->nullable(); // описание

            $table->index('parent'); // присваиваемым индекс полю родитель
            $table->timestamps();
        });

        Schema::create('permission_user', function (Blueprint $table) {
            $table->integer('permission_id')->unsigned(); // id права
            $table->integer('user_id')->unsigned(); // id пользователя

            $table->foreign('permission_id')->references('id')->on('permissions') // устанавливаем зависимости полей
                ->onUpdate('cascade')->onDelete('cascade');
            $table->foreign('user_id')->references('id')->on('users')
                ->onUpdate('cascade')->onDelete('cascade');

            $table->primary(['permission_id', 'user_id']); // ключи
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('permissions');
        Schema::dropIfExists('permission_user');
    }
}

 

Содержимое миграции.

Таблица «permissions» будет содержать набор прав пользователей, «permissions_user» права пользователя.

Выполняем миграцию  php artisan migrate

Создаем модель.

php artisan make:model /Models/Permission

Содержимое модели ниже. Связь с таблицей пользователи многое ко многим.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cookie;

class Permission extends Model
{
    protected $table = 'permissions';
    protected $fillable = [
        'name', 'parent', 'display_name', 'description'
    ];

    

    /**
     * Пользователи, которые принадлежат права.
     */
    public function users()
    {
        return $this->belongsToMany('App\Models\User', 'permission_user', 'permission_id', 'user_id');
    }

}

 

Seed.

Сразу же для удобства создадим наполнения БД начальными данными(seeding). Эти классы хранятся в app/database/seeds

Создадим файл PermissionsTableSeeder.php и записываем необходимый набор прав.

У меня такой получился такой набор.

<?php

use Illuminate\Database\Seeder;
use App\Models\Permission;

class PermissionTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $permission = [
            /* client */
            [
                'name' => 'client-list',
                'parent_id' => '0',
                'display_name' => 'Просмотр клиента',
                'description' => ''
            ],
            /* master */
            [
                'name' => 'master-list',
                'parent_id' => '0',
                'display_name' => 'Просмотр мастеров',
                'description' => ''
            ],
            /* personal */
            [
                'name' => 'personal-list',
                'parent_id' => '0',
                'display_name' => 'Просмотр персонала',
                'description' => ''
            ],
            /* record */
            [
                'name' => 'record-list',
                'parent_id' => '0',
                'display_name' => 'Работа с записями',
                'description' => ''
            ],
            /* company */
            [
                'name' => 'company-list',
                'parent_id' => '0',
                'display_name' => 'Просмотр списка компаний',
                'description' => ''
            ],
            /* products */
            [
                'name' => 'product-management',
                'parent_id' => '0',
                'display_name' => 'Управление складом',
                'description' => ''
            ],
            /* metric */
            [
                'name' => 'metric-list',
                'parent_id' => '0',
                'display_name' => 'Просмотр статистики',
                'description' => ''
            ],
            [
                'name' => 'client-create',
                'parent_id' => '1',
                'display_name' => 'Создание новых клиентов',
                'description' => ''
            ],
            [
                'name' => 'client-edit',
                'parent_id' => '1',
                'display_name' => 'Изменение данных клиента',
                'description' => ''
            ],
            [
                'name' => 'client-copy',
                'parent_id' => '1',
                'display_name' => 'Выгрузка клиентской БД',
                'description' => ''
            ],
            [
                'name' => 'client-delete',
                'parent_id' => '1',
                'display_name' => 'Удаление клиента',
                'description' => ''
            ],
            /* master */
            [
                'name' => 'master-create',
                'parent_id' => '2',
                'display_name' => 'Создание мастера',
                'description' => ''
            ],
            [
                'name' => 'master-edit',
                'parent_id' => '2',
                'display_name' => 'Изменение данных мастера',
                'description' => ''
            ],
            [
                'name' => 'master-delete',
                'parent_id' => '2',
                'display_name' => 'Удаление мастера',
                'description' => ''
            ],
            /* personal */
            [
                'name' => 'personal-confirm',
                'parent_id' => '3',
                'display_name' => 'Подтвержать регистрацию',
                'description' => ''
            ],
            [
                'name' => 'personal-edit',
                'parent_id' => '3',
                'display_name' => 'Изменение данных персонала',
                'description' => ''
            ],
            [
                'name' => 'personal-delete',
                'parent_id' => '3',
                'display_name' => 'Удаление персонала',
                'description' => ''
            ],
            /* record */
            [
                'name' => 'record-delete',
                'parent_id' => '4',
                'display_name' => 'Удаление записей',
                'description' => ' '
            ],
            /* company */
            [
                'name' => 'company-create',
                'parent_id' => '5',
                'display_name' => 'Создание компании',
                'description' => ''
            ],
            [
                'name' => 'company-edit',
                'parent_id' => '5',
                'display_name' => 'Правка информации о компании',
                'description' => ''
            ],
            [
                'name' => 'company-delete',
                'parent_id' => '5',
                'display_name' => 'Удаление информации о компании',
                'description' => ''
            ],
            /* products */
            [
                'name' => 'product-category',
                'parent_id' => '6',
                'display_name' => 'Работа с категориями',
                'description' => ''
            ],
            [
                'name' => 'product-provider',
                'parent_id' => '6',
                'display_name' => 'Работа с поставщиками',
                'description' => ''
            ],
            [
                'name' => 'product-sale',
                'parent_id' => '6',
                'display_name' => 'Продажа товара',
                'description' => ''
            ],
            [
                'name' => 'product-history-sale',
                'parent_id' => '6',
                'display_name' => 'Просмотр истории продаж',
                'description' => ''
            ],
            [
                'name' => 'product-history',
                'parent_id' => '6',
                'display_name' => 'Просмотр истории',
                'description' => ''
            ],
            [
                'name' => 'product-delete',
                'parent_id' => '6',
                'display_name' => 'Удаление товаров',
                'description' => ''
            ]
        ];

        foreach ($permission as $key => $value) {
            Permission::create($value);
        }

    }
}

Запускаем сид  php artisan db:seed —class=PermissionsTableSeeder

Теперь нужно написать проверку на наличие прав получения, название права и связь с таблицей прав.

В модели User.php

    /**
     * Проверка имеет ли пользователь определенную роль
     * @param $check
     * @return boolean
     */
    public function hasPermission($check)
    {
        return in_array($check, array_pluck($this->permissions->toArray(), 'name'));
    }

    /**
     * Получает название права
     * @param $name
     * @return mixed
     */
    public function getPermissionDisplayName($name)
    {
        return DB::table('permissions')->where('name', $name)->first()->display_name;
    }

    /**
     * Функция для получение прав.
     * @return boolean
     **/
    public function permissions()
    {
        return $this->belongsToMany('App\Models\Permission', 'permission_user', 'user_id', 'permission_id');
    }

 

Создание посредника.

Проверка прав у пользователей будет через посредника(middleware).

php artisan make:middleware PermissionMiddleware

<?php

namespace App\Http\Middleware;

use Closure;

class PermissionMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     * @param  $permission
     * @return mixed
     */
    public function handle($request, Closure $next, $permission)
    {
        if (!$request->user()->hasPermission($permission)) {
            $name = $request->user()->getPermissionDisplayName($permission);
            return redirect('/')->with('error', 'Не достаточно прав! Для операции: "' . $name . '"');
        }
        return $next($request);
    }
}

После создание посредника нужно зарегистрировать его. Переходим в файл Kernel.php и дописываем строку в массив $routeMiddleware

protected $routeMiddleware = [
         ***
        'permission' => \App\Http\Middleware\PermissionMiddleware::class,
         ***
    ];

 

Использование.

Теперь в роуторе можно использовать права. Например, для работы с клиентам.

/* Работа с клиентами */
Route::get('clients', ['as' => 'clients.index', 'uses' => 'ClientsController@index', 'middleware' => ['permission:client-list']]);
Route::post('clients/create', ['as' => 'clients.store', 'uses' => 'ClientsController@store', 'middleware' => ['permission:client-create']]);
Route::post('clients/create/ajax', ['as' => 'clients.store.ajax', 'uses' => 'ClientsController@storeAjax', 'middleware' => ['permission:client-create']]);
Route::get('clients/{id}', ['as' => 'clients.show', 'uses' => 'ClientsController@show', 'middleware' => ['permission:client-list']]);
Route::patch('clients/{id}', ['as' => 'clients.update', 'uses' => 'ClientsController@update', 'middleware' => ['permission:client-edit']]);
Route::delete('clients/{id}', ['as' => 'clients.destroy', 'uses' => 'ClientsController@destroy', 'middleware' => ['permission:client-delete']]);

Со стороны UI не охота было бы видеть кнопку удалить по которой нельзя нажать. Выход из этого такой.

Переходим в файл шаблона и прописываем такую конструкцию на кнопку или на любой другой элемент и если у пользователя есть права, то он увидит содержимое.

@if (Auth::user()->hasPermission('client-delete'))
// other code
@endif

 

По присваиванию прав будет отдельная статья. В ней будет использоваться jQuery плагин jsTree.

Если есть вопросы задавайте в комментарии