Skip to main content

Новая страница

Для создания нового проекта с использованием фрэймворка Laravel необходимо установить менеджер php пакетов Composer. На момент написания этой методической работы вышла версия composer 2.7.6. https://getcomposer.org/Composer-Setup.exe

Во время установки будет предложено установить для одного или нескольких пользователей (этот этап не особо важен). После этого установщик должен определить где в системе установлен исполняемый файл php.exe. Если этот файл не будет найден или вы захотите использовать другую версию php, укажите ссылку на него. Во время работы в аудитории используется программное обеспечение xamp, по этому путь до php будет определен ссылаясь на папку установки xamp. После установки необходимо запустить консоль cmd или powershell и набрать команду:

composer -V

Если в консоли вы увидите информацию об установленном менеджере пакетов, то можно переходить к следующему этапу, если появилась ошибка, то убедитесь, что в системных путях появилась соответствующая запись. Чтобы создать новый проект необходимо выполнить набор команд находясь в консоли терминала, cmd или powershell, при этом заранее открыв командную строку там, где будет находиться проект или сменив директорию с помощью команды cd или Set-Location.

# УСТАНОВКА LARAVEL ЧЕРЕЗ COMPOSER
# Глобальная установка laravel. Выполняется один раз, в дальнейшем выполнять не нужно.
composer global require laravel/installer
# Создание нового проекта.
laravel new example-app

При запуске команды создания нового проекта появится интерактивное окно с предложением выбрать стартовый набор функционала. Введите в консоли none и нажмите enter. На вопрос о выборе framework-а для php введите 0 или просто нажмите enter для продолжения. На вопрос об инициализации git репозитория ответьте отрицательно или нажмите enter для продолжения, если в квадратных скобках стоит вариант no. Начнется установка, которая займет определенное время. В конце установки будет предложено выбрать тип базы данных для подключения. Вариант по умолчанию SQLite создаст файловую базу данных в директории проекта. Выберите этот вариант по умолчанию, чуть позже мы его изменим на подключение к базе данных в локальной сети. После окончания установки смените директорию в консоли и выполните запуск локального сервера.

# Переход в папку с проектом.
cd example-app
# Запуск локального сервера.
php artisan serve

В консоли должен появиться адрес формата http://127.0.0.1:8000, откройте его в браузере. Вы увидите стартовую страницу и версию Laravel и PHP в самом низу страницы. Для работы с кодом лучше всего использовать редактор visual code. Установите Visual Code, если он отсутствует в системе. Откройте директорию с проектом в visual code. Для этого откройте директорию example-app и через правую кнопку мыши вызовите диалоговое окно и выберите "Открыть с помощью Code". Или запустите VS CODE и откройте директорию напрямую через File->Open Folder. Сразу же изменим подключение к базе данных на mariadb. Для этого откройте файл .env и измените указанные столбцы:

DB_CONNECTION=mysql # Замените SQLite на mysql
DB_HOST=172.17.1.73
DB_PORT=3306
DB_DATABASE=ivanovNN # Измените имя базы данных
DB_USERNAME=pract
DB_PASSWORD=347456

Измените параметр DB_DATABASE на свои ФИО.

Если теперь вы откроете и обновите главную страницу, то обнаружите, что пользователю было запрещено подключение к указанной вами базе данных. Эта ошибка должна сказать нам следующее, что само подключение было выполнено успешно и была выполнена проверка на наличие базы данных. Создать базу данных можно двумя способами. Первый включает в себя подключение к серверу баз данных и создание базы данных вручную, второй автоматический. Laravel начиная с версии 8 умеет самостоятельно выполнять создание базы данных, если у пользователя имеются на это права. Для этого в Visual Code откройте терминал View->Terminal и выполните следующую команду.

php artisan migrate

Вы получите сообщение о том, что база данных не существует, но появится возможность ее создать ответив yes на запрос. После этого обновите страницу и убедитесь, что снова отображается главная страница.

Сервер возвращает домашнюю страницу при запросе потому, что по умолчанию в новом проекте уже прописан один маршрут. Маршруты прописываются в файле web.php находящимся в папке routes. Мы видим, что здесь вызывается статический метод get класса Route, который получает маршрут (в данном случае "/" как домашнюю страницу) и выполняет функцию, которая возвращает  функцию view которой передано значение "welcome" (обычно это html страница). То, что возвращает маршрут находится в папке resources/views. Там находится файл "welcome.blade.php", часть с строкой "welcome" совпадает с написанным в маршруте, но оставшуюся часть названия прописывать не нужно. Открыв этот файл вы увидите много html кода внутри, это как раз то что было возвращено в браузере при открытии домашней страницы нашего проекта. Можно внести изменения в текущий код и посмотреть на изменения в браузере чтобы убедиться. Внесем изменения в маршрут и проверим работу маршрутов laravel на практике:

Route::get('/home', function () {
    return view('welcome');
});

При открытии главной страницы веб сервер вернет страницу 404, так как такого маршрута больше не существует, но если добавить к адресной строке '/home', то откроется предыдущая страничка welcome. После проверки вернем маршрут в прежнее состояние. Обратите внимание, что функция может возвращать не только html страницы, но и почти что угодно. Например:

Route::get('/', function () {
    return 'Hello world';
});

Laravel автоматически преобразует строку в нужный для вывода формат и вернет пользователю. Еще одним примером является возврат json записи:

Route::get('/', function () {
    return ['foo' => 'bar'];
});

Можете заранее поставить себе расширение для браузера JSON Formatter, для более структурированного чтения такого формата. После проверки верните маршруту прежний вид. Перейдем к файлу welcome.blade.php и сотрем все его содержимое, оставив только базовую html конструкцию. Обратите внимание на название файла, blade.php является некой надстройкой над php, позволяющая использовать различные функции и улучшения. Для того чтобы не переписывать код страницы воспользуйтесь механизмом автоматической генерации базовых страниц в vs code введя знак "!" и нажав tab.

<!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>Document</title>
</head>
<body>
    <h1>Hello Wordl</h1>
</body>
</html>

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

Route::get('/about', function () {
    return view('welcome');
});

В директории routes создайте файл about.php и заполните его простым html кодом как и welcome. Обратите внимание, что пропуск части имени с blade не повлияла на работу и страничка открывается. В адресной строке для этого допишите путь /about.

Дополнительное задание. Создайте новый маршрут contact и добавьте соответствующий php файл. Изменим так же файл маршрута для домашней страницы на home. Для этого переименуйте файл welcome.blade.php и измените значение в функции view на home. Так как перемещаться по страничкам не очень удобно, добавим в home.blade.php внутрь тега body навигационное меню.

    <nav>
        <a href="/">HOME</a>
        <a href="/about">ABOUT</a>
        <a href="/contact">CONTACT</a>
    </nav>

Такой вариант естественно сработает, но обратите внимание, что переходя по страницам мы теряем наше навигационное меню. Если вы добавите этот код на каждую страничку, то и меню будет на каждой, но таким методом мы вводим такое понятие как дупликация да и это не самый верный вариант, если мы захотим изменить меню на одной странице, то придется менять на каждой. Верните в именах php файлов строку blade. Это даст возможность воспользоваться функционалом механизма шаблонов в Laravel, по сути это некая надстройка над файлом php, когда документ компилируется для вывода, он содержит различные упрощения синтаксиса. Создайте в директории views директорию Components (важно соблюдать именно такое название директории). Здесь мы будем располагать специальные блоки, которые будут часто задействованы в коде, например навигационное меню, кнопки, выпадающий списки, баннеры и тд. Это называется layout или макетом, компонентом. Текущее содержимое файла home.blade.php:

<!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>Document</title>
</head>
<body>
    <nav> # Такое же навигационное меню мы размещали в contact и about
        <a href="/">HOME</a>
        <a href="/about">ABOUT</a>
        <a href="/contact">CONTACT</a>
    </nav>
    <h1>HELLO ON HOME PAGE</h1>
</body>
</html>

Создадим файл с именем layout.blade.php в директории Components, которую создали ранее. Вырежем содержимое файла home.blade.php и поместим его в этот файл. Естественно если открыть главную страницу то там теперь пусто. Теперь сошлемся на наш макет. Делается это так, как будто мы ссылаемся на пользовательский (custom) html тэг. В качестве имени тэга выступает имя макета, в нашем случае это layout. Сам тэг начинается с буквы "x", таким образом мы указываем, что не используются никакие существующие html тэги. Текущее содержимое файла home.blade.php

<x-layout>

</x-layout>

Готово. Проверьте содержимое главной страницы в браузере. Теперь осталось зайти в макет и вынести из него все то, что является уникальным для текущей страницы. В нашем случае это текст внутри тэга h1. Вырежьте строку с тэгом h1 и поместите ее внутрь тэга x-layout. Если вы обновите страницу, то текста вы не увидите, все потому, что в макете нам нужно указать, где будет располагаться добавляемый нами материал. Откройте тэг php и с помощью функции echo сделайте ссылку на переменную $slot. Содержимое файла layout.blade.php

<!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>Document</title>
</head>
<body>
    <nav>
        <a href="/">HOME</a>
        <a href="/about">ABOUT</a>
        <a href="/contact">CONTACT</a>
    </nav>
    <?php echo $slot ?> # Указываем где будет размещаться код, уникальный для каждой страницы.
</body>
</html>

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

{{ $slot }}

Замените на страницах contact и about содержимое на тэг x-layout и заголовок h1 соответственно. Теперь если нам понадобится добавить еще одну ссылку в навигационное меню мы можем внести изменение в макет и изменения отобразятся везде, где мы сделали ссылку на макет. Теперь попробуем сделать сами ссылки на страницы динамическими. Для начала в файле layout.blade.php заменим ссылку на домашнюю страницу следующим тэгом:

        <x-nav-link></x-nav-link>

А саму ссылку вынесем в файл nav-link.blade.php в директории Components. В данный момент это действие ничего не изменило в структуре страницы, по этому изменим саму ссылку следующим образом. Файл nav-link.blade.php:

<a href="/">{{ $slot }}</a>

Файл layout.blade.php:

<!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>Document</title>
</head>
<body>
    <nav>
        <x-nav-link>Home</x-nav-link> // Мы ссылаемся на ссылку и подставляем свое значение имени для этой ссылки
        <x-nav-link>About</x-nav-link>
        <x-nav-link>Contact</x-nav-link>
    </nav>
    {{ $slot }}
</body>
</html>

Обратите внимание, что все ссылки ведут на одну и тужу страницу. Нам нужно передать ссылку, ссылкой будет являться параметр href. Этими параметрами может быть что угодно, href, class, id и тд. Передадим первый параметр (напоминаю, мы сделали в качестве макета, компонента ссылку и теперь указываем уникальное значение для каждой ссылки в нашем навигационном меню):

Теперь этот параметр необходимо принять и делается это с помощью атрибутов.

<a {{ $attributes }}>{{ $slot }}</a> // имейте в виду, что $attributes является объектом, что позволяет производить манипуляции с данными выполняя различные функции.

Приведем содержимое навигационного меню файла layout.blade.php к следующему виду:

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

Создадим красивый общий макет для наших страниц. Для этого воспользуемся css библиотекой tailwind. Изучение применения этой библиотеки расширит возможности работы с сайтами и вы можете заняться этим самостоятельно.

Сперва удалим файл nav-link.blade.php. После этого скопируйте следующий код в файл layout.blade.php с заменой.

<!DOCTYPE html>
<html lang="en" class="h-full bg-gray-100">
<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>My Website</title>
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="h-full">

<div class="min-h-full">
    <nav class="bg-gray-800">
      <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
        <div class="flex h-16 items-center justify-between">
          <div class="flex items-center">
            <div class="flex-shrink-0">
              <img class="h-8 w-8" src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=500" alt="Your Company">
            </div>
            <div class="hidden md:block">
              <div class="ml-10 flex items-baseline space-x-4">
                <a href="/" class="bg-gray-900 text-white rounded-md px-3 py-2 text-sm font-medium" aria-current="page">Home</a>
                <a href="/about" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">About</a>
                <a href="/contact" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">Contact</a>
              </div>
            </div>
          </div>
          <div class="hidden md:block">
            <div class="ml-4 flex items-center md:ml-6">
              <button type="button" class="relative rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800">
                <span class="absolute -inset-1.5"></span>
                <span class="sr-only">View notifications</span>
                <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
                  <path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" />
                </svg>
              </button>
  
              <div class="relative ml-3">
                <div>
                  <button type="button" class="relative flex max-w-xs items-center rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" id="user-menu-button" aria-expanded="false" aria-haspopup="true">
                    <span class="absolute -inset-1.5"></span>
                    <span class="sr-only">Open user menu</span>
                    <img class="h-8 w-8 rounded-full" src="https://e7.pngegg.com/pngimages/115/92/png-clipart-circle-logo-circle-blue-logo-thumbnail.png" alt="">
                  
                </button>
                </div>
  
              </div>
            </div>
          </div>
          <div class="-mr-2 flex md:hidden">
            <!-- Mobile menu button -->
            <button type="button" class="relative inline-flex items-center justify-center rounded-md bg-gray-800 p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" aria-controls="mobile-menu" aria-expanded="false">
              <span class="absolute -inset-0.5"></span>
              <span class="sr-only">Open main menu</span>
              <!-- Menu open: "hidden", Menu closed: "block" -->
              <svg class="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
                <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
              </svg>
              <!-- Menu open: "block", Menu closed: "hidden" -->
              <svg class="hidden h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
                <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
              </svg>
            </button>
          </div>
        </div>
      </div>
  
      <!-- Mobile menu, show/hide based on menu state. -->
      <div class="md:hidden" id="mobile-menu">
        <div class="space-y-1 px-2 pb-3 pt-2 sm:px-3">
          <!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" -->
          <a href="/" class="bg-gray-900 text-white block rounded-md px-3 py-2 text-base font-medium" aria-current="page">Home</a>
          <a href="/about" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">About</a>
          <a href="/contact" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Contact</a>

        </div>
        <div class="border-t border-gray-700 pb-3 pt-4">
          <div class="flex items-center px-5">
            <div class="flex-shrink-0">
              <img class="h-10 w-10 rounded-full" src="https://e7.pngegg.com/pngimages/115/92/png-clipart-circle-logo-circle-blue-logo-thumbnail.png" alt="">
            </div>
            <div class="ml-3">
              <div class="text-base font-medium leading-none text-white">Zhgmk Student</div>
              <div class="text-sm font-medium leading-none text-gray-400">contact@zhgmk.ru</div>
            </div>
            <button type="button" class="relative ml-auto flex-shrink-0 rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800">
              <span class="absolute -inset-1.5"></span>
              <span class="sr-only">View notifications</span>
              <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
                <path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" />
              </svg>
            </button>
          </div>

        </div>
      </div>
    </nav>
  
    <header class="bg-white shadow">
      <div class="mx-auto max-w-7xl px-4 py-6 sm:px-6 lg:px-8">
        <h1 class="text-3xl font-bold tracking-tight text-gray-900">Dashboard</h1>
      </div>
    </header>
    <main>
      <div class="mx-auto max-w-7xl py-6 sm:px-6 lg:px-8">
        <!-- Your content -->
        Уникальный контент помещать сюда
      </div>
    </main>
  </div>
  
</body>
</html>

В этом файле представлено навигационное меню как для полноценной версии так и для мобильной версии сайта. Проверьте что страница у вас открывается (в мобильной версии тоже) и ссылки работают. В конце кода замените "Уникальный контент помещать сюда" на:

{{ $slot }}

Переключаясь по ссылкам мы видим, что в зависимости от страницы на которой мы находимся у нас меняется содержимое уникального блока. Надпись Dashboard (предполагаемый заголовок) в свою очередь не меняется, потому что она прописана напрямую. Эта надпись тоже должна динамически меняться. Создадим переменную и поместим ее в тэге php с функцией echo. За это у нас отвечают две фигурные скобки. Замените саму надпись Dashboard на:

{{ $heading }}

Имя переменной мы просто придумываем (heading = заголовок). Но теперь при обновлении страницы мы получим ошибку о том, что переменная не объявлена. Рассмотрим файл home.blade.php:

<x-layout>

    <h1>HELLO ON HOME PAGE</h1>

</x-layout>

Содержимое тега h1 попадет в переменную $slot, которая является переменной по умолчанию для основного слота, куда будет размещено содержимое в layout.blade.php. Но мы знаем, что помимо этого в другом месте нашего кода нужно разместить динамически содержимое и для этого мы создаем еще один слот и называем его:

<x-layout>

    <x-slot:heading>
    HOME PAGE
    </x-slot:heading>

    <h1>HELLO ON HOME PAGE</h1>
    
</x-layout>

Обновите файлы contact.blade.php и about.blade.php соответственно. После этого проверьте что у вас открываются другие странички и их содержимое выводится на экран. Обратите внимание на то, что при открытии страницы контактов или информации выделение в навигационном меню держится на страничке Home. Начнем с того, что рассмотрим наши ссылки:

          <a href="/" class="bg-gray-900 text-white block rounded-md px-3 py-2 text-base font-medium" aria-current="page">Home</a>

          <a href="/about" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">About</a>

          <a href="/contact" class="text-gray-300 hover:bg-gray-700 hover:text-white block rounded-md px-3 py-2 text-base font-medium">Contact</a>

В данный момент у нас выделен элемент меню Home. Для выделенного элемента прописан следующий стиль:

bg-gray-900 // В Tailwind значение 900 означает самое темное
text-white // Текст белый, для отличия на темном фоне
text-gray-300 // Цвет текста светло серый, чтобы не выделяться на фоне Home
hover:bg-gray-700 // При выделении элемента менять фон на темно серый
hover:text-white // При выделении элемента менять текст на белый

Для того чтобы динамически менять выделенный в данный момент элемент напишем условие, которое будет проверять какая сейчас открыта страница. Сравним строчку с ссылкой Home до и после:

<a href="/" class="bg-gray-900 text-white block rounded-md px-3 py-2 text-base font-medium" aria-current="page">Home</a> // До изменений

<a href="/" class="{{ true ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white' }} rounded-md px-3 py-2 text-sm font-medium" aria-current="page">Home</a> // С условием

Условие, записанное в фигурных скобках (напоминаю это конструкция ), звучит следующим образом. Если значение true, то меняем цвет фона на темно серый а текст на белый, а в другом случае (символ обозначающий else ":") текст делаем светло серым, а при выделении подсвечиваем фон. Замените строку на новую и поменяйте значение true на false и проверьте результат. Теперь осталось заменить прямо прописанное утверждение на проверку. В Laravel есть метод под названием request(). Методом называют функцию определенную внутри класса в php. Считайте что это обычная функция. Этот метод можно вызвать для получения информации о текущем запросе. Таким образом мы проверяем если текущий запрос равен указанному маршруту, то выполним условие.

<a href="/" class="{{ request()->is('/') ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white' }} rounded-md px-3 py-2 text-sm font-medium" aria-current="page">Home</a>

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

Теперь снова введем макет nav-link.blade.php, создайте его в директории Components. В качестве содержимого файла мы скопируем полностью ссылку из файла layout.blade.php, но уберем тэг href, так как ссылка это уникальное значение и она будет передаваться.

#Содержимое файла nav-link.blade.php
<a class="{{ request()->is('/') ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white' }} rounded-md px-3 py-2 text-sm font-medium" 
    aria-current="page"
    >Home</a>
// Такие переносы строки можно делать для удобства.

А ссылки в файле layout.blade.php заменим на тэг "x-nav-link" и удалим class и все что после него. Приведите ссылки к следующему виду:

Вернемся к файлу nav-link.blade.php. Код aria-current отвечает за проверку является ли данная страница текущей и имеет два значения "page" и "false". Таким образом мы можем проверять это динамически используй уже известную функцию request(), где если страница является текущей подставляется значение "page" а если нет, то "false".

#Содержимое файла nav-link.blade.php
<a class="{{ request()->is('/') ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white' }} rounded-md px-3 py-2 text-sm font-medium" 
    aria-current="{{ request()->is('/') ? 'page' : 'false' }}"
    >Home</a>

Теперь что касается имени ссылки навигационного меню Home. Так как это уникальное значение, то мы его будем подставлять используя переменную $slot.

#Содержимое файла nav-link.blade.php
<a class="{{ request()->is('/') ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white' }} rounded-md px-3 py-2 text-sm font-medium" 
    aria-current="{{ request()->is('/') ? 'page' : 'false' }}"
    >{{$slot}}</a>

Теперь что касается ссылки, мы можем поступить разными способами, например как ранее передавая атрибуты, но вместо этого введем такое определение как props (свойства). Свойствами является все, что не является атрибутом. В этом нам поможет объявление директивы в начале кода. Директивами выступают различные функции в Laravel, это могут быть функция if или foreach, функция для отладки кода и тд. Мы объявляем директиву props и передаем ей массив, который содержит свойство active.

@props(['active'])
<a class="{{ request()->is('/') ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white' }} rounded-md px-3 py-2 text-sm font-medium" 
    aria-current="{{ request()->is('/') ? 'page' : 'false' }}"
    >{{$slot}}</a>

Рассмотри на примере в чем различия. Добавим функцию php echo (напоминание, echo функция выводит текст, как функция print и т.д.) и выведем все атрибуты, затем в одну из ссылок, допустим contact добавим несколько свойств, такие как id и name.

@props(['active'])
<a class="{{ request()->is('/') ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white' }} rounded-md px-3 py-2 text-sm font-medium" 
    aria-current="{{ request()->is('/') ? 'page' : 'false' }}"
    {{ $attributes }}
    >{{$slot}}</a>

Изменим ссылку соответственно:

// Часть содержимого layout.blade.php
<x-nav-link href="/contact" id="randomtext" name="randomnames"> Contact </x-nav-link>

Теперь откройте веб страницу и с помощью функционала браузера проанализируйте ссылку Contact (обычно это можно сделать нажав клавишу F12 в браузере) и после выделив ссылку вы увидите что все атрибуты были выведены функцией echo. Теперь измените ссылку следующим образом, указав имя свойства active и присвоив ему случайное значение:

Обновите страницу и снова проведите анализ ссылки, свойства active вы не увидите, потому что мы заранее объявили его как свойство, если бы мы это не сделали Laravel посчитал бы его как один из атрибутов и включил в вывод как один из атрибутов. Теперь обновим логику нашего условия так, чтобы значение передавалось извне. Сделаем это заменив проверку текущей страницы на переменную $active. Таким образом мы можем ссылаться на значения свойства и на случай если это свойство не передается присвоим ему значение по умолчанию:

@props(['active' => false]) // Мы присвоили значение по умолчанию на случай если свойство не будет передано
<a class="{{ $active ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white' }} rounded-md px-3 py-2 text-sm font-medium" 
    aria-current="{{ request()->is('/') ? 'page' : 'false' }}"
    {{ $attributes }}
    >{{$slot}}</a>

Но если мы сейчас обновим страницу нашего сайта, то увидим, что страница contact будет выделена независимо от того, что написано в свойстве active. А если мы передадим свойству значение false, то в условие мы все равно получим значениt true при проверке условия. Это потому что false передается как строковое значение, а проверка строкового значения состоящего из 5 символов вернет true. Поэтому мы хотим напрямую указать что мы имели в виду при присваивании значения свойству. Измените ссылку contact следующим образом:

Теперь при проверке выделение ссылки пропадет, затем измените значение на true и проверьте выполняется ли условие. Теперь мы можем вернуть нашу функцию request для того, чтобы проверить, является ли эта ссылка текущей и в случае успеха мы передадим свойству active значение true, а в любом другом случае false. Измените ссылки следующим образом:

Таким образом мы немного изучили компоненты blade в Laravel.

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

Route::get('/', function () {
    return view('home', [
        'greeting' => 'HELLO'
    ]);
});

Теперь, когда домашняя страница будет загружена мы будем иметь доступ к переменной $greeting. Вернемся к файлу home.blade.php. и заменим заголовок приветствия о том, какая эта страница на следующую строку: Содержимое файла home.blade.php на текущий момент:

<x-layout>
    <x-slot:heading>
    HOME PAGE
    </x-slot:heading>

    <h1>{{ $greeting }} ON HOME PAGE</h1>
</x-layout>

Так как это массив, то мы добавим еще одно значение, например name равное вашему имени.

Route::get('/', function () {
    return view('home', [
        'greeting' => 'HELLO',
        'name' => 'Алексей'
    ]);
});

И в файле home:

    <h1>{{ $greeting }} ON HOME PAGE. My name is {{ $name }}</h1>

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

Route::get('/', function () {
    return view('home', [
        'jobs' => [
            [
                'title' => 'Director',
                'salary' => '$50000'
            ],
            [
                'title' => 'Programmer',
                'salary' => '$10000'
            ],
            [
                'title' => 'Teacher',
                'salary' => '$1000'
            ]
            ]
    ]);
});

Теперь вернемся на главную страницу и перечислим профессии. Для этого воспользуемся функцией @foreach, что является сокращением конструкции цикла на php (благодаря директиве blade в Laravel). В самом цикле мы переберем значения из массива и выведем в виде списка (тэг li).

    @foreach ($jobs as $job)
        <li>{{$job['title']}}</li>
    @endforeach

Проверьте результат на главной странице. Теперь расширим результат суммой заработной платы:

    @foreach ($jobs as $job)
        <li> <strong>{{$job['title']}}:</strong> Заработная плата {{ $job['salary'] }} за год. </li>
    @endforeach

Переименуем файл about.blade.php в jobs.blade.php и заполним следующим образом:

<x-layout>
    <x-slot:heading>
        Список профессий
        </x-slot:heading>
</x-layout>

В маршруте перенесите массив из маршрута для главной страницы в маршрут about, который мы заменим на jobs: Два маршрута home и jobs:

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

Route::get('/jobs', function () {
    return view('jobs', [
        'jobs' => [
            [
                'title' => 'Director',
                'salary' => '$50000'
            ],
            [
                'title' => 'Programmer',
                'salary' => '$10000'
            ],
            [
                'title' => 'Teacher',
                'salary' => '$1000'
            ]
            ]
    ]);
});

Не забудьте заменить ссылку about на jobs в layout.blade.php в меню навигации:

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

Теперь попробуем сделать так, чтобы мы могли открывать ссылку нажимая на каждую профессию переходя на новую страницу с подробной информацией по профессии. Для того чтобы сделать из строки ссылку, поместим ее в тэг 'a'. Добавим тэг 'ul' для списка. Если вы его не напишите, браузер все равно его подставит, но хорошей практикой является соблюдение синтаксиса. Осталась ссылка 'href=' для ссылки. Ссылка будет вести на новый маршрут 'href="/jobs/"'. Где после jobs, в качестве окончания, будет некий id или slug (slug-ом как раз называют хвостик ссылки, ее последнюю часть).

<x-layout>
    <x-slot:heading> Список профессий </x-slot:heading>
    <ul>
        @foreach ($jobs as $job)

        <li>
            <a href="/jobs/">
                <strong>{{ $job["title"] }}:</strong> Заработная плата
                {{ $job["salary"] }} за год.
            </a>
        </li>

        @endforeach
    </ul>
</x-layout>

Осталось добавить параметр id в массив. Сделаем это попроще и в качестве id присвоим числа по порядку.

Route::get('/jobs', function () {
    return view('jobs', [
        'jobs' => [
            [
                'id'    => '1',
                'title' => 'Director',
                'salary' => '$50000'
            ],
            [
                'id'    => '2',
                'title' => 'Programmer',
                'salary' => '$10000'
            ],
            [
                'id'    => '3',
                'title' => 'Teacher',
                'salary' => '$1000'
            ]
            ]
    ]);
});

Таким образом мы имеем ключ, который будет уникален для записи. Теперь если мы напишем ссылку 'href="/jobs/1"', то предположительно мы обратимся к первой профессии и тд., но как мы для этого пропишем маршрут? Создадим еще один маршрут:

Route::get('/jobs/{id}', function ($id) {
    dd($id);
    return view('contact');
});

Обратите внимание сначала на путь. Laravel видя значение в фигурных скобках автоматически его захватывает и передает в функцию (callback function), после чего оно становится нам доступно. Мы добавили функцию dd(), для того чтобы вывести содержимое этой переменной. В адресной строке самостоятельно допишите /jobs/1 или /jobs/2 или /jobs/3.

[!Внимание] Обратный вызов — это любой исполняемый код, который передается в качестве аргумента другому коду, который, как ожидается, выполнит обратный вызов (выполнение) аргумента в заданный момент времени. Проще говоря, если ссылка на функцию передается другой функции в качестве аргумента для ее вызова, то она будет называться функцией обратного вызова.

Вам будет выведено значение 1,2 и 3 соответственно. Перенесем в маршрут список наших профессий. Перенести их нужно в функцию, а не вторым аргументом view. Изменим массив, указав что jobs это переменная с массивом и закроем фигурные скобки.

Route::get('/jobs/{id}', function ($id) {

    $jobs = [
        [
            'id'    => '1',
            'title' => 'Director',
            'salary' => '$50000'
        ],
        [
            'id'    => '2',
            'title' => 'Programmer',
            'salary' => '$10000'
        ],
        [
            'id'    => '3',
            'title' => 'Teacher',
            'salary' => '$1000'
        ]
        ];

    return view('contact');
});

Теперь попробуем написать условие, благодаря которому мы получим профессию по тому же id что было передано в ссылке. Мы можем снова с помощью цикла перебрать значения и найти то где id равно переданному. Но в Laravel есть специальные функции по работе с массивами. В visual code начните писать Arr и в выпадающем списке у вас должен появиться выпадающий список \Illuminate\Support\Arr . Выберите его нажав tab. В таком случае у вас в самом начале кода появится вот такая строка 'use Illuminate\Support\Arr;' и таким образом мы можем обращаться к функции Arr не ссылаясь на полный путь (считайте что мы импортировали данную библиотеку). Дописав после Arr два символа двоеточия появится полный набор функций по работе с массивами. Нас интересует функция first. Выберем ее и заполним следующим образом:

        \Illuminate\Support\Arr::first($jobs, function ($job){
            return $job['id'] == $id;
        });

Рассмотрим нашу функцию. Мы обратились к функции first, которой мы передали в качестве значения массив профессий. После этого функция переберет каждое значение в массиве и добавит его в переменную $job и во время выполнения этой функции мы вернем ту профессию, чья id будет равна числу переданному в ссылке. Но обратите внимание, что у вас появится ошибка о том, что переменная $id не найдена. Это можно поправить нескольким способами. По сути нам нужно пробросить эту переменную чтобы ее область видимости была и внутри нашей функции. Способ первый, более классический в php путем перечисления переменных которые можно использовать внутри:

        \Illuminate\Support\Arr::first($jobs, function ($job) use ($id) {
            return $job['id'] == $id;
        });

Второй способ работает в php начиная с версии 8 и называется линейными функциями:

        Arr::first($jobs, fn($job) => $job['id'] == $id);

Такой способ написания гораздо короче, но может быть непривычным в начале. В качестве задания расшифруйте данную функцию и запишите её фразами. Проверим, получилось ли у нас записав результат выполнения этой функции в переменную $job и выведя ее значение используя функцию отладки dd().

        $job = Arr::first($jobs, fn($job) => $job['id'] == $id);
        dd($job);

Откройте браузер и проверьте ссылку по типу /jobs/1 снова. Что выводится у вас на экране? Если ввести ссылку по типу /jobs/5 то мы получим null. Пользователи могут запрашивать несуществующие ссылки и это тоже нужно учитывать и обрабатывать такие типы запросов. Уберите функцию dd и измените функцию view так, чтобы открывался файл с именем job и передайте в качестве второго аргумента переменную job присвоив ей значение переменной $job, значения которой мы получили ранее.

    return view('job', ['job' => $job]);

Осталось создать файл job.blade.php и заполнить его.

<x-layout>
    <x-slot:heading> Профессия </x-slot:heading>
<h2 class="font-bold text-lg"> {{$job['title']}} </h2>

<p>
    Годовая зарплата для этой професси {{ $job['salary'] }}.
</p>
</x-layout>

Обновим ссылки на страничке профессий jobs.blade.php: Содержимое файла jobs.blade.php на текущий момент:

<x-layout>
    <x-slot:heading> Список профессий </x-slot:heading>
    <ul>
        @foreach ($jobs as $job)

        <li>
            <a
                href="/jobs/{{ $job['id'] }}"
                class="text-blue-500 hover:underline"
            >
                <strong>{{ $job["title"] }}:</strong> Заработная плата
                {{ $job["salary"] }} за год.
            </a>
        </li>

        @endforeach
    </ul>
</x-layout>

Таким образом нам не пришлось создавать много маршрутов и файлов в директории view, а обошлись одним, на который данные передаются динамически.

Обратите внимание на то, что в файле web.blade.php у нас присутствует повторение кода. Попробуем исправить это, постепенно перейдя к новой теме. Текущее сопровождение файла web.blade.php (скопируйте или адаптируйте его под себя):

<?php

use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Route;

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

Route::get('/jobs', function () {
    return view('jobs', [
        'jobs' => [
            [
                'id'    => '1',
                'title' => 'Director',
                'salary' => '$50000'
            ],
            [
                'id'    => '2',
                'title' => 'Programmer',
                'salary' => '$10000'
            ],
            [
                'id'    => '3',
                'title' => 'Teacher',
                'salary' => '$1000'
            ]
            ]
    ]);
});

Route::get('/jobs/{id}', function ($id) {

    $jobs = [
        [
            'id'    => '1',
            'title' => 'Director',
            'salary' => '$50000'
        ],
        [
            'id'    => '2',
            'title' => 'Programmer',
            'salary' => '$10000'
        ],
        [
            'id'    => '3',
            'title' => 'Teacher',
            'salary' => '$1000'
        ]
        ];

        $job = Arr::first($jobs, fn($job) => $job['id'] == $id);

    return view('job', ['job' => $job]);
});

Route::get('/contact', function () {
    return view('contact');
});

Что можно сделать с повторяющимися массивами? Можно вынести их из функции и объявить глобально. Давайте попробуем.