Upgrade/downgrade via web de APP
Per saber quines migracions tenim pendents (les que estan al sistema de fitxers però no a la base de dades), hem de comparar manualment la llista de totes les migracions disponibles amb l'historial de les ja executades.
MigrateController.php
Utilitzarem findMigrations() per llegir els fitxers del disc i getHistory() per llegir la taula de la BD, i després les compararem.
namespace App\Controllers;
use Config\Services;
use Throwable;
class MigrateController extends BaseController
{
protected $migrations;
public function __construct()
{
$this->migrations = Services::migrations();
}
public function index()
{
// 1. Obtenim l'historial de les ja executades (ordenades per ID descendent)
$history = $this->migrations->getHistory();
// 2. Obtenim TOTES les migracions que existeixen al directori /Database/Migrations
$allAvailable = $this->migrations->findMigrations();
// 3. Calculem les pendents comparant els noms (procediment manual en CI4)
$executedNames = array_column($history, 'name');
$pending = [];
foreach ($allAvailable as $migration) {
// Si el nom de la migració no està a l'historial, està pendent
if (!in_array($migration['name'], $executedNames)) {
$pending[] = $migration;
}
}
return view('admin/migrations', [
'history' => $history,
'pending' => $pending,
]);
}
public function upgrade()
{
try {
// latest() retorna el batch (int) si s'executen, o false si no n'hi ha
if ($this->migrations->latest()) {
return redirect()->back()->with('message', 'Base de dades actualitzada al darrer batch.');
}
return redirect()->back()->with('message', 'No hi havia migracions noves per aplicar.');
} catch (Throwable $e) {
return redirect()->back()->with('error', 'Error en Upgrade: ' . $e->getMessage());
}
}
public function downgrade()
{
try {
// regressive(0) torna enrera l'últim grup (batch) sencer
// Retorna true si ha tingut èxit
$this->migrations->regressive(0);
return redirect()->back()->with('message', 'S’ha desfet l’últim grup de migracions (Rollback).');
} catch (Throwable $e) {
return redirect()->back()->with('error', 'Error en Downgrade: ' . $e->getMessage());
}
}
}
Vista views/admin/migrations.php
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8">
<title>Gestor de Migracions</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
</head>
<body class="container mt-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>🛠️ Gestió de Migracions Web</h2>
<div>
<a href="<?= base_url('migrate/upgrade') ?>" class="btn btn-success">Executar Pendents (Upgrade)</a>
<a href="<?= base_url('migrate/downgrade') ?>" class="btn btn-danger" onclick="return confirm('ATENCIÓ: Això eliminarà taules o columnes segons el mètode down(). Continuar?')">Desfer últim Batch (Rollback)</a>
</div>
</div>
<?php if (session()->getFlashdata('message')): ?>
<div class="alert alert-info"><?= session()->getFlashdata('message') ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger"><?= session()->getFlashdata('error') ?></div>
<?php endif; ?>
<div class="row">
<div class="col-md-8">
<h4>📜 Historial (Taula `migrations`)</h4>
<table class="table table-sm table-bordered">
<thead class="table-dark">
<tr>
<th>Batch</th>
<th>Nom de la Migració</th>
<th>Data d'execució</th>
</tr>
</thead>
<tbody>
<?php if (empty($history)): ?>
<tr><td colspan="3" class="text-center">No hi ha migracions executades.</td></tr>
<?php else: ?>
<?php foreach ($history as $m): ?>
<tr>
<td><span class="badge bg-primary"><?= $m->batch ?></span></td>
<td><code><?= $m->name ?></code></td>
<td><?= date('Y-m-d H:i:s', $m->time) ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<div class="col-md-4">
<h4>⏳ Fitxers Pendents</h4>
<div class="list-group">
<?php if (empty($pending)): ?>
<div class="list-group-item list-group-item-success">Sincronitzat: No hi ha fitxers pendents.</div>
<?php else: ?>
<?php foreach ($pending as $p): ?>
<div class="list-group-item">
<small class="text-muted"><?= $p['version'] ?></small><br>
<strong><?= $p['name'] ?></strong>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
</body>
</html>
-
findMigrations(): Retorna un array amb tots els fitxers trobats aapp/Database/Migrations. Cada element conténame,version,path, etc. -
getHistory(): Retorna un array d'objectes directament de la taula de la base de dades. -
Lògica de comparació: Com que CI4 no té un mètode "màgic" per donar-te la resta, fem un
array_columndels noms ja guardats i filtrem els fitxers del disc que no hi són.
Aquest mètode és molt més robust i "real", ja que utilitza exactament el que el framework posa a la nostra disposició.