En la actualidad es común encontrarnos con páginas web que poseen una barra de navegación la cual especifica dentro de cuáles secciones estamos ubicados, esto es conocido como Breadcrumbs y en este tutorial vamos a utilizar el paquete davejamesmiller/laravel-breadcrumbs  en un mini-proyecto para conocer su funcionamiento.

Mini-Proyecto

Vamos a comenzar nuestro mini-proyecto instalando Laravel, para hacerlo ejecutamos el siguiente comando en nuestra terminal:

$ composer create-project laravel/laravel styde-cine

Seguidamente, procedemos a configurar nuestro archivo .env para enlazar este proyecto a una base de datos, agregando las credenciales, como aprendimos en el Curso de Laravel 5.5 desde cero.

Para este proyecto vamos a definir 2 nuevos modelos llamados Genre y Movie, además del modelo User ya existente por defecto, ejecutamos los siguientes comandos en nuestra terminal:

$ php artisan make:model Genre -mcf
$ php artisan make:model Movie -mcf

Con la opción -mfc estamos indicando que queremos, además de crear un modelo, crear la migración, controlador y model factory.

Seguidamente vamos a editar las migraciones que hemos creado, para ello comenzamos con la migración create_genres_table:

<?php

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

class CreateGenresTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('genres', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('description');
            $table->timestamps();
        });
    }

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

Continuamos con la migración para las películas create_movies_table:

<?php

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

class CreateMoviesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('movies', function (Blueprint $table) {
            $table->increments('id');

            $table->unsignedInteger('genre_id');
            $table->foreign('genre_id')->references('id')->on('genres');

            $table->string('name');
            $table->string('image');
            $table->integer('length');
            $table->integer('year');
            $table->timestamps();
        });
    }

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

Model Factories & Seeder

Ahora vamos modificar los Models Factories de los modelos creados anteriormente. Comenzamos editando el que tiene por nombre GenreFactory.php:

<?php

use Faker\Generator as Faker;

$factory->define(App\Genre::class, function (Faker $faker) {
    return [
        'name' => $faker->word,
        'description' => $faker->paragraph,
    ];
});

Continuamos ahora editando el archivo MovieFactory.php:

<?php

use Faker\Generator as Faker;

$factory->define(App\Movie::class, function (Faker $faker) {
    return [
        'name' => $faker->realText($faker->numberBetween(10,50)),
        'image' => $faker->imageUrl(500, 500),
        'length' => $faker->numberBetween(100, 140),
        'year' => $faker->numberBetween(1970, 2018),
    ];
});

Si no conoces cómo funcionan los Model Factories te invitamos a ver la lección Generar registros usando Model Factories en Laravel del Curso de Laravel desde cero

Con los Models Factories definidos vamos a crear datos de prueba, editando el archivo DatabaseSeeder.php ubicado en database/seeds:

<?php

use App\{Genre, Movie};
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $genres = factory(Genre::class, 10)->create();

        $genres->each(function ($genre) {
            factory(Movie::class, 5)->create([
                'genre_id' => $genre->id
            ]);
        });
    }
}

Para finalizar con las migraciones y los seeders debemos ejecutar el siguiente comando en nuestra terminal:

$ php artisan migrate --seed

Con esto ya tenemos nuestra base de datos con las tablas necesarias y con algunos registros de prueba gracias a los models factories y los seeders.

Si deseas aprender más sobre los Seeders puedes visitar la lección Inserción de datos con los seeders de Laravel del Curso de Laravel desde cero.

Instalación & Configuración del paquete

Necesitamos decirle a Composer que requerimos el nuevo paquete encargado de ayudarnos a generar los breadcrumbs, vamos a ejecutar la siguiente instrucción en la terminal:

$ composer require davejamesmiller/laravel-breadcrumbs:5.x

Después de finalizar la instalación del paquete es necesario crear un nuevo archivo llamado breadcrumbs.php dentro de la carpeta routes con el cual estaremos trabajando más adelante.

Por defecto, este paquete al generarse en nuestra vista posee los estilos de Bootstrap 4, si deseas cambiarlo debemos ejecutar un comando para generar un archivo de configuración llamado breadcrumbs.php que se ubicara en la carpeta config.

$ php artisan vendor:publish --provider="DaveJamesMiller\Breadcrumbs\BreadcrumbsServiceProvider"

Después de ejecutar el comando podemos abrir el archivo config/breadcrumbs.php y buscaremos en el arreglo la llave view la cual estará de esta forma por defecto:

'view' => 'breadcrumbs::bootstrap4',

El paquete por defecto tiene soporte para algunos frameworks CSS:

Por ejemplo, si estamos trabajando con Materialize deberíamos reemplazar el valor de llave view por:

'view' => 'breadcrumbs::materialize',

Si no estás trabajando con alguno de estos frameworks y deseas personalizar lo que se genere puedes hacerlo indicando la dirección de una vista de la siguiente forma:

'view' => 'partials.breadcrumbs',

Esto es, el contenido de la vista está enresources/views/partials/breadcrumbs.blade.php :

@if (count($breadcrumbs))

    <ol class="breadcrumb">
        @foreach ($breadcrumbs as $breadcrumb)

            @if ($breadcrumb->url && !$loop->last)
                <li class="breadcrumb-item"><a href="{{ $breadcrumb->url }}">{{ $breadcrumb->title }}</a></li>
            @else
                <li class="breadcrumb-item active">{{ $breadcrumb->title }}</li>
            @endif

        @endforeach
    </ol>

@endif

Rutas de nuestro proyecto

Para este mini-proyecto debemos definir algunas rutas, para hacer esto vamos a dirigirnos al archivo routes/web.php y vamos a dejarlo de la siguiente forma:

<?php

Route::get('/', function () {
    return view('home');
})->name('home');

Route::get('/genres', 'GenreController@index')->name('genres');
Route::get('/genres/{genre}', 'GenreController@show')->name('genres.show');
Route::get('/movies/{movie}', 'MovieController@show')->name('movies.show');

En total tenemos 4 rutas que apuntan a los controladores que ya hemos creado anteriormente pero que aún no tienen definidos los métodos que estamos llamando.

Controladores

Comenzaremos editando el controlador GenreController y lo dejaremos de la siguiente forma:

<?php

namespace App\Http\Controllers;

use App\Genre;
use Illuminate\Http\Request;

class GenreController extends Controller
{
    public function index()
    {
        $genres = Genre::all();

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

    public function show(Genre $genre)
    {
        return view('genres.show', compact('genre'));
    }
}

Procedemos ahora con el controlador MovieController:

<?php

namespace App\Http\Controllers;

use App\Movie;
use Illuminate\Http\Request;

class MovieController extends Controller
{
    public function show(Movie $movie)
    {
        return view('movies.show', compact('movie'));
    }
}

Vistas

Para este proyecto he creado un archivo llamado template.blade.php el cual nos servirá como plantilla para el resto de nuestros vistas, su contenido es el siguiente:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Styde.net</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body>
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <h1 class="mt-4 mb-4">Styde.net</h1>
            </div>

            <div class="col-md-8">
                @yield('breadcrumbs')
            </div>

            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">@yield('title')</div>

                    <div class="card-body">
                        @yield('content')
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

En nuestros controladores apuntamos hacía algunas vistas y ahora vamos a crearlas comenzando por la vista home.blade.php ubicándola en la carpeta resources/views

@extends('template')

@section('title', 'Inicio')

@section('breadcrumbs')
    {{ Breadcrumbs::render('home') }}
@endsection

@section('content')
    Pagina de inicio.
    <a href="{{ route('genres') }}">Ver géneros</a>
@endsection

Como puedes notar estamos utilizando el Facade Breadcrumbs perteneciente al paquete, además se llama a la función render y le pasamos el valor 'home', este valor es el nombre de la sección del breadcrumbs que aun no hemos definido, esto lo vamos hacer más adelante en el archivo routes/breadcrumbs.php.

Continuamos con la siguiente vista y para esto vamos a crear una carpeta llamada genres y dentro colocamos una vista llamada index.blade.php y la dejamos de esta forma:

@extends('template')

@section('title', 'Generos')

@section('breadcrumbs')
    {{ Breadcrumbs::render('genres') }}
@endsection

@section('content')
    <ul>
        @foreach ($genres as $genre)
        <li>
            {{ $genre->name }} <a href="{{ route('genres.show', $genre) }}">(Ver películas)</a>
        </li>
        @endforeach
    </ul>
@endsection

Podemos notar como ahora hemos colocado un valor diferente en la función render ya que se trata de otra sección.

Vamos a continuar creando una vista llamada show.blade.php en la misma carpeta:

@extends('template')

@section('title', $genre->name)

@section('breadcrumbs')
    {{ Breadcrumbs::render('genre', $genre) }}
@endsection

@section('content')
    <div class="row">
        @foreach ($genre->movies as $movie)
        <div class="col-md-4">
            <div class="card mb-4">
                <div class="card-body text-center">
                    <img src="{{ $movie->image }}" class="img-thumbnail">

                    <a href="{{ route('movies.show', $movie) }}">
                        <h5 class="mt-2">{{ $movie->name }}</h5>
                    </a>
                </div>
            </div>
        </div>
        @endforeach
    </div>
@endsection

Por último, vamos a crear una nueva carpeta llamada movies en resources/views, dentro de ella creamos una nueva vista llamada show.blade.php y le colocaremos lo siguiente:

@extends('template')

@section('title', $movie->name)

@section('breadcrumbs')
    {{ Breadcrumbs::render('movie', $movie) }}
@endsection

@section('content')
    <div class="row">
        <div class="col-md-3">
            <img src="{{ $movie->image }}" class="img-thumbnail">
        </div>
        <div class="col-md-9">
            <h1>{{ $movie->name }}</h1>
            <hr>
            <p>Duración: <strong>{{ $movie->length }}</strong> minutos</p>
            Año de estreno: <strong>{{ $movie->year }}</strong>
        </div>
    </div>
@endsection

Secciones del breadcrumbs

Ahora, en el archivo routes/breadcrumbs.php vamos a definir todas esas secciones que ya hacemos referencia desde nuestras vistas con el Facade Breadcrumbs y el método render.

<?php

use DaveJamesMiller\Breadcrumbs\Facades\Breadcrumbs;

// Inicio
Breadcrumbs::for('home', function ($trail) {
    $trail->push('Inicio', route('home'));
});

// Inicio > Generos
Breadcrumbs::for('genres', function ($trail) {
    $trail->parent('home');
    $trail->push('Generos', route('genres'));
});

// Inicio > Generos > [Genero]
Breadcrumbs::for('genre', function ($trail, $genre) {
    $trail->parent('genres');
    $trail->push($genre->name, route('genres.show', $genre));
});

// Inicio > Generos > [Genero] > [Pelicula]
Breadcrumbs::for('movie', function ($trail, $movie) {
    $trail->parent('genre', $movie->genre);
    $trail->push($movie->name, route('movies.show', $movie));
});

En este archivo estamos haciendo el llamado nuevamente del Facade Breadcrumbs pero esta vez usamos el método for para crear las secciones, el primer parámetro que recibe es el nombre de la sección y como segundo parámetro le pasamos una función anónima o callback.

El callback obtiene como primer parámetro la variable con el nombre $trail la cual es una instancia de la clase DaveJamesMiller\Breadcrumbs\BreadcrumbsGenerator, como segundo parámetro pasamos un valor opcional, útil cuando manejamos secciones dinámicas como el nombre de un post, categoría, usuario, etc.

Entre los métodos que tenemos disponibles está push, encargado de colocar el nombre en la vista. En nuestro ejemplo vemos como en ocasiones le colocamos un nombre estático como Inicio pero también puede ser dinámico como lo hacemos con el nombre del género y el de la película, este método también recibe una ruta como segundo parámetro.

Por otro lado, podemos observar que usamos el método parent, con este método definimos cual es el padre de la sección. En el caso de la primera sección llamada home veamos como no se define ya que no tiene padre porque es la sección principal. Este método recibe un primer argumento obligatorio que es el nombre de la sección padre y un segundo argumento opcional que es el valor que está esperando la sección padre como por  ejemplo:

$trail->parent('genre', $movie->genre);

En este caso, debemos enviar el género de la película ya que la sección genre lo está esperando en su callback.

Podemos dirigirnos a nuestro navegador y observar como el Breadcrumbs o barra de navegación es dinámica mientras ingresamos a los enlaces, al ingresar a una película deberíamos tener una vista como esta:

Resultado final de nuestro ejemplo

Finalmente hemos terminado el código del ejemplo, puedes obtenerlo en este repositorio. Te invito a que compartas este artículo en tus redes sociales y no olvides comentar las dudas que tengas o el resultado obtenido después de seguir este artículo.

Espero que te haya gustado este material. 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.