Есть вполне определенное чувство страха, возникающее, когда вы впервые открываете легаси-кодовую базу. Везде летают глобальные переменные, вызовы mysql_query() зарыты на глубину трех функций, файл functions.php имеет длину в 4000 строк, а тестов ровно ноль. И тем не менее, бизнес от этого зависит. Клиенты этим пользуются. И теперь вас попросили это модернизировать.
Миграция легаси-сайта на Laravel — это не просто техническое упражнение, это задача по переводу с двух совершенно разных философий. В этом руководстве рассматривается структурированный подход, который позволяет выполнять миграцию постепенно, сохранять существующую систему в рабочем состоянии и избежать классической ловушки бесконечного переписывания.
Почему инкрементальная миграция лучше переписывания «с нуля»
Возникает искушение заморозить работу над фичами, переписать всё с нуля и запустить совершенно новую систему шесть месяцев спустя. Это почти никогда не работает. Требования меняются, в старой системе появляются патчи, которых нет в новой, а команды теряют темп.
Паттерн «душитель» (strangler fig) — когда новая система оборачивается вокруг старой, а трафик перенаправляется по кусочкам — почти всегда является правильным решением. Laravel отлично для этого подходит, потому что его маршрутизация, middleware и сервис-контейнер могут сосуществовать с легаси-кодом, запущенным на том же сервере.
Шаг 1: Аудит перед тем, как вы к чему-либо прикоснетесь
Прежде чем написать хотя бы одну строку на Laravel, составьте карту существующей системы:
-
Точки входа: какие файлы веб-сервер обслуживает на самом деле? Часто в легаси PHP-приложениях есть десятки файлов
index.phpв поддиректориях. -
Схема базы данных: экспортируйте полную ERD-диаграмму. Такие инструменты, как SchemaSpy или даже простой экспорт через
SHOW CREATE TABLE, станут вашими лучшими друзьями. - Внешние зависимости: платежные шлюзы, настройки SMTP, сторонние API — документируйте каждое исходящее подключение.
- Модель сессий и аутентификации: аутентификация основана на сессиях? На куках? На кастомной таблице токенов?
Документируйте это безжалостно. Вы будете постоянно к этому возвращаться.
Шаг 2: Развертывание Laravel рядом с легаси-приложением
Установите свежее приложение Laravel в соседнюю директорию. Настройте веб-сервер (в данном случае проще всего использовать nginx) для проксирования запросов либо в легаси-приложение, либо в Laravel на основе префикса URL.
server {
listen 80;
server_name example.com;
# New Laravel routes
location /account {
proxy_pass http://127.0.0.1:8001;
}
location /api {
proxy_pass http://127.0.0.1:8001;
}
# Everything else goes to the legacy app
location / {
root /var/www/legacy;
index index.php;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Laravel работает на PHP 8.x на порту 8001 через отдельный пул FPM. Легаси-приложение продолжает обрабатывать все остальное без каких-либо изменений.
Шаг 3: Общее состояние сессий
Самая сложная проблема в этом паттерне миграции — аутентификация. Пользователи, вошедшие в легаси-систему, не должны сталкиваться с необходимостью повторного входа, когда запрос обрабатывает Laravel.
Прагматичное решение: использовать общий бэкенд сессий. Если легаси-приложение использует файловые сессии PHP, Laravel тоже может их читать, но это выглядит грязно. Лучше сначала перевести легаси-приложение на использование базы данных или хранилища сессий Redis, а затем настроить Laravel на использование того же хранилища.
// config/session.php in Laravel
'driver' => 'redis',
'connection' => 'default',
'cookie' => 'legacy_session', // must match the legacy app's cookie name
Со стороны легаси используйте кастомный обработчик сессий, который записывает данные в Redis с тем же форматом ключей. PhpRedis или Predis отлично подходят для этой задачи. Это обеспечивает настоящий единый вход (SSO) между старой и новой системами без предварительной полной миграции аутентификации.
Шаг 4: Оборачивание легаси-базы данных в модели Eloquent
Не переименовывайте таблицы. Пока не приводите схему к нормальному виду. Просто укажите Eloquent на то, что уже существует.
class LegacyUser extends Model
{
protected $table = 'tbl_users'; // old naming convention
protected $primaryKey = 'user_id';
public $timestamps = false; // no created_at/updated_at columns
protected $casts = [
'is_active' => 'boolean',
'created' => 'datetime', // maps the legacy column name
];
public function getCreatedAtAttribute()
{
return $this->attributes['created'] ?? null;
}
}
Это дает вам всю мощь Eloquent — отношения, скоупы, построитель запросов — без прикосновений к базе данных. Рефакторинг схемы будет позже, после того как вы докажете, что миграция работает.
Шаг 5: Помодульная миграция маршрутов
Сначала выберите наименее рискованный и наиболее изолированный модуль. Идеально подходят панели администратора, страницы отчетности или внутренние инструменты. Избегайте платежных сценариев и основного процесса оформления заказа, пока не обретете уверенность.
Для каждого модуля:
- Воспроизведите функциональность в контроллере Laravel и представлении Blade (или компоненте Livewire).
- Напишите функциональные тесты, покрывающие позитивный сценарий и краевые случаи, обнаруженные во время аудита.
- Обновите конфигурацию nginx для проксирования этого префикса URL на Laravel.
- Мониторьте логи ошибок в течение 48–72 часов перед переходом к следующему модулю.
// Example feature test to lock in behaviour before refactoring
public function test_order_history_displays_for_authenticated_user(): void
{
$user = LegacyUser::factory()->create();
$orders = LegacyOrder::factory()->count(3)->for($user)->create();
$response = $this->actingAs($user)->get('/account/orders');
$response->assertOk();
$response->assertSee($orders->first()->order_ref);
}
Тесты, написанные на основе легаси-поведения, становятся защитной сеткой для каждого последующего рефакторинга.
Шаг 6: Миграция схемы — когда вы будете готовы
Как только модуль полностью заработал на Laravel и был протестирован, вы можете задуматься о нормализации схемы. Используйте миграции Laravel с методом Schema::table() , чтобы добавлять колонки, переименовывать вещи и внедрять правильные индексы — но поначалу делайте это аддитивно. Сохраняйте старую колонку, выполняйте запись в обе, проверяйте целостность данных, а затем удаляйте старую колонку в одной из последующих миграций.
Schema::table('tbl_users', function (Blueprint $table) {
$table->string('email')->nullable()->after('user_email');
$table->index('email');
});
// Backfill in a job, not in the migration itself
LegacyUser::whereNull('email')->eachById(function ($user) {
$user->update(['email' => $user->user_email]);
});
Никогда не выполняйте бэкфилл (заполнение) больших наборов данных прямо в миграции. Это блокирует таблицу и вызывает простои.
Шаг 7: Финальный перенос
Когда все маршруты обрабатываются Laravel, а легаси-точки входа стали мертвым кодом, вы готовы к полному переключению. На этом этапе:
- Переименуйте или заархивируйте легаси-директорию.
- Обновите nginx, чтобы корневой путь (root) указывал напрямую на папку
public/Laravel. - Удалите старый пул FPM.
- Запустите
php artisan route:cache,config:cacheиview:cacheна продакшене.
Храните заархивированную легаси-кодовую базу не менее 90 дней. Вы еще захотите к ней обратиться.
Выводы, добытые кровью и потом на продакшене
Несколько вещей, усвоенных в ходе реализации подобных проектов для реальных клиентов (включая миграции, выполненные в рамках работы на hanzweb.ae), о которых обычно не упоминают в учебниках:
- Не позволяйте идеальному быть врагом работающего. Уродливые модели Eloquent, отражающие запутанную схему — это нормально. Наведите в них порядок, когда система стабилизируется.
-
В легаси-приложении будет недокументированная бизнес-логика. Прочитайте старый код перед тем, как его удалять. У каждого загадочного оператора
ifесть своя история. - Логирование — ваш лучший инструмент диагностики. Добавьте структурированное логирование через Monolog с первого же дня. Вы скажете себе спасибо, когда что-нибудь сломается на продакшене в 2 часа ночи.
- Общайтесь со стейкхолдерами по поводу сроков применения паттерна «душитель». Миграции такого рода занимают месяцы, а не недели. Задайте эти ожидания заранее.
Заключение
Легаси-миграции проходят успешно, когда к ним относятся как к серии небольших, проверяемых шагов, а не как к единому героическому переписыванию. Паттерн «душитель», общие сессии, обертки Eloquent над существующими таблицами и функциональные тесты, написанные под легаси-поведение — всё это не блестящие гламуром техники, но именно они действительно доводят дело до релиза.
Конечный результат стоит усилий: кодовая база, в которой вы можете разобраться, которую можете уверенно тестировать и передать другому разработчику без написания 40-страничного онбординг-документа.
Комментарии (0)
Пока нет комментариев — будьте первым.