laravel-watch Tailwind Templates

Laravel Watch

Laravel project using Blade, Tailwind CSS and PostgreSQL

laravel-watch

Laravel для новичков
Stack: Laravel, Blade Templates, PostgreSQL

Lesson 1. Подготовка окружения разработки

  • Установка Laravel Herd
  • Установка PHP через Laravel Herd
  • Создаем новый проект через консоль laravel new
  • Подключаем проект к Herd для работы с встроенным сервером
  • Создаем сервер БД PostgreSQL
  • Создаем БД
  • Настраеваем .env файл
  • Подключаемся к БД
  • Запускаем миграции php artisan migrate
  • herd unlink закроет доступ к приложению в браузере
  • Запуск защищенного соединения (TLS сертификат)с проектом herd secure

Lesson 2. Плагины

  • Для более удобной работы с Laravel проектами рекомендуется к установке плагин Laravel Idea

Lesson 3. Форматирование кода

  • Стандартный форматер кода
  • Настраеваем форматер Pint от Laravel

Lesson 4. Установка Tailwind CSS

  • Устанавливаем Tilewind CSS
  • Настраиваем tailwind.config.js
  • Устанавливаем npm зависимости (npm install)
  • Запускаем Vite сервер (npm run dev)
  • Подключаем стили tailwind @vite(.../app.css)
  • Указываем роут для тестирования подключения Tailwind

Lesson 5. Делаем разметку шапки сайта

  • Пишем html структуру
  • Добавляем иконку с сайта fontawesome.com в качестве логотипа
  • Указываем стили для логотипа при помощи класса "size-12"
  • Верстка меню навигации и определяем стили
  • Задаем контейнер для контента
  • Указываем настройки для контейнера в файле tailwind.config.js для центровки (theme:{container:{center:true}})
  • Задаем шапке fixed свойства

Lesson 6. Интерполяция в blade-шаблонах. Динамические значения

  • Двойные фигурные скобки в blade {{ | }} - Вывод экранированных данных с дополнительной защитой от XSS атак
  • config('app.name') - вывод значения APP_NAME из config -> app.php -> env -> APP_NAME
  • Присваивание имени пути Route::view('/', 'index')->name('index'); для обращения к пути через route('index')

Lesson 7. Маршруты

  • GET обращение к маршруту /courses и возврат строкового значения из функции
  • Рефакторим функцию в стрелочную для улучшения читаемости кода
  • Создаем маршруты для всех страниц сайта

Lesson 8. Компонент layout

  • Создаем папку components для хранения blade компонентов
  • Создаем компонент при помощи функционала Laravel Idea, create view
  • Создаем компонент layout.blade.php
  • Связываем Content c {{ $slot }}. В $slot падает весь контент, который был определен в связь организуется по названию компонентов и тега
  • Создаем компоненты для всех роутов

Lesson 9. Компоненты шапки

  • Разбиваем разметку хедера на компоненты
  • Выделяем ссылки навигации в массив при помощи директивы @php
  • Выводим ссылки при помощи директивы @foreach
  • В компоненте nav.blade мы определяем с атрибутом :href (с двоеточием), который в последствии упадет в {{ $attributes }} указанную в компоненте nav-item
  • Сокращаем :href так как имя атрибута совпадает с именем переменной, которую мы помещаем в качестве атрибута

Lesson 10. Подвал

  • Пишем структуру футера. Копирайт (©), дата (date('Y')), меню, социальные сети
  • Повторяем тот же вывод навигации как в шапка
  • Определяем маршруты. Для выделения повторяющихся слов используем Alt+J
  • Добавляем файлы с шаблонами новых маршрутов
  • Задаем стили для навигации в футере
  • Центрируем контент
  • Добавляем иконки соцсетей

Lesson 11. Компоненты подвала

  • Создаем директорию footer. Файл footer.blade.php перемещаем в созданную директорию и переименовываем в index.blade.php таким образом адрес к файлу footer находящийся в layout (x-footer) можно не менять.
  • Отделяем copyright
  • Переносим nav в директорию footer. Указываем Blade адрес
  • Сбрасываем кеш Blade командой php artisan view:clear
  • Выносим nav в отдельную директорию

Lesson 12. Перемещение пунктов навигации в layout

  • Оптимизируем nav использующийся в header и footer при помощи пропса :nav-items="$navItems"
  • Объединяем массивы с повторяющимися данными в корневом файле layout
  • Передаем массив в компоненты header и footer через props (инструмент передачи динамических данных) :nav-items="$navItems". Двоеточие перед именем пропса (nav-items) указывает, что мы передаем динамическое значение
  • Используем сокращенную нотацию для пропса :$navItems
  • Фильтруем массив для отображения конкретных данных в header и footer (В футере у нас отличаются два пункта)
  • Используем laravel helper collect для создания коллекции из нашего массива для более гибкой работы с ним
  • Используем laravel метод reject. Метод reject фильтрует коллекцию, используя заданное замыкание. Замыкание должно возвращать, true если элемент должен быть удален из результирующей коллекции:
  • Передаем отфильтрованную коллекцию дальше в дочерний элемент (nav)
  • Задаем route() пропсу :href чтобы исключить данную функцию из нашего массива и следовательно из метода reject
  • Оптимизируем вызовы хелпера collect, один раз обернув исходный массив, вместо оборачивания отдельно в футере и хедере

Lesson 13. Активный пункт навигации

  • В файле items отделяем переменную attributes в блок @php
  • Присваиваем переменной attributes значения по умолчанию через метод $attributes->merge(['class'])
  • Элементу class присваиваем стили по умолчанию через merge
  • Применяем условное слияние классов через метод class()
    • Метод class, принимающий массив классов, где ключ массива содержит класс или классы ('px-4, 'flex'), которые вы хотите добавить, а значение является логическим выражением.
    • Метод class применит наш класс указанный в ключе при выполнении условия true указанном в значении
  • Добавляем пропс isActive из родительского элемента для передачи его в item
  • При помощи request->routeIs($routeName)) проверяем соответствие входящего запроса "именованному маршруту"
    • Метод request() содержит текущий HTTP запрос
    • При помощи request()->path() можно проверить путь текущей страницы
    • Метод routeIs($val) выполняет проверку совпадает ли текущий маршрут с заданным именем $val и возвращает treu || false

Lesson 14. Компонент иконок

В данном уроке мы оптимизируем код вывода иконок соц. сетей при помощи отдельного массива
с названиями компании и её URL.

Данный урок был прослушан, но не повторен, потому как функцианал реализуемый лектором я ввел заранее в рамках самостоятельной работы.

Lesson 15. Перемещение пунктов навигации и ссылок на социальные сети в конфигурацию

  • В директории config создаем файл с названием проекта watch
  • Внутри watch делаем return содержащий многоменрый массив с названием nav_items содержащий в себе пары ключ=>значение элементов навигации
  • Убираем пропсы из кода. Теперь мы можем обращаться к элементам навигации через метод config('nav_items')
  • Оборачиваем config(nav_items) в метод collect. Возможно будет целесообразно обернуть в коллекцию исходный массив определенный в директории config
  • Повторяем тоже самое с иконками социальных сетей

Lesson 16. Основная секция главной страницы

  • Прибиваем футер к низу экрана
  • Верстка Tailwind. Наполняем index.blade.php контентом
  • Определяем section в отдельный blade шаблон x-hero/li>

Lesson 17. Секция новых курсов.

  • Верстка структуры блока с последними курсами
  • Вывод item курсов через перебор foreach(range(0, 3 as $i)) <- вернет массив элементов
  • Выносим верстку в отдельный компонент

Lesson 18. Рефакторинг иконок

  • Определяем маршрут course и задаем его нашим блокам курсов
  • Задаем маршрут у карточек курсов
  • Удаляем иконки из файла config
  • Создаем файл x-icon в котором будут храниться наши иконки
  • Внутри x-icon создаем ветвление @if ($name = "youtube")
  • Что бы прокинуть эту переменную в x-icon пишем

Lesson 19. Компонент курса

  • Создаем отдельный элемент для карточки курса
  • Создаем массив данных с информацией о курсе
  • Перебераем массив как $course и прокидываем эти данные пропсом в компонент курса

Lesson 20. Страница с курсами

  • Выносим массив с данными курса в отдельный файл (пока web) для доступа к нему из разных компонентов
  • Определяем роут (index) get c return view('/,['courses' => $courses]); используем замыкание (use) для передачи функции доступа к массиву $courses
  • Повторяем подобную маршрутизацию для courses для отображения внутри нее данных по курсам
  • Для блока с курсами на главной странице и странице с курсами указываем уникальные h2 прокидывая атрибут () как строку
  • Выводим "заглушку" курса (первый элемент массива $courses) по маршруту course
  • Правим верстку
  • В маршрутах, замыкания меняем на стрелочную функцию чтобы не использовать use, return и для более лаконичного вида

Lesson 21. Отображение курса по ID

  • У каждого элемента массива прописываем id
  • В маршруте определяем course/{id}, Так же указываем $id в качестве аргумента к функции роута и в переданном ей массиве
  • Прописываем id в роутах внутри ссылок навигационного меню

Lesson 22. Получение курсов из базы данных

  • Создаем таблицу для курсов php artisan make:migration create_courses_table
  • За что отвечают методы up и down в миграциях
  • Пишем структуру для таблицы с курсами (default,string,text,integer,nullable,)
  • migrate:reset | migrate
  • Заполняем таблицу произвольными данными
  • В роутах подключаемся к БД и забираем из неё все данные select
  • Используем функцию compact(). Создает массив, содержащий переменные и их значения.
  • Изменяем обращения к элементам прошлого массива с курсами, на обращение к объектам (->) $course->title
  • Считываем одну запись для страницы курса selestOne

Lesson 23. Контроллер курсов

  • Переносим логику для обработки маршрутов в контроллеры
  • Создаем контроллер php artisan make:controller CourseController
  • Создаем экшены index и show и переносим в них соответствующие маршруты
  • Указываем в качестве аргумента к show(string $id) для параметризированного роута
  • Указываем тип возвращаемого значения для экшена show (: view)
  • В маршруте указываем к какому экшену обратится роут для обработки запроса Route::get('/course/{id}', [CourseController::class, 'show'])->name('course');
  • Включаем интеграцию с БД в phpstorm

Lesson 24. Команды для работы с базой данных в терминале

Работа с БД через консоль на данный момент невозможна. Windows !

Lesson 25. Инспектирование SQL запросов

  • В файле AppServiceProvider в методе boot определяем слушатель SQL запросов
  • DB::listen(function(QueryExecuted $query)
  • Обращаемся к методам $query dump([$query->sql, $query->bindings, $query->time]); внутри DB::listen

Lesson 26. Конструктор SQL запросов

  • Переписываем запрос на получение всех данных из таблицы courses с помощью SQL конструктора через метод table->get
  • Переписываем запрос на получение данных из конкретного кортежа $course = DB::table('courses')->where('id', $id)->first();
  • Рассматриваем сокращенный вариант записи через метод find($id)
  • Инспектируем SQL запрос при помощи dump | $courses = DB::table('courses')->dump()->get();
  • Метод ddRowSql() - позволяет отобразить SQL запрос с учетом переданных динамических данных
  • query->toRawSql в AppServiceProvider
  • Для доступа метода toRawSql обновляем зависимости composer

Lesson 27. Группировка маршрутов

  • Оборачиваем схожие маршруты в Route::group
  • Указываем контроллер, который отвечает за обработку сгруппированных маршрутов
  • Используем метод prefix('courses') для сокращения длинны маршрута
  • Именуем маршруты по конвенции Laravel name('courses.index')
  • Используем метод name и удаляем префикс из названия маршрута name('index')
  • Меняем названия маршрутов (courses.show и courses.index) в ссылках ведущих на страницы курсов
  • Меняем путь до страницы Courses в файле конфигурации
  • Создаем новую директорию для хранения шаблонов страниц | pages
  • Меняем наименование компонентов на show и index
  • Переопределяем маршрут для view с учетом новой директории (pages)

Lesson 28. Модель курса

  • invokable controller - контроллер с одним методом
  • Создаем контроллер php artisan make:controller --invokable
  • Переносим содержимое роута главной страницы в созданный контроллер
  • Импортируем класс DB
  • Указываем инвокабл контроллер в роуте
  • Создаем модель
  • Используем модель в контроллере курса и Index контроллере

Lesson 29. Связывание модели с параметром маршрута

  • Связываем модель с маршрутом при помощи параметризированного роута. Неявное связывание модели с параметром маршрута
  • Указываем явно к какому полю в БД обращаться роуту для вывода данных /{course:lessons_count}
  • Рассматриваем getRouteKeyName для глобального обращения роутов к определенным колонкам в БД
  • Используем метод missing для отображения кастомных данный в случае если запись не будет найдена в БД
  • Используем класс Redirect

Lesson 30. Сидер курсов

  • Создаем класс сеятель для создания моковых данных курсов
  • В методе run Определяем класс Course с методом create внутри которого вкладываем массив с данными-заглушками
  • Тестируем сидер php artisan db:seed --class=CourseSeeder
  • В сидере определяем коллекцию с диапазоном значений (1, 10) для добавления 10 записей collect()->range(1, 10)->each(function (int $i)
  • Указываем переменную $i в поле 'title' => 'Course'.$i, для нумерации курсов
  • Course::truncate - метод для удаления данных из таблицы
  • Указываем наш класс сидера в DatabaseSeeder и выполняем db:seed (без указания класса сидера) для заполнения БД мок данными
  • Рассматриваем команду php artisan migrate:fresh для обновления миграций. флаг --seed дополнительно прокинет наши сиды

Lesson 31. Фабрика курсов

  • Создаем класс фабрики CourseFactory
  • Вызываем класс фабрики в CourseSeeder | Course::factory(10)->create(); в качестве аргумента factory() указываем кол-во создаваемых данных
  • Указываем в модели трейт use HasFactory;
  • Выполняем сидер
  • Генерируем данные при помощи faker
  • $this->faker->words(rand(2, 4), true)
  • $this->faker->text()
  • $this->faker->numberBetween(1, 50)
  • Используем faker с интерполяцией blade

Lesson 32. Генерация фабрик моделей

  • Правка верстки
  • Альтернативный синтаксис библиотеки fakerphp | глобальная функция fake()
  • Оборачиваем title в ucfirst - Верхний регистр первого символа строки
  • Выносим функции фейк данных в отдельную переменную для большей читаемости
  • fake()->paragraph(1)
  • generate helper code

Lesson 33. Метод sequence в фабриках

  • Добавляем массив с предопределенными данными для использования метода sequence в сидере
  • В методе factory получаем кол-во элементов массива $data (массив с предопред. данными)
  • Используем метод sequence и оператор спред (...)

Lesson 34. Пагинация

  • Course::paginate(); По умолчанию пагинация отображает 15 записей
  • В конфигурационном файле tailwind указываем путь до стилей к пагинации
  • darkMode: 'selector'
  • Указываем методу paginate(4) в качестве аргумента кол-во элементов для вывода на одной странице
  • На {{ $courses->links() }} вешаем проверку на существование метода, для отображения пагинации только на странице курсов method_exists
  • В контроллере IndexController указываем $courses=Course::take(4)->get() для отображения 4 карточек курсов

Lesson 35. Сортировка курсов

  • В инвокабл контроллере добавляем метод latest для сортировки курсов
  • Метод oldest - сортировка в порядке возрастания
  • Метод сортировки orderBy() - Сортировка по определенному значению указанному в аргементе метода

Lesson 36. Форма поиска курсов

  • Отвязываем логику компонента с курсами от логики курсов на главной странице (Правим шаблон с пагинацией)
  • Создаем компонент поисковой строки
  • Определяем иконку поиска в компонент x-icon
  • Прописываем стили для формы поиска
  • Установка tailwindCSS forms
  • Правка стилей

Lesson 37. Поиск курсов

  • В форме поиска указываем action ведущий на страницу с курсами courses.index
  • В CourseController задаем доступ к объекту request | index(Request $request)
  • Указываем имя поисковому полю () | В request->all упадет массив search=>... при обращении к адресу ...test/courses?search=bla-bla
  • При указании request->search мы получим только строку указанную после ?search=
  • Тестируем отправку запроса из input в поимковую строку
  • К $courses применяем метод whereLike('title', "%{$request->search}%") В качестве аргументов указываем по какому ключу будем проводить сравнение, а также шаблон SQL запроса к БД
  • Используем функцию orWhereLike('description', "%{$request->search}%") для поиска по описанию'
  • В результате получим запрос select * from 'courses' where 'title like ? or 'description like ? order by 'created_at' desc
  • Указываем или для того чтобы запрос оставался в поле инпута после отправки
  • Используем метод withQueryString() для правильного взаимодействия поиска с пагинацией (при выдаче результата по поиску и переходу на новую страницу запрос перестает быть актуальным и пагинатор выдает нам все страницы вместо тех, которые соответствуют запросу)

Lesson 38. Страница курса

  • Рефакторинг верстки страницы с курсом
  • В шаблоне курса выводим информацию о последнем обновлении {{ $course->updated_at->format('d.m.Y') }}

Lesson 39. Список уроков

  • Верстка блока со списком уроков. Использование спец символа ·
  • Вывод списка уроков через foreach

Lesson 40. Группировка компонентов ресурса курсов

  • Рефакторинг. Разбиение страницы с курсом на компоненты

Lesson 41. Заполнение таблицы уроков

  • Создаем модель Lesson, а так же фабрику, сидер и миграции
  • Определяем необходимые нам поля в миграциях
  • Заполняем фабрику и сидер
  • Работа с функцией mt_rand() (в фабрике)
  • Добавляем класс LessonSeeder в массив вызовов внутри DatabaseSeeder
  • Запускаем миграцию
  • Прокидываем данные из модели Lesson в компонент курсаю Работа с методом take()
  • Указываем данные полученные из класса Lesson и раскидываем их в компоненте {{$lessons->title}} и т.д.

Lesson 42. Внешние ключи (foreign keys)

  • Добавляем в миграции lessons, колонку с id (course_id) для связывания таблиц через внешние ключи (foreign key)
  • Работа с методом foreign, а также references для связывания course_id (lessons) и id->on('courses')
  • Работа с методом foreignId('course_id')->constrained(table: 'courses');
  • Метод onUpdate('cascade') при обновлении кортежа обновит так же связанный с ним кортеж
  • Метод onDelete('cascade') при удалении кортежа так же удалит связанный с ним кортеж
  • Метод foreignIdFor(Class::class) - альтернативный синтаксис для реализации связывания таблиц
  • cascadeOnUpdate и cascadeOnDelete - работают вметсе с foreignIdFor(Class::class)
  • Course::inRandomOrder->first()->id() - связывание одной записи с другой в хаотичном порядке
  • Schema::withoutForeignKeyConstraints() - отключение проверок

Lesson 43. Отношение один ко многим

  • Определив public function lessons(){}:HasMany, которая вернет $this->hasMany(Lesson::class) мы связываем модель Course с моделью Lesson
  • Проверяем полученные (связанные) данные через интерфейс командной строки tinker
  • Обращаемся к свойству lessons ($course->lessons) в котором находятся связанные с id <-> course_id данные
  • Помещаем внешнее свойство в компонент
  • Используем метод $lessons->count() для подсчета кол-ва уроков в коллекции
  • Если мы не следуем соглашениям именования, мы можем указать связь таблиц (в Models\Course) явно $this->hasMany(Lesson::class, 'course_id', 'id')

Lesson 44. Отношение один ко многим

  • Меняем логику генерации случайных данных в LessonSeeder для кол-ва уроков в курсах | Course::each. В логике указываем добавление данных в поле 'number'
  • В модели Lesson описываем отношение "Многие к одному" belongsTo
  • При помощи Lesson::factory()->for($course) в LessonSeeder указываем с какой моделью связать сгенерированные данные
  • Выводим данные о кол-ве уроков в карточку курса
  • Удаляем добавление фейковых данных в таблицу из factory lessons_count и саму колонку lessons_count из миграций
  • В уроках по отношениям осталось много магии. К данным урокам нужно вернуться и посмотреть их повторно
    В LessonSeeder мы обращаемся к Course::each.
    Перебираем курсы и для каждого из них генерируем определенное кол-во уроков
    так же связываем текущие сгенерированные уроки с конкретным курсом и 
    добавляем в таблицу данных number курса.
    

Lesson 45. Страница урока

  • Создаем контроллер в котором создаем метод show
  • Определяем маршрут для урока и указываем к какому контроллеру и экшену обращаться Route::get('lessons/{lesson}', [LessonController::class, 'show'])
  • В методе show(Lesson $lesson) в (LessonController) возвращаем (return) страницу lesson.show.blade.php и передаем ей массив lesson, указав вторым аргументом compact('lesson')
  • Указываем маршрут в ссылках внутри компонентов в тегах
  • Создаем компонент lesson.show.blade.php. Верстка

Lesson 46. Видео плеер

  • Добавляем iframe видео на страницу урока
  • Убираем лишние атрибуты (height, width)
  • В поле title указываем $lesson->title
  • Верстка кнопок плеера. пред/след. урок.
  • Добавление иконок
  • В роуте для кнопки "Все курсы" указываем отношение урока к курсу {{ route('courses.show', $lesson->course) }}
  • Добавление иконок
  • Добавление иконок

Lesson 47. Тестовые данные уроков

  • Отделение компонента плеера
  • Правка контейнера в tailwind.config | Рефакторинг верстки
  • Создаем массив данных приближенных к реальным в LessonSeeder

Lesson 48. Ссылки на видео и исходные коды

  • Добавляем поле repository_uri в миграции courses
  • Обновляем фабрику и сидер курсов указав в них данные поля repository_uri к заполнению
  • Добавляем ссылку указанную в сидере
  • Прокидываем данные repository_uri в компонент уроков
  • Для миграции lessons так же создаем доп. атрибуты. video_url и commit_url
  • Правим фабрики и сидер для lesson

Lesson 49. Отношение "Один из многих"

  • Добавляем ссылку для кнопки Start Watching route('lessons.show', $course->lessons->first())
  • Для модели Course прописываем отношение HasOne с названием firtsLesson
  • Работа с функцией ofMany('column', 'aggregate') | return $this->hasOne(Lesson::class)->ofMany('number', 'min');
  • Рефакторим firstLesson return $this->lessons()->one()->ofMany('number', 'min');
  • Меняем роут с route('lessons.show', $course->lessons->first()) на route('lessons.show', $course->firstLesson)

Lesson 50. Навигация по урокам (аксессоры)

  • Создаем метод аксессор protected function previous(): Attribute Этот метод используется для создания кастомного аксессора с помощью фасада Attribute. Он задает поведение для получения значения атрибута previous.
  • Возвращаем вызов метода make у Attribute внутри которого при помощи get определяем функцию ,которая вернет нам номер урока, который отличается от текущего на 1
  • Обращаемся к course, lessons и firstWhere для поиска number курса -1
  • Определяем роут в котором указываем наш метод аксессор {{ route('lessons.show', $lesson->previous) }}
  • Отображаем кнопку с маршрутом, только в случае если есть урок с номером ниже текущего
  • Пишем такую же реализацию для кнопки next

Lesson 51.

Lesson 52.

Lesson 53.

Lesson 54.

Lesson 55.

Top categories

Loading Svelte Themes