«Пару раз» пришлось занимать миграцией проектов с yii1 на yii2 и хочу поделиться с сообществом своим опытом. Ничего сложного в этом процессе нет и откровений не будет. Характер публикации — свой опыт + tutorial для начинающих.
Предпосылки
Если проекты, исторически сделанные на первой версии yii, продолжают развиваться, то каждому разработчику, который с ними работает рано или поздно приходит мысль: "как было бы хорошо, если бы это было на yii2".
Но дальше мыслей дело, обычно, не идет т.к. объем работы представляется колоссальным. В целом, так оно и есть, объем огромный, но все-таки не запредельный — по поговорке «глаза боятся». Плюс для перехода к действиям нужно определенная сила воли (к миграции первого проекта я, внутренне, готовился почти год).
Моя идея миграции — под катом.
Прежде чем «показать код» напишу много букв «зачем это делал я», т.к. причины определяют характер работ. У меня было 2 похожих кейса.
В первом проекте было все просто. Я и совладелец, и единственный разработчик. Поэтому причина «мне просто надоело писать на yii1» — достаточно веская. Писать должно быть «в кайф», иначе на выходе может получиться
Во втором случае, я подрядчик в проекте, который долго писался разными разработчиками без четкой архитектуры. Поэтому на выходе получилась огромная куча legacy кода. Переписать такое – легче
Ситуация патовая: Все понимают, что с кодом проблемы, но вырваться из этого круга не могут. Я предложил постепенную модульную миграцию на yii2. Через 1.5 месяца часть сайта заработала на yii2, а значит появилось место куда мигрировать и инфраструктура, в которой можно осмысленно работать. Конечно, можно продолжать писать плохо, но оправдаться тем, что «посмотрите, какой ужас вокруг» уже нельзя.
Подумать, прежде чем начинать
Для себя я определил несколько правил. Если затевать миграцию, то либо они должны соблюдаться, либо не стоит и начинать.
- Нужно понять и принять «зачем нам это геморрой». Мотивация может быть любая, но она должна с большим запасом перевешивать все минусы.
- Не стоит начинать миграцию, если у проекта нет ясного будущего в прогнозе на 2-3 года вперед. Либо вы делайте это для fun-а.
- Весь новый функционал, всё развитие, всё новое пишем на yii2. В yii1 должно остаться только поддержка. Если это правило не соблюдать, то вы получите сразу 2 активные ветки, которые потребуют в 2 раза больше ресурсов. А, так как ресурсов всегда не хватает, то на этом может всё закончится.
- Не ставить задачу «тупо переписать всё, что есть». «Переписать всё» – это настолько абстрактно и скучно, что если озвучите ее своей команде именно в такой формулировке, то в их погрустневших лицах вы сможете прочитать много о себе интересного.
- Т.к. даже «всё что хочется» сразу переписать нельзя, то нужен план постепенной миграции – по страничкам, по сервисам, по модулям.
- Самое важное! Рассматривать миграцию на yii2 лучше всего, как глубокий рефакторинг всего проекта, направленный на развитие. Тогда может оказаться, что треть проекта вообще не нужно переписывать (если она хорошо она работает «как есть» и требует только минимальной поддержки), а часть проекта можно красиво похоронить. Т.е. не просто убив сервисы/страницы, а так переделать проект, что они будут просто не нужны.
Идея миграции
Моя идея миграции – это одновременная совместная работа двух веток одного проекта на yii1 и yii2 в одном домене, на одном виртуальном хосте. Постепенно, пошагово портировать сервисы / страницы / модули на yii2.
Например, есть сайт, работает на yii1
site.ru/ # главная
site.ru/news # новости
site.ru/pages # страницы
site.ru/comments # отзывы
Переписали новости на yii2, получили:
site.ru/ # главная
site.ru/news # новости (yii2)
site.ru/pages # страницы
site.ru/comments # отзывы
Переписали отзывы, получили
site.ru/ # главная
site.ru/news # новости (yii2)
site.ru/pages # страницы
site.ru/comments # отзывы (yii2)
И так постепенно, страница за страницей, пока не перепишем всё, что нам хочется переписать. Совершенно понятно, что чем больше мы переписали, тем проще идет процесс. Самый сложный всегда первый шаг: первая страница, первый модуль, первый сервис.
Часть первая. Просто чтобы работало одновременно
Добавлю тавтологии, но все, действительно, просто. В самом просто варианте, держите обе ветки (yii1 и yii2) в одной рабочей области, например, так:
/var/www/site/htdocs/ - DOCUMENT_ROOT виртуального хоста
/var/www/site/yii1/ - проект на yii1
/var/www/site/yii2/ - проект на yii2
/var/www/site/public_html/ - DOCUMENT_ROOT виртуального хоста
/var/www/site/protected/ - проект на yii1
/var/www/site/yii2/ - проект на yii2
или так
/var/www/site/ - DOCUMENT_ROOT виртуального хоста
/var/www/site/protected/ - проект на yii1
/var/www/site/yii2/ - проект на yii2
Неважно как назвать и разместить директории. Нужно сделать так, чтобы код на yii1 и yii2 лежал рядом и был доступен для работы в одном виртуальном хосте. А вся магия по одновременной работе будет во входных скриптах index.php и .htaccess.
В чем плюсы такого подхода:
- В вашей среде разработки будут доступны сразу 2 ветки проекта. Это может быть удобно, т.к. долгое время придется работать с ними одновременно, переключаясь туда-сюда.
- Оба проекта будут иметь прямой доступ к DOCUMENT_ROOT, что важно для простой работы со статикой css/js.
Минусы могут быть как эстетические (по типу: нафига мешать все вместе), так и связанные с многопользовательской работой. Да, можно разделить места хранения кода и разделить проекты в среде разработки. Сути это не поменяет, просто добавит нюансов.
Лично я создавал в IDE отдельный проект для ветки yii2, даже когда файлы веток физически лежали рядом.
Базовый пример. Исходники веток проекта yii1/yii2 в одной директории
В DOCUMENT_ROOT используются 2 входных скрипта.
index.php - для yii1
index_yii2.php - для yii2.
htdocs/
htdocs/index.php
htdocs/index_yii2.php
yii1/
yii2/
index.php
Если вы не меняете файловой структуры для проекта на yii1, то ваш index.php останется неизменным.
<?php
/*
* Как-то собираем конфиги и запускаем приложение.
* Код не привожу, т.к. во всех моих проектах yii1 index.php
* был слишком кастомизированный и отличался от оригинала.
*/
$app = Yii::createApplication('WebApplication', $config);
$app->run();
?>
index_yii2.php
<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
// Путь к директории с yii2 относительно index_yii2.php,
// а дальше почти как из «коробки».
$path = '/../yii2/';
require(__DIR__ . $path.'vendor/autoload.php');
require(__DIR__ . $path.'vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . $path.'common/config/bootstrap.php');
require(__DIR__ . $path.'frontend/config/bootstrap.php');
$config = yii\helpers\ArrayHelper::merge(
require(__DIR__ . $path.'common/config/main.php'),
require(__DIR__ . $path.'common/config/main-local.php'),
require(__DIR__ . $path.'frontend/config/main.php'),
require(__DIR__ . $path.'frontend/config/main-local.php')
);
(new yii\web\Application($config))->run();?>
.htaccess
В .htaccess будем делать роутинг между yii1 и yii2
Options +FollowSymlinks
RewriteEngine On
RewriteBase /
# Это отправляем на yii2
#
# Полностью контроллеры
RewriteRule ^test index_yii2.php [L]
RewriteRule ^news index_yii2.php [L]
# По отдельным action
RewriteRule ^page/one index_yii2.php [L]
# Проверка на существование файлов и директорий
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Всё остальное следует на yii1
RewriteRule . index.php
Т.е. нижеперечисленные URL-ы обрабатываются index_yii2.php и работают на yii2.
https://site/test
https://site/news
https://site/page/one
За остальной сайт отвечает index.php (yii1).
Это базовый запуск одновременной работы. Конечно, у каждого будут свои нюансы: команда, пользователи, права доступа, сервера, разные репозитарии и т.п. И у каждого будет свой огород.
Исходники веток yii1/yii2 разнесены по директориям
Например, если у вас свой сервер, то можно разнести хранение веток проекта по разным директориям.
/var/www/site/htdocs - DOCUMENT_ROOT виртуального хоста для site.ru
/var/www/site/protected - проект на yii1
/srv/site_yii2 - проект на yii2
Тогда нужно сменить путь к директории с проектом yii2 в index_yii2.php. Разумеется, так будет работать если отключен, либо настроен open_basedir. Плюс соответствующие права на сервере и отключенный / настроенный, SELinux.
<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
$pathYii2 = '/srv/site_yii2/';
require $pathYii2 . 'vendor/autoload.php';
require $pathYii2 . 'vendor/yiisoft/yii2/Yii.php';
require $pathYii2 . 'common/config/bootstrap.php';
require $pathYii2 . 'frontend/config/bootstrap.php';
$config = yii\helpers\ArrayHelper::merge(
require $pathYii2 . 'common/config/main.php',
require $pathYii2 . 'common/config/main-local.php',
require $pathYii2 . 'frontend /config/main.php',
require $pathYii2 . 'frontend /config/main-local.php'
);
(new yii\web\Application($config))->run();?>
Что дальше
Если на сайте есть пользователи, то единая авторизация — это критичный элемент без которого одновременная работа 2 веток, по факту, невозможна. В следующей статье планирую показать как легко организовать единую авторизацию. Например, сама авторизация остается в yii1, но авторизованные пользователи прозрачно видны в ветке yii2 или наоборот.
Комментарии (20)
VolCh
25.07.2018 09:20Когда переводил проект с symfony1
VolCh
25.07.2018 09:25на Symfony 2+, то на уровне nginx сделал прямое перечисление старых роутов на старые фронтконтроллеры, а по дефолту на новый. Всегда было легко определить сколько ещё переписывать. Да и уменьшение долга было визуально заметно, что мотивировало. А необходимость менять конфиги сервера для добавления роута в легаси мотивировала делать в новой.
glsv Автор
25.07.2018 09:41Тоже размышлял про nginx, но решил, что мне будет лень регулярно лазить в его конфиг. Т.к. пошел по противоположному пути, не по «дефолту на новый», а только роутинг нового пока не накопится достаточный объем.
>по дефолту на новый
А сколько лет было проекту?
Просто в своем я не рискнул перечислить все старые роуты, боясь что-нибудь потерять. А чужой был мне недостаточно знаком для этого.VolCh
25.07.2018 10:38Более 5 лет. В symfony легко просмотреть все имеющиеся роуты одной командой. Была даже мысль автоматической генерации части конфига nginx при деплое.
Jenly
25.07.2018 10:04Если проекты на yii, продолжают развиваться, то каждому разработчику рано или поздно приходит мысль: «как было бы хорошо, если бы это было не Yii»
Akdmeh
25.07.2018 12:28Или даже «не PHP».
Yii отличный инструмент для прототипирования и быстрого создания сайтов.
Сейчас больше всего мешает привязанность к jQuery и слабое разделение фронтенда и бэкенда.
В прочем, версия 3.0 обещает это если не исправить на 100%, то хотя бы улучшить.
Но это уже будет совсем другая история.Jenly
25.07.2018 16:09В 2018 мигрировать на yii2 для сколько-нибудь серьезного приложения странно, минимум на yii3.
А так выглядит, что люди, которые умеют пользоваться только молотком, на все смотрят как на гвозди.
glsv Автор
25.07.2018 21:16слабое разделение фронтенда и бэкенда
Хм… не замечал неудобств. Что имеется ввиду, если чуть конкретнее?Akdmeh
25.07.2018 21:22Если не замечали, то просто не было необходимости. На самом деле, я тоже с этим мало сталкивался. Но если вам нужен сайт не на JQuery, а при использовании Yii2 будет навязывать его почти что по умолчанию — приходится делать немало дополнительных манипуляций, чтобы выключить. У него есть модуль-сборщик JS и CSS в один файл и динамическая подгрузка нужных asset-библиотек, но при этом используются свои решения, а не как в том же Lavarel, где это сделано продуманнее с использованием стандартных инструментов типа Webpack (поправьте, если ошибаюсь).
Это все мелочи. Если ваш сайт завязан на JQuery — все хорошо. PJAX с коробки тоже позволяет вытворять ТАКИЕ динамические сайты, что мало не покажется — очень удобная возможность, особенно учитывая, что поддержка модуля идет почти что с коробки. Но если вы используете Angular/React, то вам придется потратить дополнительное время, чтобы перенастроить Yii2 под них.
Yii3 должен решить эти проблемы, но пока есть как есть. Нужно понимать, что делает Yii2 и зачем, и тогда вы «не выстрелите себе в ногу».glsv Автор
25.07.2018 23:41Про Jquery согласен, есть такое. Я имел ввиду коммент по слабое разделение front/back. Или имеется ввиду, что и там и там торчат уши jquery?
Я не ставил целью избавится от jquery полностью. В этом плане, да — необходимости не было. Но ужа с ежом скрещивал, например, webpack и vuejs применял на yii.
>Нужно понимать, что делает Yii2
Спору нет, но перефразировал бы: «Нужно понимать что делаешь» :)Akdmeh
25.07.2018 23:43Да, «уши jQuery» относятся к этому. Еще критикуют виджеты, которые на себя иногда перетягивают бизнес-логику в неправильных руках (но с ними хотя бы проще: не нужно — не используй).
peresada
26.07.2018 07:20Yii из коробки предлагает много функционала, который жалко не использовать (activeForm) но при этом это PHP-код, причем объемный, и в итоге view-файлы имеют больше серверного кода, чем клиентского. Можно конечно отказаться от ActiveForm, ListView, GridView и т.д. — но они действительно упрощают многие вещи, но загрязняют фронтенд. Хотя в то же время можно использовать расширения-шаблонизаторы. В любом случае — никто не обязывает делать фронтенд именно таким, но так как он такой из коробки, и он такой в документациях и гайдах — все делают так, в итоге многие фронтенд-программисты плюются от этого, несмотря на то, что можно делать иначе.
glsv Автор
26.07.2018 08:04Сейчас мы придем к тому, что front/front-у рознь и все зависит от продукта. Там где не «корпоративное использование», не back-офис, а front для обычных пользователей я стараюсь готовые вообще виджеты не применять (кроме activeForm) по тем же причинам.
ИМХО, все они — явный backend и на frontend я их видеть не хочу.
sambusak
27.07.2018 11:24Не стоит начинать миграцию, если у проекта нет ясного будущего в прогнозе на 2-3 года вперед. Либо вы делайте это для fun-а.
Очень важный пункт т.к. встречаются личности которым переписать или внедрить, что-то новое приоритет всех их жизни.
peresada
Ничего особенного собственно и нет, по такому же принципу можно создать отдельный репозиторий с Yii2 и постепенно клонировать с переписыванием в него модели, контроллеры и т.д.
glsv Автор
Это да, но сам репозитарий в данном контексте ничего не меняет. Он может быть, как отдельным, так и «все в кучу». Я скорее описываю свою методику: выполнять миграцию не «глобально» — «все старое на все новое», а пошагово.
Про «ничего особенного» согласен.
Просто части видел ситуации, когда берется именно глобальная задача и люди в ней увязают, либо от миграции вообще отказываются ибо «страшно».
glsv Автор
Т.е. даже когда стоит задача переписать полнофункциональный «модуль», в котором, например, 5 контроллеров, 10 моделей и которые тянут за собой еще туеву кучу зависимостей то да — это сложновато и иногда страшно (или лениво).
Если же рассматривать задачу в контекте «переписать страницу».
А это всего один action и «пара моделей», а нужные связанные модели реализуются, например, только в рамках маппинга на БД, то всё выглядит значительно проще и делается быстрее.
VolCh
Сложности могут быть если модели были не просто толстые, а божественные.
glsv Автор
Если модели совсем тонкие, то либо их много, либо толстый кто-нибудь другой :)
Конечно, я рассматриваю не ситуации с почти идеальным кодом на который приятно смотреть и приятно работать, а более распространенные и жизненные.