Я сразу, как только вышла новость о релизе, решил, что нужно посмотреть, пощупать и разобраться, чего же изменилось. Да-да, на днях, а именно 8 февраля 2022, вышел официальный релиз Laravel 9, который включает довольно много новых улучшений. Для тех же из нас, кто не боится таких слов, как alpha и beta, девятая версия фреймворка давно не новость и уже в работе.
Теперь эта версия будет поддерживаться дольше (LTS), и разработчики фреймворка пришли к решению не выпускать новые версии каждые 6 месяцев, а делать это раз в год - в феврале. Судя из расписания, эта версия останется актуальной год, а обновления безопасности будут выпускаться вплоть до 2025 года.
Версия |
Язык |
Дата релиза |
Выпуск багфиксов |
Выпуск патчей безопасности |
6 (LTS) |
7.2 - 8.0 |
3 сен 2019 |
25 янв 2022 |
6 сен 2022 |
7 |
7.2 - 8.0 |
3 марта 2020 |
6 окт 2020 |
3 марта 2021 |
8 |
7.3 - 8.1 |
8 сен 2020 |
26 июля 2022 |
24 янв 2023 |
9 (LTS) |
8.0 - 8.1 |
8 фев 2022 |
8 фев 2024 |
8 фев 2025 |
10 |
8.0 - 8.1 |
7 фев 2023 |
7 авг 2024 |
7 фев 2025 |
Версия языка
Новый Laravel работает только с php 8.0 и выше. Почему это так? Как мы увидим далее, разработчики воплотили в девятом фреймворке немало фишек последней версии языка, а значит, что использование php7 означало бы потерю именно этих нововведений.
composer create-project laravel/laravel example-app
Команда создала директорию, наполнила ее файлами нового проекта и установила зависимости, среди которых основным является laravel/framework
версии v9.0.2. Как видим, релизную версию уже патчат.
Новые помощники
Представлены две новые функции-помощника, которые, выполняя уже встроенный ранее функционал, делают это гораздо удобнее.
Добавленная функция str создает объект класса Illuminate\Support\Stringable
для переданной строки. Это позволяет применять к тексту все методы манипуляции строкой, доступные данному классу. Такая возможность существовала и в 8 версии фреймворка и реализовывалась через Str::of
, но теперь получается немного лаконичнее.
<?php
Route::get('/', function () {
return str('Hello, ') // Создаем класс из строки
->append('Max') // Метод добавления в конец строки
->upper(); // Метод делает все буквы строки заглавными
});
Функция to_route создает редирект на существующий роут по его имени. Мы можем воспользоваться ей как в наших роутах, так и вернуть ее результат из метода контроллера.
<?php
// Главная страница
Route::get('/', function () {
return view('welcome');
})->name('home'); // Имя роута для главной страницы
// Старый способ редиректа по имени роута
Route::get('/test', function () {
return redirect()->route('home');
});
// Новый способ редиректа с помощью функции to_route
Route::get('/new_test', function () {
return to_route('home');
});
А еще мы можем передавать в новую функцию параметры роута, статус HTTP и дополнительные заголовки:
<?php
Route::get('/', function () {
return to_route('users.show', // Наименование роута
['user' => 1], // Параметры роута
302, // Код статуса редиректа
['X-Framework' => 'Laravel']); // Дополнительные заголовки
});
Также благодаря базированию на языке PHP 8, в классе \Illuminate\Support\Str
добавилась поддержка таких функций для работы со строкой как str_contains()
, str_starts_with()
и str_ends_with()
.
Страница исключений
Встроенная в фреймворк страница сообщения об исключении была и раньше крайне удобной, информативной и симпатичной. Теперь в ней изменилось оформление. Добавлены возможность переключения темы на светлую/темную, настройка функционала "открыть в редакторе" и др.
Ниже видно, как можно менять тему, а также как отображаются данные о запросе, вызвавшем исключение доступное системе.
Анонимные миграции
Возможность создавать миграции в виде анонимных классов появилось чуть ранее в Laravel 8.37. А затем в 9 - это стало своего рода стандартом, и при вызове консольной команды php artisan make:migration
создается класс без названия, расширяющий Migration. Довольно таки небольшое изменение, но мне показалось достаточно интересным, чтобы отметить и его.
Миграция, добавляющаяся в database/migrations при создании проекта:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('password_resets');
}
};
Полнотекстовая индексация
Если мы пользуемся такими реляционными системами БД как MySQL или PostgreSQL, то можем прям в миграции добавить для текстовых полей полнотекстовую индексацию. При чем это делается всего лишь вызовом одного метода:
<?php
$table->text('bio')->fulltext();
По таким полям мы можем искать специальными методами полнотекстового поиска whereFullText и orWhereFullText, добавляя их как условие where. Эти методы преобразуются в SQL запросы поиска по индексу текста.
<?php
$users = DB::table('users')
->whereFullText('bio', 'php developer')
->get();
Общий контроллер
Используя методы роутера controller и group, появилась возможность объединять несколько роутов в одну группу с общим для них контроллером. Это довольно удобно и позволяет экономить на количестве кода, не указывая одно и тоже в нескольких строках. Если раньше нам пришлось бы писать как-то так:
<?php
use \App\Http\Controllers\UsersController;
Route::get('/users', [UsersController::class, 'index']);
Route::get('/users/{id}', [UsersController::class, 'showOne']);
То теперь можно это сгруппировать:
<?php
use \App\Http\Controllers\UsersController;
Route::controller(UsersController::class)->group(function () {
Route::get('/users', 'index');
Route::get('/users/{id}', 'showOne');
});
Scoped Bindings в роутерах
Благодаря усилиям разработчика из Амстердама Клаудио Деккера (Claudio Dekker) была реализована и внедрена удобная возможность связывать параметры строки запроса между собой. Лично я не сразу смог понять, что за связанность такая, но на примере кода все становится "ясно-понятно".
Предположим, что мы создали таблицы с пользователями и статьями, причем каждая статья относится к определенному пользователю, как многое-к-одному. То есть у каждого пользователя есть статьи, которые он написал. И вот мы сделали url для вывода списка статей: /users/{user}/posts
. Отсюда логично построить url для вывода отдельной статьи конкретного пользователя по следующей ссылке: /users/{user}/posts/{post}
.
Мы используем два параметра независимых друг от друга для идентификаторов пользователя и статьи. Это позволит сопоставлять любого пользователя с любым постом. Конечно, этого нам не нужно, но и делать проверку вручную - идея так себе. Новый метод scopeBindings сделает это за нас.
<?php
use App\Models\Post;
use App\Models\User;
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
return $post;
})->scopeBindings();
Теперь, если мы попытаемся открыть статью несоответствующую пользователю (связь по внешнему ключу в таблице с другим пользователем), то получим страницу 404 ошибки:
Также есть возможность объединить в одну группу роуты, для которых важно соблюдение связывания параметров:
<?php
Route::scopeBindings()->group(function () {
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
return $post;
});
Route::get('/users/{user}/comments/{comment}', function (User $user, Comment $comment
return $comment;
});
});
Laravel Scout
Полнотекстовый поиск по отдельным параметрам Eloquent моделей, основанный на драйвере базы данных - вот, что представляет из себя этот Scout. Он отлично подходит для поиска по тексту в небольших и даже среднего размера базах. Если наша база вполне умещается на одном сервере, тогда мы можем не использовать такие драйверы как Algolia или MeiliSerach, а воспользоваться простым и удобным Scout с драйвером MySQL/PostgreSQL.
Для начала нужно установить Laravel Scout и выбрать его провайдер-класс для полнотекстового поиска:
composer require laravel/scout
php artisan vendor:publish
Which provider or tag's files would you like to publish?:
[0 ] Publish files from all providers and tags listed below
[1 ] Provider: Fruitcake\Cors\CorsServiceProvider
[2 ] Provider: Illuminate\Foundation\Providers\FoundationServiceProvider
[3 ] Provider: Illuminate\Mail\MailServiceProvider
[4 ] Provider: Illuminate\Notifications\NotificationServiceProvider
[5 ] Provider: Illuminate\Pagination\PaginationServiceProvider
[6 ] Provider: Laravel\Sail\SailServiceProvider
[7 ] Provider: Laravel\Sanctum\SanctumServiceProvider
[8 ] Provider: Laravel\Scout\ScoutServiceProvider
[9 ] Provider: Laravel\Tinker\TinkerServiceProvider
[10] Provider: Spatie\LaravelIgnition\IgnitionServiceProvider
[11] Tag: cors
[12] Tag: laravel-errors
[13] Tag: laravel-mail
[14] Tag: laravel-notifications
[15] Tag: laravel-pagination
[16] Tag: sail
[17] Tag: sail-bin
[18] Tag: sail-docker
[19] Tag: sanctum-config
[20] Tag: sanctum-migrations
> 8
Copied File [/vendor/laravel/scout/config/scout.php] To [/config/scout.php]
Был создан конфигурационный файл для Scout. И в нем мы можем установить нужный нам драйвер database.
Теперь можно настраивать поиск по в классах - моделях, чтобы определить какие модели имеют поддержку полнотекстового поиска и по каким полям. И обязательно добавим трейт Searchable к модели, что позволит использовать все необходимые нам методы поиска. Как видно из скриншота ниже, кроме имени я добавил поиск и к нестандартному полю bio (биография).
Поищем всех пользователей, у которых в биографии есть упоминание обучения в ВУЗе.
<?php
Route::get('/', function () {
return \App\Models\User::search('институт')->get();
})->name('home');
Покрытие кода тестами
В Laravel 9 мы теперь можем генерировать отчет о покрытии кода тестами с помощью консольной команды php artisan test. Давайте посмотрим, что отобразится, если запустить команду как и раньше.
Если посмотрим подсказку по команде запуска тестов, то увидим 2 новых ключа --coverage
и --min[=MIN]
.
php artisan help test
Usage:
test [options]
Options:
--without-tty Disable output to TTY
--coverage Indicates whether code coverage information should be collected
--min[=MIN] Indicates the minimum threshold enforcement for code coverage
Coverage - указывает, следует ли собирать информацию о покрытии кода.
Min - минимальное пороговое значение для покрытия кода. Другими словами, это возможность указать, какой минимальный уровень покрытия кода тестами для нас удовлетворителен.
Я запускаю команду в консоли, но вижу ошибку.
Не установлен драйвер, но, думаю, это легко починить, установив Xdebug для php. Как несложно было догадаться по скриншотам, у меня MacOS, поэтому воспользуюсь встроенными утилитами для установки и перезапуска сервисов. Предположу, что на другие ОС установка не намного сложнее.
pecl install xdebug
brew services restart php
brew services restart nginx
Казалось бы, запускаем команду и радуемся результату, но нет, опять ошибка.
Xdebug ругается о том, что не установлен режим покрытия кода тестами. Это мы легко исправляем и видим красивое описание покрытия кода.
Пару слов в конце
С публикацией нового релиза, мы увидели немало полезных новшеств, поддержку и реализацию возможностей последней версии языка. Мы уже увидели два патча, и возможно будет еще несколько, прежде чем фреймворк станет по-настоящему достоин продакшна. Но мои руки уже чешутся, чтобы обновиться на проектах до последней 9 версии Laravel.
Комментарии (28)
MaryRabinovich
17.02.2022 10:34+1Добрый день,
спасибо за статью, познавательно.
Одно уточнение: я сейчас на восьмёрке пишу, и ->controller(UsersController::class) тут уже есть. Правда, это восьмёрка какая-то из последних, 8.75 (при загрузке версию устанавливала как "8.*")
Ну и пара вопросов:
индексацию полнотекстовую делает тоже скаут? Или это внутри фреймворка из коробки?
чтобы поиграться, сохраняя возможность работать на прежних версиях, какую версию php вы сейчас используете? Похоже, вы ставите всё на компьютер прямо. А с какими при этом самыми старыми версиями фреймворка вы всё ещё можете работать с той же машины?
MaryRabinovich
17.02.2022 10:37(в продолжение вопроса 2)
Я запланировала как-то с девяткой перейти в докер. При этом в докере я разбираюсь пока чисто паразитически (девопс настроил, а я могу запустить/остановить), вот книжку взялась читать про него. Вопрос, а тем ли я занята, не бессмысленная ли это трата времени. Насколько это разумный выбор, вместо банальной переустановки локального xampp.
vLachugin
17.02.2022 10:59+1MaryRabinovich
17.02.2022 14:57Прошла по ссылке... гм. Похоже, девятка рекомендуемо ставится именно через sail. То есть, на инсталляционной странице https://laravel.com/docs/9.x/installation composer идёт уже после sail.
... и для восьмёрки уже та же последовательность в документации. Сначала про сейл, потом уже про композер. Я отстала от жизни.
Rukis
18.02.2022 15:14+1Разберитесь с докером, это не сложно и удобно. Вам могут потребоваться разные версии в разных проектов не только PHP. В добавок получите лёгкость переноса рабочей среды между своими машинами и возможность поделиться средой с коллегой.
zm_llill Автор
17.02.2022 11:20+2У меня стоит сразу и 7 и 8 версия языка на компе, я переключаюсь между ними настройкой нужного. Я занимаюсь поддержкой Ларавел 5, Битрикс (посыпаю пеплом за это свою голову) и вот пробую Ларавел 9. Пятая версия и Битрикс идут у меня только на php7.2, поэтому и приходится держать обе. Изучение докера - отличная идея, хотя я предпочел не книги, а гугление и практику. Еще смотрю в сторону podman, потому что докер сжирает слишком много ресурсов.
MaryRabinovich
17.02.2022 11:54А что именно вы делаете, когда "переключаетесь настройкой нужного"? Вписываете в апач каждый раз, какую версию php брать (единственное, что приходит в голову)?
Кстати да, если есть эта опция, "переключаться настройкой нужного", то для чего вообще люди морочатся с докерами и др. (может быть, podman менее замороченный - впервые про него слышу. С докером точно предварительных вложений времени на изучение больше, чем новый блог на ларе слепить). Вроде бы, основной аргумент же в том, что они позволяют избавить локалку от непосредственной установки вот этого вот всего. Но если можно держать сразу несколько версий (вот этого вот всего) и переключаться, то какова проблема, которую решают докер и ко.
zm_llill Автор
17.02.2022 12:05+2Переключаться довольно затратно по времени и необходимой информации в голове, поэтому это решение ну прям сильно такое себе. Переключаюсь командой смены пути к исполняемому файлу для bash команды php.
Докер придуман для поддержания единого окружения на разных машинах, а возможность делать разное окружение на одной машине уже скорее бонус. Ну и как я уже сказал, это в теории проще чем постоянно переключаться.
zm_llill Автор
17.02.2022 11:20Сейчас проверил, Ларавел до версии 7.29.0 не поддерживал версию языка выше 7.2
zm_llill Автор
17.02.2022 11:13Индексация не связана со Скаут, я поставил рядом их потому что тема близкая, теперь вижу, что зря, это немного путает.
IgorAlentyev
17.02.2022 13:08+1И кстати полнотекстовый индекс и whereFullText методы появились в 8.79.
Если бы вы могли поправить текст, было бы здорово, ибо действительно может запутать.
oxidmod
17.02.2022 11:55+2dev зависимости в prod коде так и не убрали
https://github.com/laravel/framework/blob/9.x/src/Illuminate/Mail/Mailable.php#L20wendel
17.02.2022 13:29+1А на**** надо если можно из phpunit взять ху** х**к и в продакшн! а то что есть альтернативы, я уж не говорю о том что бы выпустить свой пакет - не, не слышали. Особенно обожаю когда в очередном мажорном релизе сделали "более красивое отображние ошибок", без этого версия просто не может быть мажорной!!!
FatalStrike
17.02.2022 13:33О каких альтернативах или своих пакетах идёт речь? Касательно указанного выше кода
oxidmod
17.02.2022 14:15Вот тут есть детали как этот код попал в ларку
https://habr.com/ru/post/534378/
zm_llill Автор
17.02.2022 13:57Я не разрабатываю сам фреймворк, поэтому может ошибусь, но разве Illuminate\Mail\Mailable не собственная разработка команды Ларавел?
Layan
18.02.2022 14:01+1Illuminate\Mail\Mailable то их разработка. Но претензии к использованию PHPUnit (конкретно PHPUnit\Framework\Assert) в продакшн коде. Зависимость от библиотеки тестирования в прод коде — не очень хорошо.
vfreelancer
17.02.2022 11:55по поводу примера с scopeBindings.
У вас там в ссылке 2 ошибки: user/1/pots/5 - а должно быть users/1/posts/5 точно 404 без всяких scopeBindings
kraso4niy
17.02.2022 22:31+5А еще мы можем передавать в новую функцию параметры роута, статус HTTP и дополнительные заголовки:
невероятно, в 2022 году в фреймворке для работы по HTTP проктолу теперь можно передавать заголовки ответа!!!! Браво! Рекомендую забыть этот фреймворк и обратить внимание на symfony, в котором всё что вышло ларе в 2022 году уже было в sf 10 лет назад.
Mellorn
18.02.2022 08:43+1Ну, справедливости ради, с отправкой заголовков и до 2022 было всё отлично.
Просто в статье на этом действительно сделан акцент, из-за чего может сложиться неправильное впечатление.
А по своей сути, to_route — обычный сахарок, более короткая запись.if (! function_exists('to_route')) {
/**
* Create a new redirect response to a named route.
*
* @param string $route
* @param mixed $parameters
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
function to_route($route, $parameters = [], $status = 302, $headers = [])
{
return redirect()->route($route, $parameters, $status, $headers);
}
}
Но принципиально ничего нового этот хелпер не привносит.wendel
18.02.2022 13:56+2Как и новая страница обработки ошибок, как и половина новых фич в каждой новой версии Laravel (:
SerafimArts
Как раз судя из расписания по вашей же ссылке — 9ка не LTS ;)
MichaelBro
Непомню где , но обэтом писали что Тейлор втихаря отменил LTS в Laravel 9
DAGpro
В твиттере Тейлор писал, что отказались от LTS из-за issue в симфони, чтоб в версии симфони 6.1 поднять требования до php 8.1