Banner Laravel

En este tutorial aprenderemos a implementar Bouncer en Laravel. Bouncer es una librería que nos permite administrar roles y habilidades en cualquier aplicación que use modelos del ORM Eloquent.

Para poder continuar con este tutorial es importante que primero revises el post Manejo de roles y habilidades en Eloquent con Bouncer donde aprenderás a instalar y configurar Bouncer.

Luego de instalar Laravel y Bouncer, copiado las migraciones y realizada la configuración necesaria, podemos comenzar a crear nuestro ejemplo. Recuerda que todos los pasos de instalación y configuración son explicados en el tutorial Manejo de roles y habilidades en Eloquent con Bouncer.

Para este tutorial crearemos una pequeña aplicación que contará con dos modelos: Post y Comment además del modelo User que ya incluye Laravel. A continuación te explico el funcionamiento de cada modelo:

  • Post: este modelo almacenará posts o publicaciones creadas por los usuarios.
  • Comment: este modelo se encargará de almacenar los comentarios de las publicaciones.

También tendremos un rol y dos habilidades:

  • El rol admin es asignado a los administradores y permite realizar todo tipo de acciones.
  • La habilidad Create Post es asignada a los autores y permite crear posts.
  • La habilidad Delete Comment es asignada a los autores al momento de crear un comentario y permite a dicho autor eliminar sus propios comentarios.

Básicamente, en nuestra aplicación los autores podrán crear posts o publicaciones y dejar comentarios en dichas publicaciones. Estos tienen el control de sus propios comentarios y pueden eliminarlos, siempre y cuando hayan sido creados por ellos mismos. Sólo los administradores pueden eliminar los comentarios de cualquier usuario.

Asegúrate de ejecutar php artisan make:auth antes de continuar, para activar la autenticación por defecto de Laravel.

Creando el modelo Post

Al inicio del tutorial mencionamos que usaremos dos modelos: Post y Comment. Como en este tutorial nos centraremos en la creación de los posts vamos primero a crear el modelo Post:

// Ejecuta el siguiente comando en la terminal:
php artisan make:model Post -mc

Dentro del modelo Post agregamos:

class Post extends Model
{
    protected $guarded = [];

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

También debemos editar el modelo User que viene por defecto con Laravel. Recuerda implementar el trait HasRolesAndAbilities que pertenece a Bouncer. En el modelo User agregaremos los métodos necesarios para la relación entre los usuarios y los posts:

class User extends Authenticatable
{
    // ...

    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

Creando el seeder

En nuestro archivo DatabaseSeeder.php agregaremos dos usuarios y los almacenaremos en las variables $user y $creatorOfPosts:

<?php

use Bouncer;
use App\User;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $user = factory(User::class)->create([
            'name' => 'Foo Bar',
            'email' => '[email protected]',
            'password' => bcrypt('secret')
        ]);

        $creatorOfPosts = factory(User::class)->create([
            'name' => 'Creator of Posts',
            'email' => '[email protected]',
            'password' => bcrypt('secret')
        ]);
    }
}

No olvides importar los modelos y el alias Bouncer al principio del archivo.

En el mismo archivo DatabaseSeeder.php también crearemos y asignaremos el rol admin y la habilidad Create Post al segundo usuario que hemos creado:

<?php
//...

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        // ...

        // Creamos el rol de administrador
        Bouncer::allow('admin')->everything();

        // Asignamos el rol de administrador a $user
        $user->assign('admin');

        // Asignamos al autor la habilidad de crear posts
        $creatorOfPosts->allow('create', Post::class);
    }
}

Luego de hacer esto podemos ejecutar las migraciones y los seeders con el comando php artisan migrate:refresh --seed.

Controladores

Para continuar con nuestra aplicación debemos agregar la lógica al controlador PostController. Este controlador fue creado automáticamente al generar el modelo Post (puesto que usamos la opción -c). A continuación vamos a agregar la lógica para listar y crear posts al controlador PostController.php:

class PostController extends Controller
{
    public function index()
    {
        $posts = Post::paginate(10);

        return view('posts.index', compact('posts'));
    }

    public function create()
    {
        return view('posts.create');
    }

    public function store(Request $request)
    {
        auth()->user()->posts()->create($request->all());

        return redirect()->action('PostController@index');
    }

    public function show(Post $post)
    {
        return view('posts.show', compact('post'));
    }
}

Rutas

En el archivo web.php vamos a agregar las rutas que nos van a permitir realizar las diferentes acciones en nuestra aplicación:

Route::group(['middleware' => 'auth'], function () {
    Route::get('/home', 'PostController@index')->name('posts.index');

    Route::group(['middleware' => 'can:create,App\Post'], function () {
        Route::get('/posts/create', 'PostController@create')->name('posts.create');
        Route::post('/posts', 'PostController@store')->name('posts.store');
    });
});

Route::get('posts/{post}', 'PostController@show')->name('posts.show');

Observa como mediante can:create,App\Post protegemos las rutas para que sólo los autores que pueden crear posts puedan acceder a los recursos que permiten ejecutar dicha acción.

Vistas

Finalmente podemos agregar las vistas de nuestra aplicación. Dentro del directorio resources/views/posts agrega las siguientes vistas:

Archivo index.blade.php:

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">Posts</div>

                    <div class="card-body">
                        @foreach ($posts as $post)
                            <div class="card mb-3">
                                <div class="card-body">
                                    <h3>
                                        <a href="{{ route('posts.show', $post) }}">{{ $post->title }}</a>
                                    </h3>
                                    <hr>
                                    {{ $post->body }}
                                </div>
                            </div>
                        @endforeach
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

Archivo create.blade.php:

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">Crear nuevo post</div>

                    <div class="card-body">
                        <form method="POST" action="{{ route('posts.store') }}">
                            @csrf
                            <div class="form-group">
                                <label for="title">Titulo</label>
                                <input type="text" class="form-control" name="title" required>
                            </div>

                            <div class="form-group">
                                <label for="body">Contenido</label>
                                <textarea name="body" class="form-control" cols="30" rows="10"></textarea>
                            </div>

                            <div class="form-group">
                                <button type="submit" class="btn btn-primary">Publicar</button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection

En el archivo resources/views/layouts/app.blade.php agregamos un enlace para la creación de los nuevos posts, esto lo hacemos dentro de la etiqueta ul ubicada después del comentario «Left Side Of Navbar», de esta forma:

<!-- Left Side Of Navbar -->
<ul class="navbar-nav mr-auto">
    @can ('create', App\Post::class)
    <li>
        <a href="{{ route('posts.create') }}" class="nav-link">Crear Post</a>
    </li>
    @endcan
</ul>

Utilizando la directiva @can comprobamos si el usuario tiene la habilidad para crear posts.

Si tienes alguna pregunta o inquietud no dudes en dejarla en los comentarios. En el próximo tutorial agregaremos todo lo relacionado a los comentarios.

Puedes ver y descargar el código de este ejemplo en GitHub: laravel-bouncer-example.

Si te ha gustado este tutorial y te interesa el tema de manejo de roles y habilidades puedes ver una explicación detallada y a fondo en el curso Técnicas de Autorización con Laravel donde aprenderás no sólo a utilizar Bouncer sino también sobre muchos otros temas respecto al manejo de roles, permisos y autorización:

No olvides seguirnos en Twitter y suscribirte a nuestro boletín:

Suscríbete a nuestro boletín

Te enviaremos publicaciones con consejos útiles y múltiples recursos para que sigas aprendiendo.

Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.