Про обновление Laravel

На днях нашлось немного времени стряхнуть пыль с одного старого проектика на Ларавеле и немножко им позаниматься. Стряхнул я, значит, пыль, а под пылью написано — Laravel 5.1. А сейчас-то уже 5.4. А в июле вообще обещают выпустить Laravel 5.5 LTS, новыми плюшками из которого пестрят тематические новости и рассылки уже несколько месяцев.

В общем, стал обновляться до актуальной версии. Это и само по себе хорошо (см. новые плюшки), и полезно чтобы вспомнить, как вообще в коде всё было устроено, втянуться. Плюс тренировка перед выходом 5.5 — можно отработать порядок действий, а заодно обнаружить изъяны в составе тестов, если они есть (а они есть).

TL;DR: далее написано об обновлении проекта на Laravel с 5.1 до 5.4, некоторых подводных камнях, на которые пришлось наступить в процессе, и несколько слов об обратной совместимости.

Порядок обновления минорных версий 5.1 → 5.2 → 5.3 → 5.4 описан в документации (раз, два, три) вместе со всеми изменениями, которые нужно проверять и править. Глядя на длинные списки для каждого из обновлений немного приуныл и выбрал другой способ: взять чистую и новую установку Laravel 5.4 и на нее накатить логику проекта.

Конечно, сверяться с изменениями всё равно надо, но так хотя бы не нужно некоторые вещи по три раза повторять.

Всякие мелочи — перенос routes.php в отдельную директорию, разделение его на 4 разных файла, перенос listener-ов в другое место и т.п. — поправить нетрудно.

Первая проблема обратной совместимости, с которой столкнулся — это обращение к Auth::user() в конструкторе контроллера чтобы получить модель текущего пользователя. Оказалось, что в новых версиях изменили порядок инициализации, и теперь когда создается контроллер — middleware еще не отработали и, соответственно, залогиненный пользователь пока не доступен. В свое время это изменение вызвало некоторую бучу, т.к. код

public function __construct() {
    $this->user = Auth::user();
}

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

Так или иначе, после жалоб пользователей, всем, кто хочет в конструкторе получить модель юзера, дали возможность объявить middleware на замыкании. Как-то так:

public function __construct()
{
    $this->middleware(function ($request, $next) {
        $this->user= Auth::user();
        return $next($request);
    });
}

Также немножко поменялась доступная из коробки логика авторизации: контроллеров стало четыре вместо двух, и чтобы её включить нужно разово вызвать php artisan make:auth. Чтобы починить регистрацию по инвайтам, по большей части нужно было просто код из одного контроллера в другой перетащить.

Вообще, в Ларавеле так уж вышло, что минорные версии (5.1, 5.2 и т.д.) вполне себе нарушают обратную совместимость. После Друпала это немного непривычно, но чего уж. Сама по себе обратная совместимость — это компромисс в споре между, так сказать, консерваторами и прогрессорами. Первым хочется пилить проекты и продвигать их, а не подстраиваться постоянно под обновления. Ну и чтобы не ломать то, что уже работает. А вторых хлебом не корми — дай снова сделать все еще немножко круче, и чтобы всякое legacy под ногами не путалось.

У обоих подходов есть и плюсы, и их логические продолжения — минусы. В случае Ларавела, над движением (простите) движка вперед работают, по-видимому, в основном прогрессоры. Поэтому хочешь новых фич — будь готов периодически рихтовать старый код под новые версии. И пиши тестов побольше, чтобы не свихнуться, каждый раз перепроверяя, всё ли после обновления работает.

До тестов, кстати, прогресс добрался тоже: попереименовали наверное половину методов. С каким-нибудь visit() это понятно — ну перешли на Dusk, да, там по-другому работает. С assertDatabaseHas() тоже ладно — название явно лучше и конформнее, чем старое seeInDatabase(), можно пройтись по тестам, переименовать. Но во когда assertResponseOk() и assertResponseCode() надо везде менять на assertStatus(), который делает то же самое — это уже немножко бесит. Вот зачем?

В целом апгрейд прошел нормально и ничего не сломалось. По шагам процесс можно описать так:

  1. Установить Laravel 5.4.
  2. php artisan make:auth.
  3. Перенести старый routes.php в routes/web.php и routes/api.php.
  4. Перенести контроллеры, модели, вьюзы, middleware.
  5. Перенести события и их обработчики.
  6. Проверить работу авторизации.
  7. Перенести фронт-энд.
  8. Восстановить конфиги.
  9. Перенести тесты.
  10. Тестировать и исправлять, пока всё не заработает.

Кстати, у выбранного метода «накатить старый код на новый Laravel 5.4» нашелся небольшой недостаток. Поскольку код накатывается заново, git при попытке смёрджить ветку обновления со старой версией даст конфликт на каждый файл. Чтобы сохранить историю изменений в ветке master, можно принудительно разрешить все конфликты в пользу новой ветки примерно так:

git checkout new-branch
git merge -s ours master
git checkout master
git merge new-branch

Комментарии