В прошлом веб-разработчиков больше волновали функции программ и информация, которую они могли передать, а вот на внешний вид веб-страниц они не обращали внимание. Не то что бы они в этом виноваты, ведь в те времена не было таких технологий, которые бы позволяли разрабатывать сложные дизайны веб-страниц. Те же, которые могли сделать что-то магическое, были слишком трудны в использовании.
Важность хлебных крошек для сайта
В настоящее время наблюдается совершенно иная ситуация. В веб-разработках этап 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](https://api.falbar.one/storage/images/609/78e/174/3u59v7r1z-rendering-dinamicheskoj-xlebnoj-kroshki.jpg)
Чтобы сгенерировать цепь навигации хлебных крошек, используя отношения между моделями, как выше, начнем с написания кода, который регистрирует хлебную крошку 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](https://api.falbar.one/storage/images/609/78e/174/s6egkgxnw-lokalnom-kompyutere-posle-laravel.jpg)
Рендеринг полной цепи навигации
Мы рассмотрели код, в котором обрабатываются хлебные крошки по отдельности. Теперь посмотрим на код и логику, нужные для обработки всей цепи. В этой статье мы обработаем страницу города, которая относится к странице страны, а та - к континенту, последняя - к домашней странице.
Это финальный код файла 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 хлебные крошки можно создать еще много всего (например, можно влиять на стиль и разметку). Изучайте его мощь самостоятельно.