/ laravel

Что нового в Laravel 6.0

В этой статье узнаем, что нового добавили в Laravel 6.0, и ознакомимся с новыми функциями и обновлениями фреймворка.

Да будет Laravel 6.0! Тейлор - создатель Laravel наконец-то показал миру новую версию феймоворка Laravel. С этого момента версия 6.0 официально опубликована и доступна для всеобщего пользования. Давайте посмотрим, какие изменения были внесены в Laravel 6.0, и как мы можем взять их на вооружение в свой девелоперский арсенал.

Что нового?

Семантическое версионирование.
Хоть фреймворк и "перепрыгнул" с версии 5.8 на 6.0, Тейлор сказал, что с точки зрения новых функций и изменений, это обновление больше похоже на переход с 5.8 на 5.9.

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

Миддлверы для Job-ов

Вместо ручного добавления дополнительных, однотипных проверок в Job-ах, теперь вы можете создать миддлвер (аналогично HTTP-миддлверам), вынеся туда общую логику, а затем просто можете привязать его к конкретному Job-у.

В этом примере я продемонстрирую, как можно добавить ограничение скорости для Job-а до 6 секунд. Ранее вы бы добавили этот код в метод handle() добавляя этот код в каждый из Job-ов, где это требует логика работы.

// app/Jobs/JobToBeThrottled.php
// выполняем работу
public function handle() {
    // задаём таймаут в 5 секунд между выполнение работ
    Redis::throttle('key')->block(0)->allow(1)->every(5)->then(function () {
        // выполняем работу
    }, function () {
        // Не удалось снять блокировку, ждём...
        return $this->release(5);
    });
}

В этом новом релизе теперь вы можете вынести этот код из метода handle() и переместить его в специальный общий миддлер. Вы можете создать папку app/Jobs/Middleware (или где угодно, Laravel не призывает к чему-то конкретному) и добавить файл нового миддлера.

// app/Jobs/Middleware/RateLimited.php
...
namespace App\Jobs\Middleware;
use Illuminate\Support\Facades\Redis;

class RateLimited {
    // Выполняем работу в очереди.
    public function handle($job, $next) {
        Redis::throttle('key')
            ->block(0)->allow(1)->every(5)
            ->then(function () use ($job, $next) {
            // Выполняем работу, ставим блокировку (таймаут)
            $next($job);
        }, function () use ($job) {
            // Не удалось снять блокировку, ждём...
            $job->release(5);
        });
    }
}

Теперь вы можете привязать этот миддлер к Job-у, и каждый Job будет проходить через этот миддлвер перед выполнение работы. Если вы знакомы с HTTP-миддлверами, который используете в контроллерах/роутах проекта, концепция одна и та же.

// app/Jobs/JobToBeThrottled.php
use App\Jobs\Middleware\RateLimited;

// указываем миддлвер, который должен сработать в Job-е
public function middleware() {
    return [new RateLimited()];
}

public function handle() {
    // основная логика работы, что-то тут шаманим...
}

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

Ленивые коллекции

В Laravel 6.0 представлены ленивые коллекции, которые позволят вам снизить потребление памяти при работе с большими данными.

Когда вы работаете с Eloquent моделями, вы можете загружать одну модель в память за раз вместо получения всех сразу (потому что их может быть слишком много), используя метод cursor() вместо all().

// Этот код загрузит все Eloquent-модели в оперативную память
// Их может быть очень большое количество, если у вас тысячи записей

$posts = App\Post::all()->filter(function ($post) {
    return $post->id > 500;
});

При замене метода all() на cursor(), в память за один раз загружается только одна Eloquent модель, так как cursor() использует новый экземпляр класса LazyCollection.

$posts = App\Post::cursor()->filter(function ($post) {
  return $post->id > 500;
});

foreach ($posts as $post) {
  echo $post->id;
}

Eloquent улучшенная работа с подзапросами

Eloquent всегда облегчал работу со сложными запросами, создав отличную абстракцию при построении запросов. В новом релизе вам станет работать с ещё проще, когда дело доходит до выполнения подзапросов или запросов в запросе за один вызов. Это полезно, когда вам нужно выбрать информацию из двух таблиц со связями. В Laravel 5.x вы иногда ограничивались тем, что вы могли бы сделать в подзапросе, и часто всё заканчивалось тем, что были вынуждены дополнять запросы с помощью "сырого" SQL-кода DB::raw().

Метод addSelect был добавлен в подзапросы, что должно устранить большую часть этой боли! Eloquent подзапросы теперь также будут иметь доступ к orderBy.

В этом примере представьте, что у вас есть 2 таблицы: отели hotels и reservations брони. Вы хотите узнать, номер какого типа был зарезервирован последним для определенного отеля. Вместо того, чтобы делать два отдельных Eloqunt запроса, теперь вы можете сделать так:

use App\Reservation;
use App\Hotel;

return Hotel::addSelect(['last_booked_room' => Reservation::select('room_type')
  ->whereColumn('hotel_id', 'hotels.id')
  ->orderBy('created_at', 'desc')
  ->latest()
  ->limit(1)  
])->get();

Улучшение пользовательских ответов на авторизацию

Если вы используете Laravel аутентификацию из коробки, проверяя с помощью Gate-ов авторизацию пользователей, то для вас хорошая новость - Laravel 6.0 вводит новый метод Gate::inspect(). Он облегчает создание кастомных, пользовательских сообщений пользователям во время запросов на авторизацию. Теперь вы можете указать конкретное сообщение об ошибке, если запрос авторизации (проверки прав на доступ к данным) пользователя был отклонен.

Например, представим, что у вас есть метод, который определяет, имеет ли пользователь права на редактирование сообщения. До версии 6.0 было трудно передать конкретное сообщение о том, по каким причинам доступ был отклонён, и были некоторые трудности с отображением подобного сообщения обратно пользователю.

С добавлением метода Gate::inspect() теперь вы можете легко задать сообщение ошибки, которое получит пользователь и отправить его ему обратно.

К примеру, если вы хотите ограничить редактирование сообщений любому пользователю, не являющимся администратором, вы бы создали Gate правило для редактирования поста.

// App/Providers/AuthServiceProvider.php
...

public function boot()
{
    $this->registerPolicies();

    // Определите гейта (Gate), который проверяет, кто может редактировать сообщения
    Gate::define('edit', function ($user) {
        return $user->isAdmin()
          ? Response::allow()
          : Response::deny('Только администратор может редактировать посты.');
    });
}

Обычно, вы бы просто используете метод allow() или denies(), для того, чтобы определить, имеет ли этот пользователь права для выполнения этого действия. В результате, при выполнении этого метода, мы получим булев ответ: true или false, который зависит от правил, заданных в методе Gate::define(), определенного выше. Выполнив код, как в примере, вы получите только булево значение, никаких подробностей, и пользовательских ошибок.

if (Gate::allows('edit')) {
    // пользователь имеет права на редактирование...
}

if (Gate::denies('edit')) {
    // пользователю запрещено редактировать запись. Ругаемся на него...
}

Но, если вы хотите получить доступ к более подробному, кастомному ответу о причине отказа, вы можете использовать метод Gate::inspect.

// получаем подробный ответ о результате проверки авторизации пользователя 
$response = Gate::inspect('edit');

if ($response->allowed()) {
    // Дать пользователю возможность отредактировать этот пост
} else {
    // Показать подробное сообщение о причинах отклонения 
    
    // 'Только администратор может редактировать посты.'
    echo $response->message();
}

Laravel UI composer библиотека

Ещё одно обновление, о котором следует знать - это добавление нового пакета laravel/ui. Теперь, при создании нового проекта Laravel 6.0 немного изменилась философия поставки фронтенд-окружения. С этой версии, по-умолчанию, у вам не будет установлен VueJs или Bootstrap.

Теперь ваш файл app.js выглядит так:

require('./bootstrap');

А bootstrap.js, убрав все комментарии, выглядит так:

window._ = require('lodash');
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

Теперь в философии Laravel нет жесткого навязывания использования VueJs, Bootstrap в проекте.

Тейлор и команда вынесли в отдельный пакет заготовки для проектов с ядром под различный фронтенд. Этот пакет теперь включает в себя пресеты для React, Vue и Bootstrap.

Для того, чтобы подключить этот пакет, установим его через composer: composer require laravel/ui.

После чего, у нас добавятся новые ui команды для работы с фронтендом.

// Сгенерировать базовые шаблоны...
php artisan ui vue
php artisan ui react

// Сгенерировать шаблоны с страницами login/registration
php artisan ui vue --auth
php artisan ui react --auth

Удаление ранее помеченных deprecated-хелперов

В новой версии все str_, array_ хелперы удалены из ядра по умолчанию, и перемещены в новый пакет laravel/helpers. Для того, чтобы вернуть старые хелперы Laravel в проект, необходимо через composer подключить этот пакет:

composer require laravel/helpers

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

С полным списком прочих мелких модификаций можете ознакомиться на странице.

Ребрендинг

С каждой новой мажорной версией также появляется редизайн официального сайта Laravel, и 6.0 не является исключением!
laravel-6-logo

В дополнение к новому логотипу, вы также можете найти увидеть новый дизайн сайта Laravel.com.

Laravel Vapor

И последнее обновление, но не менее важное, чем остальные - запуск долгожданного продукта экосистемы Laravel - Laravel Vapor.

До Laravel Vapor большинство людей использовали Laravel Forge для деплоя и развертывания своих приложений. С помощью Forge вы можете подключит любой выбранный вами сервер (Digital Ocean, AWS и т.д.), а Forge уже сам позаботится о том, чтобы быстрее, и с вашим минимальным участием выгрузить и развернуть приложение. Это, конечно, круто, но вам всё равно приходилось вручную устанавливать некоторые компоненты/обновления.

Laravel Vapor делает всё то, что может Forge, и даже немного больше. Теперь, вместо того, чтобы самостоятельно управлять и обновлять серверы для вашего приложения Laravel, Vapor полностью избавляет вас от регистрации и управления сервером!

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

Вот еще несколько удивительных и полезных функций, которые предоставляет Vapor:

  • Масштабирование по требованию - может выполнять задачи сразу же, по мере их поступления
  • Работает на основе AWS
  • Готов к скачкам трафика
  • Нулевой простой во время деплоя (т.е. во время обновления сайта ваше приложение не "падает" на время их применения)
  • Несколько рабочих сред с возможностью бесплатного тестирования. Вы получите URL, вида https://snowy-hurricane-123456789456654.vapor.build
  • Режим технического обслуживания
  • Логирование с возможностью поиска
  • Создавайте и масштабируйте вашей базы данных прямо из Vapor
  • Резервное копирование БД и восстановление информации из бекапа
  • Метрики и мониторинг данных с уведомлениями
  • Возможность покупки доменов и управления их DNS-записями прямо из панели Vapor
  • Автоматическое создание SSL-сертификата
  • Job-ы, воркеры, обновления PHP и многое другое, настраиваемое и работающее без вашего участия, автоматически.
  • Простое и легко настраиваемое развёртывание приложения с помощью простого файла vapor.yaml.

Стоимость всего этого составляет 39 долларов в месяц или 399 долларов в год с неограниченным количеством членов команды и проектов. Имейте в виду, что к этому всему ещё придётся добавить собственные расходы на AWS-сервера. Но, это избавляет вас от многих проблем с деплоем приложения, масштабирования и администрирования серверов вручную.

Обновление Laravel до версии 6.0

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

Как обновить Laravel до версии 6.0

Для начала, в вашем файле composer.json измените зависимость фреймворка Laravel с 5.8.* На ^6.0. Если вы используете версию более раннюю, чем 5.8, то рекомендуется сначала выполнить обновление до 5.8, прежде чем переходить на 6.0.

// В вашем файле composer.json
"laravel/framework": "^6.0",

После чего, в терминале выполните:

composer update

Laravel Shift

Еще один удобный способ обновления версии фреймворка, особенно для более крупных приложений - использовать Laravel Shift. Laravel Shift - это автоматизированный инструмент, который произведёт обновление вашего приложения за вас.

Это работает так:

  • Сначала предоставляем Laravel Shift доступ к вашему репозиторию приложения на Laravel
  • Выберите версию Laravel, на которой в данный момент работаете, и на какую версию вы хотите обновиться
  • Shift создаст новую ветку в репозитории и изменит всё то, что необходимо изменить в вашем коде для обновления
  • После чего, Shift выполнит pull-request, для того, чтобы вы могли проверить и решить, следует ли его мерджить

Процесс очень простой и является идеальным вариантом, если простое изменение версии пакетов в вашем composer.json не сработало. Услуга не бесплатная, не такая большая. Инструкцию по обновлению вы можете найти на странице.

Нужно ли обновляться?

Laravel 6.0 - это последняя версия с Long-Term поддержкой, что означает гарантированное исправление ошибок в течение двух лет, а исправления ошибок безопасности - в течение трёх лет. Предыдущий релиз LTS с аналогичными гарантиями был Laravel 5.5.

Эта таблица с сайта Laravel описывает текущий график поддержки: support.

Поскольку исправление ошибок в предыдущем LTS-релизе заканчиваются очень скоро, возможно, стоит обновить его до 6.0, чтобы точно быть уверенным в том, что ваш проект будет поддерживаться ещё несколько лет.

Резюме

Laravel вызвал большой энтузиазм и волнение в мире PHP, и было здорово видеть, как фреймворк и его сопутствующие продукты продолжают улучшаться и двигать вперёд разработку. И из этой статьи вы узнали, что нового в Laravel 6.0, какие фичи были добавлены, и что было удалено в Laravel 6.0.

С введением семантического версионирования, вы сможете обновляться между мажорными выпусками, не беспокоясь об огромных, кардинальных изменениях структуры фреймворка. Этот подход означает больше, чем просто управление версиями - он показывает, что после всех улучшений, появившихся в 5.x, фреймворк теперь находится в стабильной точке, и будет двигаться только вперед на основе уже стабильного и надёжного ядра (и архитектуры)!

В дополнение к семантическому версионированию, в версии 6.0 улучшена скорость доступа к коллекциям, добавлены Eloquent-выборку в подзапросах, добавлены миддлеверы в Job-ах, и многое другое.