Hace unos meses, nuestro equipo enfrentó un gran desafío: migrar parte de un sistema heredado escrito en **PHP puro** a **Laravel**, sin afectar la producción y manteniendo la misma base de datos. Fue un proceso lleno de aprendizajes, y hoy quiero compartir cómo lo logramos, los errores que evitamos y las estrategias que nos funcionaron.
Uno de los mayores temores en una migración es modificar la estructura de la base de datos. Decidimos no tocar las tablas existentes y trabajar con los mismos esquemas, pero aprovechando Eloquent, el ORM de Laravel.
Supongamos que teníamos una tabla users
con una estructura poco convencional:
CREATE TABLE users (
user_id INT PRIMARY KEY,
username VARCHAR(50),
signup_date DATETIME,
is_active TINYINT(1)
);
En Laravel, definimos el modelo respetando la estructura antigua:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model {
protected $table = 'users';
protected $primaryKey = 'user_id';
public $timestamps = false;
protected $casts = [
'is_active' => 'boolean',
];
protected $dates = [
'signup_date',
];
}
Beneficio:
Como el sistema legacy aún escribía en la base de datos, usamos triggers para sincronizar cambios importantes con el nuevo sistema.
Queríamos auditar cambios en la tabla orders
sin modificar el código legacy:
DELIMITER //
CREATE TRIGGER after_order_update
AFTER UPDATE ON orders
FOR EACH ROW
BEGIN
INSERT INTO order_audit_log (order_id, changed_by, old_status, new_status)
VALUES (NEW.id, CURRENT_USER(), OLD.status, NEW.status);
END //
DELIMITER ;
¿Por qué?
No migramos todo de golpe. Priorizamos módulos con bajo impacto en producción:
Módulo | Complejidad | Riesgo | Beneficio Migración |
---|---|---|---|
Sistema de logs | Baja | Bajo | Fácil de testear |
Gestión de FAQs | Media | Medio | Mejor rendimiento |
Procesos de pago | Alta | Alto | Postergado |
Estrategia:
Para no sacar nunca la app de producción, implementamos un ruteador simple que decidía qué sistema usar según la ruta:
// En public/index.php (Laravel) y legacy/index.php (PHP puro)
$requestPath = $_SERVER['REQUEST_URI'];
if (str_starts_with($requestPath, '/new/')) {
// Laravel maneja esta ruta
require __DIR__ . '/../laravel/public/index.php';
} else {
// El sistema legacy sigue respondiendo
require __DIR__ . '/legacy/index.php';
}
Ventaja:
El sistema legacy no tenía tests. Introdujimos PHPUnit y Pest para el nuevo código:
// Tests/Feature/UserTest.php
test('User model respects legacy schema', function () {
$user = User::create([
'user_id' => 999,
'username' => 'testuser',
'is_active' => 1,
]);
$this->assertDatabaseHas('users', [
'user_id' => 999,
'is_active' => true,
]);
});
Resultado:
Usamos daily standups y un documento compartido para registrar:
¿Estás pensando en migrar un sistema legacy? Empieza pequeño, mantén la base de datos estable y no subestimes el poder de los tests.
¿Has pasado por una migración similar? ¡Comparte tus aprendizajes en los comentarios! 👇
#PHP #Laravel #LegacyCode #Refactoring #WebDevelopment
Me dedico a crear soluciones web eficientes y a compartir mi conocimiento con la comunidad de desarrolladores.