История 00097. Восхождение на Ларавель

История 00097. Восхождение на Ларавель. История 00097. Восхождение на Ларавель.

Восхождение на Ларавель. Нет, эта история вовсе не про альпинизм. Laravel (Ларавел, Ларавель) - один из самых популярных фреймворков с открытым кодом на базе php. Создал Ларавель Тейлор Отвелл (Taylor Otwell) в 2011 году. Название Laravel образовалось путем замены первой буквы от слова Paravel. Cair Paravel (Кэйр Паравел) - замок из мира Нарнии, придуманного Клайвом Льюисом. Один из авторитетных разработчиков сказал мне, что Laravel популярнее и перспективнее, чем YII2. Это сподвигло меня на изучение Ларавеля. Это история о том, как я это делал и что из этого вышло.

© 2025 Константин Оборотов

История 00097. Восхождение на Ларавель.

Стр.00 Содержание

Тема Ссылка
Содержание... Стр.00
Глава 1. Как я сходил в поход на Ларавель Стр.01
Глава 2. Курс молодого бойца Стр.02
Глава 3. Начало пути, выбор направления Стр.03
Глава 4. Процесс установки Ларавель Стр.04
Глава 5. Запуск локального сервера Стр.05
Глава 6. Реализация всех функций аутентификации Laravel Стр.06
Глава 7. Создание чик-чириков (Chirps) Стр.07
Глава 8. Маршрутизация - Routing Стр.08
Глава 9. Blade - Лезвие, используем представление Стр.09
Глава 10. Меню навигации - Navigation menu Стр.10
Глава 11. Настройки для записи чик-чириков в БД Стр.11
Глава 12. Показ чик-чириков Стр.12
Глава 13. Редактирование чик-чириков Стр.13
Глава 14. Удаление чик-чириков Стр.14
Глава 15. Самостоятельная работа Стр.15

Стр.01 Как я сходил в поход на Ларавель



Сподвигли меня на изучение Ларавели утверждения одного авторитета в области разработки, что Ларовель перспективнее и интереснее, чем YII2, да и, вообще, чем любой фреймворк на PHP.

Мне стало очень любопытно. Мною овладел азарт, и даже искушение. А лучший способ избавиться от искушения - следовать ему.

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

Также я решил по итогам путешествия сделать вот эти путевые заметки, которые вы сейчас читаете. Если вы разработчик-новичок (как и я в Ларавеле), то возможно они будут для вас интересными и полезными.

Начну я с описания своего "дипломного проекта", который получился в итоге первичного изучения Ларавели.

  • "Чинумо - Chinumo - ch.inumo.ru" - Учебный проект, выполненный с целью изучения фреймворка Ларавель (Laravel). Главная страница этого сайта.
  • Пошаговая шпаргалка - Как сделать свой первый проект на Laravel. Там почти тоже самое, что и на этой странице. Только там я постарался все написать кратко и сухо. А здесь я дал волю эмоциям. Выбирайте сами, какой стиль вам нравится больше, или читайте и там и здесь. Бесплатно. Регистрации для входа в этот раздел не требуется.
  • Список сообщений - а здесь суть проекта. Можно читать и публиковать короткие сообщения в стиле "Чик-чирик". Это тоже бесплатно, но требуется простейшая регистрация. Эта регистрация сделана в лайтовой версии, в ходе регистрации не требуется подтверждения, можно сразу читать и публиковать свои "телеграммы". Это поможет вам лучше понять, как работает проект.

Вот скриншот из проекта, чтобы было примерно понятно, как выглядет раздел сообщений.

Список сообщений, "чик-чириков".






Стр.02 Курс молодого бойца



Теперь я хочу рассказать, как я знакомился с Ларавель, что мне понравилось и что мне категорически не понравилось.

Первичное обучение я прошел учебно-тренировочном лагере Ларавели вот по этому поддомену:
https://bootcamp.laravel.com/

Это обучение мне очень понравилось. Все четко, все по сути, все пошагово. В результате на выходе получился сайт, про который я рассказал вам ранее.

Какое же у меня было разочарование, когда я зашел на этот ресурс через какое-то время и обнаружил, что никакого учебного лагеря больше нет!

А ссылка выше теперь ведет сюда:
https://laravel.com/docs/12.x

Это мне очень сильно не понравилось! Я был в шоке. Разве можно так делать? Если по каким-то причинам вы закрыли свой учебный проект, то разместите какое-то сообщение по данному адресу. А то как-то молчком, без объяснения причин. Разве можно так делать?

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

Решил я проверить это общество на "вшивость" и задал простой вопрос, "куда делся этот учебный лагерь?"

Мне сильно понравилось, что мой вопрос был опубликован в отдельной ветке и на него поступили внятные ответы по сути вопроса:
Этот вопрос и ответы на laracasts.com в подробном изложении.

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

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

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

Впрочем, не важно. Работает, и ладно.

Кстати, я на всякий случай, еще раз прошелся от начала до конца по учебному курсу в плане создания курсового проекта. Теперь автоматом ставится версия 12 (она сейчас актуальная). Проект работает, но есть незначительные косяки, связанные со стилями. Но это пустяки. В целом все работает. А стили всегда можно подправить.

Давайте, пройдемся по датам. Я пишу эти строки в марте 2025. Сейчас на Ларавеле переход с версии 11 на версию 12. А когда вы читаете этот текст, все может кардинально измениться. Может, актуальная версия будет 13 или 14, может учебный лагерь возобновит работу. Все, что угодно или негодно может случиться.

Так или иначе, мы сделали пока главное. Мы поймали дух Ларавеля. Тут постоянно все меняется, идет какая-та движуха. Тут много народа, который поможет новичку (как я или вы) консультацией бесплатно или за деньги.

Есть ли для вас вообще смысл изучать Ларавель?

Думаю, да. Перспективы Ларавели и ваша будущая зарплата пока выглядят достаточно оптимистично.

И самое главное. Изучение Ларавели очень увлекательный процесс.

Начнем погружение в Ларавель!





Стр.03 Начало пути, выбор направления


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

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

Итак, вперед!

В данном случае на выходе мы получим "Чирпер". Это пародия на известный сайт "Twitter" ("Твиттер") или по новому "Социальная сеть X".

Итак, начинаем движение по простой:
Пошаговой шпаргалке.

Первое, что нужно сделать, выбрать дорогу.

У нас три варианта для выбора:
  • Blade (Лезвие)
  • Livewire
  • JavaScript
Непонятно, что выбрать?

На самом деле, проблема выбора тут решается просто. Тренер тут дает такой совет. Если не уверены, что следует выбрать, выбирайте Blade (Лезвие). Вот и все.

Итак, выбираем Blade и двигаемся дальше.

Если у вас уже установлен PHP и Composer на локальном компьютере, то следует проверить, соответствует ли установленная версия PHP необходимым требованиям.
https://www.php.net/supported-versions.php

Поддерживыемые версии PHP

Проверка установленной версии php производится командой: php -v

C:\Windows\SysWOW64>php -v
PHP 8.2.12 (cli) (built: Oct 24 2023 21:15:15) (ZTS Visual C++ 2019 x64)
Copyright (c) The PHP Group
Zend Engine v4.2.12, Copyright (c) Zend Technologies

А что делать, если PHP не той версии или вообще не установлен?

Один из вариантов решения проблемы - Herd.

Скачать Herd для установки можно здесь:
https://herd.laravel.com/windows

Скачать Herd






Стр.04 Процесс установки Ларавель


Создаем папку для установки.

Пусть, например, это будет папка:
f:\bootcamp_laravel_com\try02\

Находясь в папке выше запускаем команду:
composer create-project laravel/laravel chirper

composer create-project laravel/laravel chirper

После запуска команды должен пойти вот такой процесс: Если процесс прошел успешно, как в данном видео, то можно двигаться дальше.

В данном виде устанавливается Ларавель версия 11. Теперь уже будет устанавливаться, как минимум версия 12, в зависимости от времени, когда вы решили заняться этим фреймворком.

Если возникли какие-либо проблемы, то их надо решить.

Для простоты команда create-project в Composer автоматически создаст новую базу данных SQLite по адресу:
database/database.sqlite для хранения данных вашего приложения.

В нашем конкретном случае файл базы данных здесь:
f:\bootcamp_laravel_com\try02\chirper\database\database.sqlite

Обращаю внимание. Laravel умеет работать и с более сложными базами данных, например, MySQL или Maria. Но для нас, новичков, лучше всего такая простая база данных как SQLite. Вся база данных представляет собой файл database.sqlite, что делает процессы резервного копирования или перемещения максимально простыми.

Если же возникло желание и/или потребность посмотреть, что там внутри этого файла, то можно воспользоваться, например, клиентами DB Browser (SQLite) или SQLiteStudio





Стр.05 Запуск локального сервера


После создания проекта запустите локальный сервер разработки Laravel с помощью команды Serve Laravel Artisan:
php artisan serve

Запускать эту команду следует из папки, в которую мы установили проект:
f:\bootcamp_laravel_com\try02\chirper\

f:\bootcamp_laravel_com\try02>cd f:\bootcamp_laravel_com\try02\chirper\

f:\bootcamp_laravel_com\try02\chirper>php artisan serve

   INFO  Server running on [http://127.0.0.1:8000].

  Press Ctrl+C to stop the server

Теперь если мы в любом браузере введем в адресной строке урл:
http://127.0.0.1:8000/

То отобразится пилотная версия главной страницы проекта (здесь Ларавель версии 11):

Пилотная версия главной страницы проекта


Для сравнения, давайте, бросим взгляд на Ларавель версии 12:

Здесь Ларавель версии 12

Получилось? Давайте, двигаться дальше.





Стр.06 Реализация всех функций аутентификации Laravel


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

Laravel Breeze предлагает несколько вариантов слоя представления, включая шаблоны Blade или Vue и React с Inertia. В этом уроке мы будем использовать Blade.

Open a new terminal in your chirper project directory and install your chosen stack with the given commands:
composer require laravel/breeze --dev
php artisan breeze:install blade
После запуска команд выше должны пойти вот такие процессы: Если процессы прошли успешно, как в данном видео, то можно двигаться дальше.

Breeze установит и настроит ваши внешние зависимости, поэтому нам просто нужно запустить сервер разработки Vite, чтобы автоматически перекомпилировать наш CSS и обновить браузер, когда мы вносим изменения в наши шаблоны Blade:
npm run dev

  VITE v6.1.1  ready in 256 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

  LARAVEL v11.43.2  plugin v1.2.0

  ➜  APP_URL: http://localhost

VITE v6.1.1

VITE в браузере http://localhost:5173/

Если вы обновите новое приложение Laravel в браузере, вы увидите ссылку "Register" ("Зарегистрироваться") в правом верхнем углу. Кликните, чтобы увидеть регистрационную форму, предоставленную Laravel Breeze.
http://127.0.0.1:8000/
Ссылка "Register" ("Зарегистрироваться") в правом верхнем углу.

http://127.0.0.1:8000/register
Регистрационная форма, предоставленная Laravel Breeze.

Теперь попробуем зарегистрировать и залогиниться в этом нашем новом проекте.

Я предлагаю использовать следующие данные, чтобы не запутаться и не забыть:

Name admin
Email admin@test.ru
Password 12345678
Confirm Password 12345678

В любом случае, обратите внимание, длина пароля должна быть не менее 8 символов.

Регистрационная форма, предоставленная Laravel Breeze. Вводим значения. Name: admin Email: admin@test.ru Password: 12345678 Confirm Password: 12345678

После этого должно появиться сообщение:
You're logged in! Вы вошли в систему!

You're logged in! Вы вошли в систему!

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

Теперь, если мы, используя какой-нибудь клиент для SQLite, посмотрим содержание файла:
f:\bootcamp_laravel_com\try02\chirper\database\database.sqlite

То увидим, что в таблице users есть одна запись.

Запись в таблице users

При этом в БД хранится не сам пароль 12345678, а хэш от пароля длинной 60 символов: $2y$12$Omf.y8OuFY2Gp5DG40aMT.iVqobGWe5f1PU2KFZeEdzfN2QtdaQyy





Стр.07 Создание чик-чириков (Chirps)


Теперь вы готовы приступить к созданию нового приложения! Давайте разрешим нашим пользователям публиковать короткие сообщения под названием Чик-чирики (Chirps).

Модели (models), миграции (migrations) и контроллеры (controllers).

Чтобы пользователи могли публиковать Чик-чирики (Chirps), нам нужно будет создать модели, миграции и контроллеры. Давайте рассмотрим каждую из этих концепций немного глубже:
  • Модели предоставляют мощный и удобный интерфейс для взаимодействия с таблицами в вашей базе данных.
  • Миграции позволяют легко создавать и изменять таблицы в базе данных. Они гарантируют, что одна и та же структура базы данных существует везде, где работает ваше приложение.
  • Контроллеры отвечают за обработку запросов, отправленных к вашему приложению, и возврат ответа.

Почти каждая создаваемая вами функция будет включать в себя все эти части, работающие вместе в гармонии, поэтому команда artisan make:model может создать их все для вас одновременно.

Давайте создадим модель, миграцию и контроллер ресурсов для наших Chirps с помощью следующей команды:
php artisan make:model -mrc Chirp

php artisan make:model -mrc Chirp

Эта команда создала для нас три файла:
  • f:\bootcamp_laravel_com\try02\chirper\app\Models\Chirp.php - Основная Модель.
  • f:\bootcamp_laravel_com\try02\chirper\database\migrations\2025_02_23_113829_create_chirps_table.php - Миграция базы данных, в ходе которой будет создана таблица вашей базы данных.
  • f:\bootcamp_laravel_com\try02\chirper\app\Http\Controllers\ChirpController.php - HTTP-контроллер, который будет принимать входящие запросы и возвращать ответы.

Примечание.
Вы можете просмотреть все доступные параметры команды artisan make:model, выполнив команду вот с такими опциями:
php artisan make:model --help

php artisan make:model --help






Стр.08 Маршрутизация - Routing


Нам также нужно будет создать URL-адреса для нашего контроллера. Мы можем сделать это, добавив "маршруты", которые управляются в каталоге маршрутов вашего проекта routes. Поскольку мы используем контроллер ресурсов, мы можем использовать один оператор Route::resource() для определения всех маршрутов, следующих обычной структуре URL.

Для начала мы собираемся включить два маршрута:
  • Маршрут index отобразит нашу форму и список Чик-чириков (Чирпов, Chirps).
  • Маршрут storeбудет использоваться для сохранения новых Чик-чириков (Чирпов, Chirps).

Мы также собираемся разместить эти маршруты за двумя промежуточными программами middleware:
  • Промежуточное программное обеспечение аутентификации auth middleware гарантирует, что только вошедшие в систему пользователи смогут получить доступ к маршруту.
  • Промежуточное программное обеспечение для верификации verified middleware будет использоваться, если вы решите включить проверку электронной почты.

Работаем с файлом: f:\bootcamp_laravel_com\try02\chirper\routes\web.php

Старая (текущая) версия:
<?php

use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;

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

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

Route::middleware('auth')->group(function () {
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});

require __DIR__.'/auth.php';

Новая версия:
<?php
 
use App\Http\Controllers\ChirpController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
 
Route::get('/', function () {
    return view('welcome');
});
 
Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
 
Route::middleware('auth')->group(function () {
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
 
Route::resource('chirps', ChirpController::class)
    ->only(['index', 'store'])
    ->middleware(['auth', 'verified']);
 
require __DIR__.'/auth.php';

Новая код:
use App\Http\Controllers\ChirpController;
...
Route::resource('chirps', ChirpController::class)
    ->only(['index', 'store'])
    ->middleware(['auth', 'verified']);

Это создаст следующие маршруты:
Verb  URI    Action Route Name
GET  /chirps index  chirps.index
POST /chirps store  chirps.store

Примечание.
Вы можете просмотреть все маршруты вашего приложения, выполнив команду:
php artisan route:list

Маршруты приложения: php artisan route:list


Давайте проверим наш маршрут и контроллер, вернув тестовое сообщение из метода index нашего нового класса ChirpController
Работаем с файлом: f:\bootcamp_laravel_com\try02\chirper\app\Http\Controllers\ChirpController.php

Старая (текущая) версия:
<?php

namespace App\Http\Controllers;

use App\Models\Chirp;
use Illuminate\Http\Request;

class ChirpController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     */
    public function show(Chirp $chirp)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(Chirp $chirp)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, Chirp $chirp)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(Chirp $chirp)
    {
        //
    }
}

Новая версия:
<?php

namespace App\Http\Controllers;

use App\Models\Chirp;
use Illuminate\Http\Request;

use Illuminate\Http\Response; // новый!

class ChirpController extends Controller
{
    /**
     * Display a listing of the resource.
     */
//    public function index()
    public function index(): Response // новый!
    {
        //
      return response('Превед, медвед! :: Hello, World!'); // новый!
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     */
    public function show(Chirp $chirp)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(Chirp $chirp)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, Chirp $chirp)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(Chirp $chirp)
    {
        //
    }
}


Если вы вошли в систему ранее, вы должны увидеть свое сообщение при переходе по адресу:
http://localhost:8000/chirps или http://localhost/chirps, если вы используете Sail!

http://localhost:8000/chirps






Стр.09 Blade - Лезвие, используем представление


Еще не впечатлены? Давайте обновим метод index нашего класса ChirpController, чтобы отображать представление (view) Blade:
Работаем с файлом: f:\bootcamp_laravel_com\try02\chirper\app\Http\Controllers\ChirpController.php
Новая версия:
<?php

namespace App\Http\Controllers;

use App\Models\Chirp;
use Illuminate\Http\Request;

use Illuminate\Http\Response;

use Illuminate\View\View; // новый!

class ChirpController extends Controller
{
    /**
     * Display a listing of the resource.
     */
//    public function index()
//    public function index(): Response // старый!
    public function index(): View // новый!
    {
      //
      // return response('Превед, медвед! :: Hello, World!'); // старый!
      return view('chirps.index'); // новый!
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     */
    public function show(Chirp $chirp)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(Chirp $chirp)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, Chirp $chirp)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(Chirp $chirp)
    {
        //
    }
}


Затем мы можем создать шаблон представления Blade с формой для создания новых Чик-чириков (Чирпы, Chirps):

Создаем новый файл: f:\bootcamp_laravel_com\try02\chirper\resources\views\chirps\index.blade.php
Новая версия:
<x-app-layout>
    <div class="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8">
        <form method="POST" action="{{ route('chirps.store') }}">
            @csrf
            <textarea
                name="message"
                placeholder="{{ __('What\'s on your mind?') }}"
                class="block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"
            >{{ old('message') }}</textarea>
            <x-input-error :messages="$errors->get('message')" class="mt-2" />
            <x-primary-button class="mt-4">{{ __('Chirp') }}</x-primary-button>
        </form>
    </div>
</x-app-layout>


Вот и все! Обновите страницу в браузере, чтобы увидеть новую форму, отображаемую в макете по умолчанию, предоставленном Breeze!

Форма для ввода Чик-чирика http://localhost:8000/chirps

Если ваш снимок экрана не совсем похож на приведенный выше, возможно, вам придется остановить и запустить сервер разработки Vite для Tailwind, чтобы обнаружить классы CSS в новом файле, который мы только что создали.

С этого момента любые изменения, которые мы вносим в наши шаблоны Blade, будут автоматически обновляться в браузере всякий раз, когда сервер разработки Vite запускается через npm run dev.





Стр.10 Меню навигации - Navigation menu


Обновите компонент navigation.blade.php, предоставленный Breeze, чтобы добавить пункт меню для мониторов компьютеров, а также для экранов смартфонов.
Работаем с файлом: f:\bootcamp_laravel_com\try02\chirper\resources\views\layouts\navigation.blade.php

Файл большой, тут только изменения:
<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
    <x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
        {{ __('Dashboard') }}
    </x-nav-link>
    <x-nav-link :href="route('chirps.index')" :active="request()->routeIs('chirps.index')">
        {{ __('Chirps') }}
    </x-nav-link>
</div>

<!-- Responsive Navigation Menu -->
<div :class="{'block': open, 'hidden': ! open}" class="hidden sm:hidden">
<div class="pt-2 pb-3 space-y-1">
    <x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
        {{ __('Dashboard') }}
    </x-responsive-nav-link>
    <x-responsive-nav-link :href="route('chirps.index')" :active="request()->routeIs('chirps.index')">
        {{ __('Chirps') }}
    </x-responsive-nav-link>
</div>



Скачать текущую версию можно здесь:
navigation.blade_st010.zip

Пояснение. Мы добавляем ссылку route('chirps.index') для мониторов компьютеров, а также для экранов смартфонов.

Если код исправлен корректно, то тогда мы увидим ссылку в навигационном меню.

Ссылка в навигационном меню на http://localhost:8000/chirps

Ссылка в навигационном меню на http://localhost:8000/chirps для мобильных устройств.
Ссылка в навигационном меню на http://localhost:8000/chirps для мобильных устройств

Переход в раздел: http://localhost:8000/chirps
Переход в раздел: http://localhost:8000/chirps

Создаем первую запись в chirps
Первый тестовый чик-чирик!
First test chirp!

Создаем первую запись в chirps

Но не надо спешить нажимать кнопку "CHIRP"!

Необходимо предварительно провести некоторые подготовительные мероприятия!





Стр.11 Настройки для записи чик-чириков в БД


Создание отношений
Возможно, вы заметили на предыдущем шаге, что мы вызвали метод chirps для объекта $request->user(). Нам нужно создать этот метод в нашей модели пользователя, чтобы определить отношение "имеет много" ("has many").

Работаем с файлом: f:\bootcamp_laravel_com\try02\chirper\app\Models\User.php
Файл большой, тут только изменения:
...
    public function chirps(): HasMany
    {
        return $this->hasMany(Chirp::class);
    }
...

Скачать текущую версию можно здесь:
User_st011.zip

Laravel предлагает множество различных типов отношений модели, о которых вы можете прочитать больше в документации Eloquent Relationships.

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

Laravel защищает вас от случайных действий, блокируя массовое назначение по умолчанию. Однако массовое назначение очень удобно, поскольку избавляет от необходимости назначать каждый атрибут один за другим. Мы можем включить массовое назначение безопасных атрибутов, пометив их как "заполняемые" ("fillable").

Давайте добавим свойство $fillable в нашу модель Chirp, чтобы включить массовое присвоение атрибута сообщения.

Работаем с файлом: f:\bootcamp_laravel_com\try02\chirper\app\Models\User.php
Содержание файла:
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Chirp extends Model
{
    //
    protected $fillable = [
        'message',
    ];

//
} // class Chirp extends Model
//

Скачать текущую версию можно здесь:
Chirp_st011.zip

Обновление миграции
Во время создания приложения Laravel уже применил миграции по умолчанию, включенные в каталог базы данных/миграции. Вы можете проверить текущую структуру базы данных, используя команды php artisan db:show и php artisan db:table
php artisan db:show
php artisan db:table users

Итак, единственное, чего не хватает, — это дополнительных столбцов в нашей базе данных для хранения отношений между Chirp и его пользователем и самим сообщением. Помните миграцию базы данных, которую мы создали ранее? Пришло время открыть этот файл и добавить несколько дополнительных столбцов.

В общем случае:
f:\bootcamp_laravel_com\try02\chirper\database\migrations\<timestamp>_create_chirps_table.php

В нашем примере:
f:\bootcamp_laravel_com\try02\chirper\database\migrations\2025_02_23_113830_create_chirps_table.php
Содержание файла:
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('chirps', function (Blueprint $table) {
            $table->id();
//
// AV 20250301
            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
            $table->string('message');
//

            $table->timestamps();
        });
    }

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


Скачать текущую версию можно здесь:
2025_02_23_113830_create_chirps_table_st011.zip

Запуск миграции:
f:\bootcamp_laravel_com\try02\chirper>php artisan migrate

   INFO  Running migrations.

  2025_02_23_113830_create_chirps_table .................................................................. 9.09ms DONE


f:\bootcamp_laravel_com\try02\chirper>


Если запуск миграции прошел успешно, то теперь можно делать записи в нашу БД.

Попробуйте отправить первую запись (см выше шаг 010).

Если мы посмотрим в нашу БД, то теперь мы сможем увидеть нашу первую запись.
Наша первая запись в БД






Стр.12 Показ чик-чириков


На предыдущем шаге мы добавили возможность создавать Чик-чирики (Chirps), теперь мы готовы их отображать!

Получение щебетаний
Давайте обновим метод index нашего класса ChirpController, чтобы передавать Chirps от каждого пользователя на нашу индексную страницу (index).
Работаем с файлом: f:\bootcamp_laravel_com\try02\chirper\app\Http\Controllers\ChirpController.php
Файл большой, тут только изменения:
<?php
...
    public function index(): View // новый!
    {
      //
      // return response('Превед, медвед! :: Hello, World!'); // старый!
      //return view('chirps.index'); // новый!
      return view('chirps.index', [
        'chirps' => Chirp::with('user')->latest()->get(),
      ]);
    }
...

Скачать текущую версию можно здесь:
ChirpController_st012.zip

Подключение пользователей к Chirps
Мы проинструктировали Laravel вернуть отношения с пользователем, чтобы мы могли отобразить имя автора Chirp. Но отношения с пользователем Chirp еще не определены. Чтобы исправить это, давайте добавим новое отношение "принадлежит к" нашей модели Chirp:
Работаем с файлом: f:\bootcamp_laravel_com\try02\chirper\app\Models\Chirp.php
Содержание файла:
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Chirp extends Model
{
    //
    protected $fillable = [
        'message',
    ];

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }


//
} // class Chirp extends Model
//

Скачать текущую версию можно здесь:
Chirp_st012.zip

Обновление нашего представления
Далее давайте обновим наш компонент index.blade.php, чтобы отображать Chirps под нашей формой:

Скачать текущую версию можно здесь:
index.blade_st012.zip

Теперь наши Чик-чирики (Chirps) отображаются под формой ввода.
Теперь наши Чик-чирики (Chirps) отображаются под формой ввода.






Стр.13 Редактирование чик-чириков


Давайте, добавим возможность, которой нет на других популярных платформах микроблогов, посвященных коротким сообщениям - возможность редактировать сообщения!

Маршрутизация - Routing

Сначала мы обновим наш файл маршрутов, чтобы включить маршруты chirps.edit и chirps.update для нашего контроллера ресурсов. Маршрут chirps.edit отобразит форму для редактирования сообщений, а маршрут chirps.update примет данные из формы и обновит модель:
routes/web.php
f:\bootcamp_laravel_com\try02\chirper\routes\web.php
<?php
 ...
use App\Http\Controllers\ChirpController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
 
Route::get('/', function () {
return view('welcome');
});
 
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
 
Route::middleware('auth')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
 
Route::resource('chirps', ChirpController::class)
- ->only(['index', 'store'])
+ ->only(['index', 'store', 'edit', 'update'])
->middleware(['auth', 'verified']);
 ...
require __DIR__.'/auth.php';
 
Примечание. Здесь кликая на три точки "..." мы можем развернуть-свернуть контент этого файла.

Наша таблица маршрутизации для этого контроллера теперь выглядит так:
Verb URI Action Route Name
GET /chirps index chirps.index
POST /chirps store chirps.store
GET /chirps/{chirp}/edit edit chirps.edit
PUT/PATCH /chirps/{chirp} update chirps.update


Ссылка на страницу редактирования

Далее давайте свяжем наш новый маршрут chirps.edit. Мы будем использовать компонент x-dropdown, который поставляется с Breeze, который мы будем показывать только автору Чирпа (Chirp, Сообщение). Мы также будем показывать признак, был ли отредактирован Чирпа, сравнивая дату created_at Чирпа с его датой updated_at:
resources/views/chirps/index.blade.php
f:\bootcamp_laravel_com\try02\chirper\resources\views\chirps\index.blade.php
<x-app-layout>
<div class="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8">
<form method="POST" action="{{ route('chirps.store') }}">
@csrf
<textarea
name="message"
placeholder="{{ __('What\'s on your mind?') }}"
class="block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"
>{{ old('message') }}</textarea>
<x-input-error :messages="$errors->get('message')" class="mt-2" />
<x-primary-button class="mt-4">{{ __('Chirp') }}</x-primary-button>
</form>
 
<div class="mt-6 bg-white shadow-sm rounded-lg divide-y">
@foreach ($chirps as $chirp)
<div class="p-6 flex space-x-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-600 -scale-x-100" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
</svg>
<div class="flex-1">
<div class="flex justify-between items-center">
<div>
<span class="text-gray-800">{{ $chirp->user->name }}</span>
<small class="ml-2 text-sm text-gray-600">{{ $chirp->created_at->format('j M Y, g:i a') }}</small>
+ @unless ($chirp->created_at->eq($chirp->updated_at))
+ <small class="text-sm text-gray-600"> &middot; {{ __('edited') }}</small>
+ @endunless
</div>
+ @if ($chirp->user->is(auth()->user()))
+ <x-dropdown>
+ <x-slot name="trigger">
+ <button>
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
+ <path d="M6 10a2 2 0 11-4 0 2 2 0 014 0zM12 10a2 2 0 11-4 0 2 2 0 014 0zM16 12a2 2 0 100-4 2 2 0 000 4z" />
+ </svg>
+ </button>
+ </x-slot>
+ <x-slot name="content">
+ <x-dropdown-link :href="route('chirps.edit', $chirp)">
+ {{ __('Edit') }}
+ </x-dropdown-link>
+ </x-slot>
+ </x-dropdown>
+ @endif
</div>
<p class="mt-4 text-lg text-gray-900">{{ $chirp->message }}</p>
</div>
</div>
@endforeach
</div>
</div>
</x-app-layout>

Создание формы редактирования

Давайте создадим новое представление Blade с формой для редактирования Chirp. Это похоже на форму для создания Chirp, за исключением того, что мы будем публиковать на маршруте chirps.update и использовать директиву @method, чтобы указать, что мы делаем запрос "PATCH". Мы также предварительно заполним поле существующим сообщением Chirp:
resources/views/chirps/edit.blade.php
f:\bootcamp_laravel_com\try02\chirper\resources\views\chirps\edit.blade.php
<x-app-layout>
<div class="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8">
<form method="POST" action="{{ route('chirps.update', $chirp) }}">
@csrf
@method('patch')
<textarea
name="message"
class="block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"
>{{ old('message', $chirp->message) }}</textarea>
<x-input-error :messages="$errors->get('message')" class="mt-2" />
<div class="mt-4 space-x-2">
<x-primary-button>{{ __('Save') }}</x-primary-button>
<a href="{{ route('chirps.index') }}">{{ __('Cancel') }}</a>
</div>
</form>
</div>
</x-app-layout>

Обновление нашего контроллера

Давайте обновим метод редактирования edit в нашем ChirpController, чтобы отобразить нашу форму. Laravel автоматически загрузит модель Chirp из базы данных с помощью привязки модели маршрута, чтобы мы могли передать ее прямо в представление.

Затем мы обновим метод обновления update, чтобы проверить запрос и обновить базу данных.

Несмотря на то, что мы отображаем кнопку редактирования только для автора Chirp, нам все равно нужно убедиться, что пользователь, получающий доступ к этим маршрутам, авторизован:
app/Http/Controllers/ChirpController.php
f:\bootcamp_laravel_com\try02\chirper\app\Http\Controllers\ChirpController.php
<?php
 ...
namespace App\Http\Controllers;
 
use App\Models\Chirp;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
+use Illuminate\Support\Facades\Gate;
use Illuminate\View\View;
 
class ChirpController extends Controller
{
 ...
/**
* Display a listing of the resource.
*/
public function index(): View
{
return view('chirps.index', [
'chirps' => Chirp::with('user')->latest()->get(),
]);
}
 
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
 
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): RedirectResponse
{
$validated = $request->validate([
'message' => 'required|string|max:255',
]);
 
$request->user()->chirps()->create($validated);
 
return redirect(route('chirps.index'));
}
 
/**
* Display the specified resource.
*/
public function show(Chirp $chirp)
{
//
}
 
/**
* Show the form for editing the specified resource.
*/
- public function edit(Chirp $chirp)
+ public function edit(Chirp $chirp): View
{
- //
+ Gate::authorize('update', $chirp);
+ 
+ return view('chirps.edit', [
+ 'chirp' => $chirp,
+ ]);
}
/**
* Update the specified resource in storage.
*/
- public function update(Request $request, Chirp $chirp)
+ public function update(Request $request, Chirp $chirp): RedirectResponse
{
- //
+ Gate::authorize('update', $chirp);
+ 
+ $validated = $request->validate([
+ 'message' => 'required|string|max:255',
+ ]);
+ 
+ $chirp->update($validated);
+ 
+ return redirect(route('chirps.index'));
}
 ...
/**
* Remove the specified resource from storage.
*/
public function destroy(Chirp $chirp)
{
//
}
 
}

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

Авторизация - Authorization

По умолчанию метод authorize не позволит всем обновлять Chirp. Мы можем указать, кому разрешено обновлять его, создав Model Policy с помощью следующей команды:
php artisan make:policy ChirpPolicy --model=Chirp

Это создаст класс политики в файле app/Policies/ChirpPolicy.php, который мы можем обновить, указав, что обновлять Chirp имеет право только автор:
app/Policies/ChirpPolicy.php
f:\bootcamp_laravel_com\try02\chirper\app\Policies\ChirpPolicy.php
<?php
 ...
namespace App\Policies;
 
use App\Models\Chirp;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
 
class ChirpPolicy
{
 ...
use HandlesAuthorization;
 
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
//
}
 
/**
* Determine whether the user can view the model.
*/
public function view(User $user, Chirp $chirp): bool
{
//
}
 
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
//
}
 
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Chirp $chirp): bool
{
- //
+ return $chirp->user()->is($user);
}
 ...
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Chirp $chirp): bool
{
//
}
 
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, Chirp $chirp): bool
{
//
}
 
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, Chirp $chirp): bool
{
//
}
 
}

Тестирование

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





Стр.14 Удаление чик-чириков


Иногда никакое редактирование не может исправить сообщение, поэтому давайте дадим нашим пользователям возможность удалять свои сообщения Chirp.

Надеюсь, вы уже начинаете понимать, что к чему. Мы думаем, вы будете впечатлены тем, как быстро мы сможем добавить эту возможность.

Маршрутизация - Routing

Начнем снова с обновления наших маршрутов, чтобы включить маршрут chirps.destroy:
routes/web.php
f:\bootcamp_laravel_com\try02\chirper\routes\web.php
<?php
 ...
use App\Http\Controllers\ChirpController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
 
Route::get('/', function () {
return view('welcome');
});
 
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth'])->name('dashboard');
 
Route::middleware('auth')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
 
Route::resource('chirps', ChirpController::class)
- ->only(['index', 'store', 'edit', 'update'])
+ ->only(['index', 'store', 'edit', 'update', 'destroy'])
->middleware(['auth', 'verified']);
 ...
require __DIR__.'/auth.php';
 

Наша таблица маршрутизации для этого контроллера теперь выглядит так:
Verb URI Action Route Name
GET /chirps index chirps.index
POST /chirps store chirps.store
GET /chirps/{chirp}/edit edit chirps.edit
PUT/PATCH /chirps/{chirp} update chirps.update
DELETE /chirps/{chirp} destroy chirps.destroy

Обновление нашего контроллера

Теперь мы можем обновить метод destroy в нашем классе ChirpController, чтобы выполнить удаление и вернуться к списку обновлений:
app/Http/Controllers/ChirpController.php
f:\bootcamp_laravel_com\try02\chirper\app\Http\Controllers\ChirpController.php
<?php
 ...
namespace App\Http\Controllers;
 
use App\Models\Chirp;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Gate;
use Illuminate\View\View;
 
class ChirpController extends Controller
{
 ...
/**
* Display a listing of the resource.
*/
public function index(): View
{
return view('chirps.index', [
'chirps' => Chirp::with('user')->latest()->get(),
]);
}
 
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
 
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): RedirectResponse
{
$validated = $request->validate([
'message' => 'required|string|max:255',
]);
 
$request->user()->chirps()->create($validated);
 
return redirect(route('chirps.index'));
}
 
/**
* Display the specified resource.
*/
public function show(Chirp $chirp)
{
//
}
 
/**
* Show the form for editing the specified resource.
*/
public function edit(Chirp $chirp): View
{
Gate::authorize('update', $chirp);
 
return view('chirps.edit', [
'chirp' => $chirp,
]);
}
 
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Chirp $chirp): RedirectResponse
{
Gate::authorize('update', $chirp);
 
$validated = $request->validate([
'message' => 'required|string|max:255',
]);
 
$chirp->update($validated);
 
return redirect(route('chirps.index'));
}
 
/**
* Remove the specified resource from storage.
*/
- public function destroy(Chirp $chirp)
+ public function destroy(Chirp $chirp): RedirectResponse
{
- //
+ Gate::authorize('delete', $chirp);
+ 
+ $chirp->delete();
+ 
+ return redirect(route('chirps.index'));
}
}

Авторизация - Authorization

Как и в случае с редактированием, мы хотим, чтобы только авторы наших сообщений могли удалять свои сообщения, поэтому давайте обновим метод удаления delete в нашем классе ChirpPolicy:
app/Policies/ChirpPolicy.php
f:\bootcamp_laravel_com\try02\chirper\app\Policies\ChirpPolicy.php
<?php
 ...
namespace App\Policies;
 
use App\Models\Chirp;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
 
class ChirpPolicy
{
 ...
use HandlesAuthorization;
 
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
//
}
 
/**
* Determine whether the user can view the model.
*/
public function view(User $user, Chirp $chirp): bool
{
//
}
 
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
//
}
 
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Chirp $chirp): bool
{
return $chirp->user()->is($user);
}
 
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Chirp $chirp): bool
{
- //
+ return $this->update($user, $chirp);
}
 ...
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, Chirp $chirp): bool
{
//
}
 
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, Chirp $chirp): bool
{
//
}
 
}

Вместо того, чтобы повторять логику из метода обновления update, мы можем определить ту же логику, вызвав метод обновления update из нашего метода удаления delete. Любой, кто имеет право обновлять Chirp, теперь будет иметь право и удалять его.

Обновление представления

Наконец, мы можем добавить кнопку удаления в раскрывающееся меню, которое мы создали ранее в нашем представлении chirps.index:
resources/views/chirps/index.blade.php
f:\bootcamp_laravel_com\try02\chirper\resources\views\chirps\index.blade.php
<x-app-layout>
<div class="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8">
<form method="POST" action="{{ route('chirps.store') }}">
@csrf
<textarea
name="message"
placeholder="{{ __('What\'s on your mind?') }}"
class="block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"
>{{ old('message') }}</textarea>
<x-input-error :messages="$errors->get('message')" class="mt-2" />
<x-primary-button class="mt-4">{{ __('Chirp') }}</x-primary-button>
</form>
 
<div class="mt-6 bg-white shadow-sm rounded-lg divide-y">
@foreach ($chirps as $chirp)
<div class="p-6 flex space-x-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-600 -scale-x-100" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
</svg>
<div class="flex-1">
<div class="flex justify-between items-center">
<div>
<span class="text-gray-800">{{ $chirp->user->name }}</span>
<small class="ml-2 text-sm text-gray-600">{{ $chirp->created_at->format('j M Y, g:i a') }}</small>
@unless ($chirp->created_at->eq($chirp->updated_at))
<small class="text-sm text-gray-600"> &middot; {{ __('edited') }}</small>
@endunless
</div>
@if ($chirp->user->is(auth()->user()))
<x-dropdown>
<x-slot name="trigger">
<button>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
<path d="M6 10a2 2 0 11-4 0 2 2 0 014 0zM12 10a2 2 0 11-4 0 2 2 0 014 0zM16 12a2 2 0 100-4 2 2 0 000 4z" />
</svg>
</button>
</x-slot>
<x-slot name="content">
<x-dropdown-link :href="route('chirps.edit', $chirp)">
{{ __('Edit') }}
</x-dropdown-link>
+ <form method="POST" action="{{ route('chirps.destroy', $chirp) }}">
+ @csrf
+ @method('delete')
+ <x-dropdown-link :href="route('chirps.destroy', $chirp)" onclick="event.preventDefault(); this.closest('form').submit();">
+ {{ __('Delete') }}
+ </x-dropdown-link>
+ </form>
</x-slot>
</x-dropdown>
@endif
</div>
<p class="mt-4 text-lg text-gray-900">{{ $chirp->message }}</p>
</div>
</div>
@endforeach
</div>
</div>
</x-app-layout>

Тестирование

Если вы написали что-то, что вас не устраивает, попробуйте это удалить!





Стр.15 Самостоятельная работа


Все, что мы делали по проекту ранее было придумано уже до нас.

Это работа относительно простая, но не такая интересная.

Очень полезно придумать задание самому себе и попробовать его выполнить.

Чередование фоновой окраски сообщений

Смысл этого задания в том, чтобы отделить цветом одно сообщение от другого.

Чередование фоновой окраски сообщений
Достигается такое чередование следующим способом.

resources/views/chirps/index.blade.php
@foreach ($chirps as $chirp)
  @if ($loop->even)
    <div class="p-6 flex space-x-2" style="background-color:#FFFFF0;">
  @else
    <div class="p-6 flex space-x-2" style="background-color:#F0FFFF;">
  @endif
Задача решается на уровне ядра Лезвия (Blade). Имеется встроенный объект $loop (официально называется переменная), а у него есть свойство even, которое мы и используем в данном случае.

Затронем немного теории, раз уж зашла речь.

Переменная цикла $loop

Свойство - Property Description - Описание
$loop->index The index of the current loop iteration (starts at 0).
Индекс текущей итерации цикла (начинается с 0).
$loop->iteration The current loop iteration (starts at 1).
Текущая итерация цикла (начинается с 1).
$loop->remaining The iterations remaining in the loop.
Оставшиеся итерации в цикле.
$loop->count The total number of items in the array being iterated.
Общее количество элементов в итерируемом массиве.
$loop->first Whether this is the first iteration through the loop.
Является ли этот проход первой итерацией цикла.
$loop->last Whether this is the last iteration through the loop.
Является ли этот проход последней итерацией цикла.
$loop->even Whether this is an even iteration through the loop.
Является ли этот проход четной итерацией цикла.
$loop->odd Whether this is an odd iteration through the loop.
Является ли этот проход нечетной итерацией цикла.
$loop->depth The nesting level of the current loop.
Уровень вложенности текущего контура.
$loop->parent When in a nested loop, the parent's loop variable.
Во вложенном цикле - переменная родительского цикла.


Отображение сообщения в форматах TEXT и HTML

Здесь мы рассмотрим две идеи.

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

Теория вопроса в рамках опять же Лезвия (Blade) следующая.
  • {{ $chirp->message }} - сообщение отображается в текстовом формате
  • {!! $chirp->message !!} - сообщение отображается в HTML формате

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

resources/views/chirps/index.blade.php
@if ($chirp->user->id==1)
  <p class="mt-4 text-lg text-gray-900">{!! $chirp->message !!}</p>
@else
  <p class="mt-4 text-lg text-gray-900">{{ $chirp->message }}</p>
@endif
Разумеется в выражении @if можно придумать и более сложную логику отбора.

Вот и все, что я хотел бы рассказать вам о Ларавели.

Желаю вам успеха, высокого заработка и качественного кода!





= Конец =




PS Можно ли использовать отрывки текстов с этой страницы?



Отрывки текстов с этой страницы можно использовать с указанием ссылки на первоисточник:

Запомните урл этой страницы: