falbar Добавляем хлебные крошки в Laravel

Добавляем хлебные крошки в Laravel

4 ноября 2018 Перевод Туториал 109 0

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

Реклама

Важность хлебных крошек для сайта

В настоящее время наблюдается совершенно иная ситуация. В веб-разработках этап UI/UX дизайна также важен, как планирование логики серверной части программы, на основе чего все и работает. Существует огромный спектр общепринятых практик в веб-дизайне, при использовании которых программа обязательно получит ярлык «дружелюбной к пользователю». Еще больше существует ошибок в дизайне, которых должны избегать все.

Люди ненавидят, когда на сайтах неожиданно выскакивают непрошенные всплывающие окна. Я тоже их недолюбливаю, но не только эти окна способны выбесить пользователя. Неспособность понять, где находится юзер, и ориентироваться на сайте сравнима с тем, чтобы потеряться в огромном супермаркете. Ощущается та же беспомощность.

Это, конечно, может быть серьезной проблемой, но необязательно до этого доводить! Хлебные крошки могут оказаться наиболее простым решением, позволяющим пользователям наслаждаться удобной веб-навигацией.

Хлебные крошки создают систему навигации, которая помогает пользователям узнать их настоящее положение в отношении других веб-страниц сайта. Такое название система получила от знаменитой сказки про Гензеля и Гретель, ведь происходит примерно то же самое. Эта система как бы оставляет за собой дорожку, благодаря которой пользователи не теряются, а потому получают хорошие впечатления от сайта. Если на веб-сайте используются хлебные крошки, будет также уменьшено количество действий, которые юзеру нужно предпринять для того, чтобы добраться до нужной страницы.

Разработка хлебных крошек в Laravel - достаточно простая задача. Существует пакет, который занимается большей частью логики. Мы рассмотрим, как пользоваться этим пакетом и применить все его лучшие функции.

Установка приложения Laravel

Весь туториал рассчитан на разработчиков, работающих с Laravel.

С помощью composer мы будем работать с пакетом Laravel Breadcrumbs. Мы напишем код, в котором динамически обработаются сервисы навигации хлебных крошек, причем рендеринг меняется в зависимости от того, какую страницу просматривает пользователь.

Готовый код, показанный в статье, доступен на github.

Ради этой статьи мы создадим приложение Laravel заново.

laravel new breadcrumbs

Возможен и другой вариант:

composer create-project --prefer-dist laravel/laravel breadcrumbs

Далее вводим пакет Laravel Breadcrumbs, вводя следующую команду:

composer require davejamesmiller/laravel-breadcrumbs

Создание HTTP роутинга

Давайте создадим и назовем HTTP маршруты в файле routes/web.php. В более поздних частях этой статьи мы будем обращаться к ним по именам.

Для этого примера нужны следующие маршруты:

routes/web.php

Route::get('/', [
    'as' => 'home',
    'uses' => 'MainController@home'
]);

Route::get('/continent/{name}', [
    'as' => 'continent',
    'uses' => 'MainController@continent'
]);

Route::get('/country/{name}', [
    'as' => 'country',
    'uses' => 'MainController@country'
]);

Route::get('/city/{name}', [
    'as' => 'city',
    'uses' => 'MainController@city'
]);

Чтобы продемонстрировать силу хлебных крошек мы разработаем небольшое приложение, в котором будет происходит регистрация континента, страны и города. С помощью атрибута hasMany мы укажем, что на континенте много стран, а в стране - много городов.

Для создания этих модель нужно ввести такие команды:

php artisan make:model Continent
php artisan make:model Country
php artisan make:model City

Также создадим файлы миграции для каждой модели:

php artisan make:migration continents
php artisan make:migration countries
php artisan make:migration cities

Далее определим отношения в каждом классе моделей:

app/Continent.php

namespace App;

use App\Country;
use Illuminate\Database\Eloquent\Model;

class Continent extends Model
{
    public function country(){
        return $this->hasMany(Country::class);
    }
}

app/Country.php

namespace App;

use App\City;
use App\Continent;
use Illuminate\Database\Eloquent\Model;

class Country extends Model
{
    protected $guarded = [];
    
    public function city(){
        return $this->hasMany(City::class);
    }

    public function continent(){
        return $this->belongsTo(Continent::class);
    }
}

app/City.php

namespace App;

use App\Country;
use Illuminate\Database\Eloquent\Model;

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

    public function country(){
       return $this->belongsTo(Country::class);
    }
}
Обратите внимание на то, что guarded присвоено значение пустого массива. Это сделано потому, что мы собираемся сделать что-то вроде массового присвоения во время наполнения СУБД данными. Когда бы вы ни производили подобные изменения с файлом модели, не забывайте их отменить перед тем, как приложение попадет на продакшен.

Теперь создадим контроллер, который будет ответственен за HTTP запросы, которые будет получать приложение. Мы назовем его MainController и будем использовать разные действия (methods), чтобы вводить (compact) вхождения в модели с обратной выборкой, причем в каждой будут содержаться разные хлебные крошки.

Чтобы создать этот контроллер, нужно написать следующую команду:

php artisan make:controller MainController

Теперь пропишем действия (methods), которые будут возвращать выборки после обработки HTTP запросов.

app/Http/Controllers/MainControllers.php

namespace App\Http\Controllers;

use App\Continent;
use App\Country;
use App\City;
use Illuminate\Http\Request;

class MainController extends Controller
{
    public function home(){
        return view('home');
    }

    public function continent($name){
        $continent = Continent::where('name', $name)->first();
        return view('continent', compact('continent'));
    }

    public function country($name){
        $country = Country::where('name', $name)->first();
        return view('country', compact('country'));
    }

    public function city($name){
        $city = City::where('name', $name)->first();
        return view('city', compact('city'));
    }
}

Мы включили отношения между моделями, так что мы можем изучить возможности сервиса Laravel Breadcrumbs в контексте динамических ссылок и относительных параметров.

Теперь создадим выборки для моделей, только что созданных нами. Добавим 4 новых файла в директории resources/views. Мы назовем их так:

home.blade.php
continent.blade.php
country.blade.php
city.blade.php

Эти имена достаточно интуитивны. Первый содержит фронтенд-код для домашней страницы, второй - для страницы континентов, третий - для стран, четвертый - для городов.

Отлично, теперь наполним эти выборки кодом.

resources/views/home.blade.php

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
</head>
<body>
{{ Breadcrumbs::render('home') }}
</body>
</html>

resources/views/continent.blade.php

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
</head>
<body>
{{ Breadcrumbs::render('continent', $continent) }}
</body>
</html>

resources/views/country.blade.php

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
</head>
<body>
    {{ Breadcrumbs::render('country', $country->continent, $country) }}
</body>
</html>

resources/views/city.blade.php

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
</head>
<body>
{{ Breadcrumbs::render('city', $city->country->continent, $city->country, $city) }}
</body>
</html>

У моделей Continent и Country используются отношения «один ко многим». Многие города принадлежат одной стране, а многие страны принадлежат одному континенту. Континент – «дед» города, а страна - его «родитель». В этой статье мы не будем углубляться в логику отношений в Laravel, однако эту информацию можно почерпнуть самостоятельно.

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

database/migrations/2017_11_02_092826_continents.php

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

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

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        //
    }
}

database/migrations/2017_11_02_092835_countries.php

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

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

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        //
    }
}

database/migrations/2017_11_02_092845_cities.php

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

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

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        //
    }
}

Миграция может проводиться с использованием этой команды:

php artisan migrate

Давайте добавим в базу данных информацию, чтобы можно было протестировать приложение. Для этого создадим 3 файла factory (по одному на каждую модель) и обновим метод run файла DatabaseSeeder.php (уже существует) в директории database/seeds.

Чтобы создать их, нужно записать такие команды:

php artisan make:factory ContinentFactory --model=Continent
php artisan make:factory CountryFactory --model=Country
php artisan make:factory CityFactory --model=City

Они создадут следующие файлы соответственно:

database/factories/ContinentFactory.php

use Faker\Generator as Faker;

$factory->define(App\Continent::class, function (Faker $faker) {
    return [
        //
    ];
});

database/factories/CountryFactory.php

use Faker\Generator as Faker;

$factory->define(App\Country::class, function (Faker $faker) {
    return [
        //
    ];
});

database/factories/CityFactory.php

use Faker\Generator as Faker;

$factory->define(App\City::class, function (Faker $faker) {
    return [
        //
    ];
});

Наконец, обновим файл DatabaseSeeder.php, который занимается всеми свежими вхождениями приложения Laravel. Мы используем эти файлы для введения 3 записей в базу данных. Мы добавим Africa (модель континента), South Africa (модель страны), Johannesburg (модель города) и уточним их отношения:

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        factory(App\City::class)->create([
            'name' => 'Johannesburg',
            'country_id' => function(){
                return factory(App\Country::class)->create([
                    'name' => 'South Africa',
                    'continent_id' => function(){
                    return factory(App\Continent::class)->create([
                        'name' => 'Africa' ])->id;
                    }
                ])->id;
            }
        ]);
    }
}

Провести сидинг базы данных можно этой командой:

php artisan db:seed --class=DatabaseSeeder

Установка файла хлебных крошек

В директории рутов нужно создать новый файл breadcrumbs.php. К нему мы будем обращаться каждый раз при обработке хлебной крошки, так как он показывает Laravel, как обращаться с этой информацией. Без этого файла Laravel будет выдавать ошибку каждый раз, когда будет сталкиваться с вызовом функции хлебных крошек.

Первая функция хлебных крошек, которая будет определена в новом файле breadcrumbs.php, будет сделана для домашней страницы. Хлебная крошка «home» будет загружаться при посещении рута с именем «home».

routes/breadcrumbs.php

Breadcrumbs::register('home', function ($breadcrumbs) {
    $breadcrumbs->push('Home', route('home'));
});

Рендеринг статической хлебной крошки

В вышенаписанном коде можно увидеть, что класс Breadcrumbs используется для вызова статического метода. Он регистрирует новую хлебную крошку с именем home и вызывает функцию-замыкание. Та берет параметр $breadcrumbs и помещает в URL новое значение хлебной крошки.

$breadcrumbs->push('Home', route('home'));

«Home» в методе push код написан хардкодом. Это то, что будет появляться при рендеринге хлебной крошки на домашней или любой другой выборке, в которой хлебная крошка Home нужна для отображения цепи навигации.

route('home') возвращает URL «home» и будет ссылкой, к которой ведет Home при обработке в любой выборке.

Наконец, в коде домашней выборки проводим рендеринг хлебной крошки.

resources/views/home.blade.php

{{ Breadcrumbs::render('home') }}

Метод render получает имя хлебной крошки для отображения в домашней выборке. В более сложных цепях навигации, которые включают отношения между БД, функция render принимает столько вхождений Models в качестве аргументов, сколько требуется для рендеринга полной цепи навигации хлебной крошки.

Об этом мы подробнее поговорим далее.

Рендеринг динамической хлебной крошки

Что же произойдет, если в веб-приложении мы не знаем точные атрибуты страниц, которые будут посещаться пользователем? Например, у нас есть веб-сайт, на котором отображается информация о континентах, странах и городах. Мы не сможем записать хардкодом название любого континента, страны и города в файл routes/breadcrumbs.php, так как мы никогда не узнаем заранее, что будет просматривать юзер.

Чтобы можно было обрабатывать динамические хлебные крошки в реальном времени, можно прописать файл routes/breadcrumbs.php так, чтобы он эффективно работал с динамическими Models в нашем приложении и создавал цепь навигации хлебных крошек, пока юзеры исследуют более глубокие слои программы.

Как говорилось ранее, в модели континента много стран, а в стране много городов. Зная это, мы можем создать интерфейс хлебной крошки, который выглядит так, если мы посетим Johannesburg, принадлежащий South Africa, где последняя принадлежит Africa.

rendering-dinamicheskoj-xlebnoj-kroshki

Чтобы сгенерировать цепь навигации хлебных крошек, используя отношения между моделями, как выше, начнем с написания кода, который регистрирует хлебную крошку continent:

routes/breadcrumbs.php

Breadcrumbs::register('continent', function ($breadcrumbs, $continent) {
    $breadcrumbs->parent('home');
    $breadcrumbs->push($continent->name, route('continent', [
        'name' => $continent->name
    ]));
});

Мы регистрируем хлебную крошку continent и передаем closure. Последняя принимает новый параметр $continent. Далее хлебная крошка home назначается «родителем». Наконец, присваиваются имя и URL хлебной крошки $continent.

Для обработки хлебной крошки в выборке континента напишем этот кусок кода:

resources/views/continent.blade.php

{{ Breadcrumbs::render('continent', $continent) }}

Метод render получает имя хлебной крошки в качестве первого аргумента. Он также получает аргумент $continent, который поможет в упрощении вхождения модели континента до базовых параметров, таких как имя и URL.

На моем локальном компьютере после посещения http://127.0.0.1:8000/continent/africa откроется страница континента Африки с таким интерфейсом хлебной крошки:

lokalnom-kompyutere-posle-laravel

Рендеринг полной цепи навигации

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

Это финальный код файла routes/breadcrumbs.php для этой статьи. Он может увеличиваться в зависимости от того, чего вы пытаетесь добиться.

routes/breadcrumbs.php

Breadcrumbs::register('home', function ($breadcrumbs) {
    $breadcrumbs->push('Home', route('home'));
});

Breadcrumbs::register('continent', function ($breadcrumbs, $continent) {
    $breadcrumbs->parent('home');
    $breadcrumbs->push($continent->name, route('continent', [
        'name' => $continent->name
    ]));
});

Breadcrumbs::register('country', function ($breadcrumbs, $continent, $country) {
    $breadcrumbs->parent('continent', $continent);
    $breadcrumbs->push($country->name, route('country', [
        'name' => $country->name
    ]));
});

Breadcrumbs::register('city', function ($breadcrumbs, $continent, $country, $city) {
    $breadcrumbs->parent('country', $continent, $country);
    $breadcrumbs->push($city->name, route('city', [
        'name' => $city->name
    ]));
});

Мы добавили еще 2 хлебные крошки: country и city. Они нужны для того, чтобы все хлебные крошки могли эффективно связываться друг с другом, когда выборка вызывает цепь навигации, в которой больше одной крошки.

Методы для регистрации и вывода хлебных крошек country и city те же, что и для continent. Мы не будем рассматривать их подробно.

Чтобы обработать хлебную крошку в коде выборки для страницы city, нужно вставить в нужное место этот кусок кода:

resources/views/city.blade.php

{{ Breadcrumbs::render('city', $city->country->continent, $city->country, $city) }}

Метод render получает имя хлебной крошки в качестве первого аргумента. Далее он получает аргумент $city->country->continent, затем получается результат вхождения модели континента. Также метод получает аргумент $city->country, который является страной, к которой принадлежит город, и вхождение $city.

Вот и все. Мы полностью разработали полностью функциональную динамическую навигацию хлебных крошек всего в нескольких строках кода.

Заключение

В мире, в котором пользователи интернет-служб ищут самые простые продукты, рано или поздно разработчикам бы пришлось добавить хлебные крошки. Эта статья показала, что всего с несколькими строками кода можно создать полностью работающую службу навигации. С пакетом Laravel хлебные крошки можно создать еще много всего (например, можно влиять на стиль и разметку). Изучайте его мощь самостоятельно.

Реклама
Комментариев еще не оставлено
no_avatar