![иллюстрация © GOLTS иллюстрация © GOLTS](https://habrastorage.org/getpro/habr/upload_files/cec/42f/531/cec42f531092e2ce377be81d4e36b84d.jpg)
Я сразу, как только вышла новость о релизе, решил, что нужно посмотреть, пощупать и разобраться, чего же изменилось. Да-да, на днях, а именно 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. Как видим, релизную версию уже патчат.
![](https://habrastorage.org/getpro/habr/upload_files/3a4/c52/d54/3a4c52d54a0e427585d842af2e57abe4.png)
Новые помощники
Представлены две новые функции-помощника, которые, выполняя уже встроенный ранее функционал, делают это гораздо удобнее.
Добавленная функция 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()
.
Страница исключений
Встроенная в фреймворк страница сообщения об исключении была и раньше крайне удобной, информативной и симпатичной. Теперь в ней изменилось оформление. Добавлены возможность переключения темы на светлую/темную, настройка функционала "открыть в редакторе" и др.
![Темное оформление мне понравилось даже больше, чем Dracula в IDE Темное оформление мне понравилось даже больше, чем Dracula в IDE](https://habrastorage.org/getpro/habr/upload_files/f74/40f/dcb/f7440fdcb84a7a6f27c93deeccdfddc8.png)
Ниже видно, как можно менять тему, а также как отображаются данные о запросе, вызвавшем исключение доступное системе.
![Тема меняется очень красиво Тема меняется очень красиво](https://habrastorage.org/getpro/habr/upload_files/be3/2ce/4fe/be32ce4fee4d176d93bbbcf417171cdc.png)
Анонимные миграции
Возможность создавать миграции в виде анонимных классов появилось чуть ранее в 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 ошибки:
![Страница ошибка с оформлением по умолчанию Страница ошибка с оформлением по умолчанию](https://habrastorage.org/getpro/habr/upload_files/816/617/2f6/8166172f69fb08e47a42d16b06ca94a5.png)
Также есть возможность объединить в одну группу роуты, для которых важно соблюдение связывания параметров:
<?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.
![Настройки посковика Scout Настройки посковика Scout](https://habrastorage.org/getpro/habr/upload_files/b8f/12a/2f2/b8f12a2f2cae5859646db9aaad5c38f8.png)
Теперь можно настраивать поиск по в классах - моделях, чтобы определить какие модели имеют поддержку полнотекстового поиска и по каким полям. И обязательно добавим трейт Searchable к модели, что позволит использовать все необходимые нам методы поиска. Как видно из скриншота ниже, кроме имени я добавил поиск и к нестандартному полю bio (биография).
![Метод toSearchableArray возвращает массив полей для поиска Метод toSearchableArray возвращает массив полей для поиска](https://habrastorage.org/getpro/habr/upload_files/5aa/ed9/6db/5aaed96db6f22cbddec1e93efd58033a.png)
Поищем всех пользователей, у которых в биографии есть упоминание обучения в ВУЗе.
<?php
Route::get('/', function () {
return \App\Models\User::search('институт')->get();
})->name('home');
Покрытие кода тестами
В Laravel 9 мы теперь можем генерировать отчет о покрытии кода тестами с помощью консольной команды php artisan test. Давайте посмотрим, что отобразится, если запустить команду как и раньше.
![Тесты успешно выполнились Тесты успешно выполнились](https://habrastorage.org/getpro/habr/upload_files/117/980/ad4/117980ad49a6c26dcb5465aa781a2809.png)
Если посмотрим подсказку по команде запуска тестов, то увидим 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 - минимальное пороговое значение для покрытия кода. Другими словами, это возможность указать, какой минимальный уровень покрытия кода тестами для нас удовлетворителен.
Я запускаю команду в консоли, но вижу ошибку.
![Ошибка запуска команды Ошибка запуска команды](https://habrastorage.org/getpro/habr/upload_files/840/53a/3b9/84053a3b9edd6ccf970c6f826698aef9.png)
Не установлен драйвер, но, думаю, это легко починить, установив Xdebug для php. Как несложно было догадаться по скриншотам, у меня MacOS, поэтому воспользуюсь встроенными утилитами для установки и перезапуска сервисов. Предположу, что на другие ОС установка не намного сложнее.
pecl install xdebug
brew services restart php
brew services restart nginx
![После установки xdebug и перезагрузки сервисов После установки xdebug и перезагрузки сервисов](https://habrastorage.org/getpro/habr/upload_files/7f4/adb/ff2/7f4adbff27927b8cfd6809e3e4ea798c.png)
Казалось бы, запускаем команду и радуемся результату, но нет, опять ошибка.
![Xdebug ругается :( Xdebug ругается :(](https://habrastorage.org/getpro/habr/upload_files/935/a5b/ad2/935a5bad2faab4ba2659b06655ce5dc7.png)
Xdebug ругается о том, что не установлен режим покрытия кода тестами. Это мы легко исправляем и видим красивое описание покрытия кода.
![](https://habrastorage.org/getpro/habr/upload_files/757/8aa/dbb/7578aadbb23e3f018b71eca8b45fff8b.png)
Пару слов в конце
С публикацией нового релиза, мы увидели немало полезных новшеств, поддержку и реализацию возможностей последней версии языка. Мы уже увидели два патча, и возможно будет еще несколько, прежде чем фреймворк станет по-настоящему достоин продакшна. Но мои руки уже чешутся, чтобы обновиться на проектах до последней 9 версии Laravel.