![](https://habrastorage.org/webt/82/pe/wu/82pewu1kgru2yvayuvehfbeb258.jpeg)
«Пару раз» пришлось занимать миграцией проектов с 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.
index_yii2.php
<?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 или наоборот.