Dar el salto desde PHP procedural hacia un framework como Laravel puede generar dudas al chocar con patrones y convenciones preestablecidas. Analizar cómo estructurar correctamente las operaciones CRUD, el enrutamiento y la arquitectura Modelo-Vista-Controlador —especialmente cuando se trabaja con esquemas de bases de datos heredados o con claves primarias no autoincrementables— permite adaptar el framework a necesidades específicas sin perder el control del flujo.
1. Contexto del esquema y configuración inicial del modelo
Al migrar sistemas o trabajar con bases de datos ya existentes, es común encontrar tablas con restricciones particulares, como claves primarias que no son autoincrementables. Supongamos una tabla de inventario en MariaDB y su respectivo modelo Eloquent ya mapeado en el proyecto DOSCLIC:
MariaDB [erp]> desc inventory;
+--------------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+---------------+------+-----+---------+-------+
| idnumber | bigint(20) | NO | PRI | NULL | |
| product_id | bigint(20) | YES | | NULL | |
| available_quantity | int(11) | YES | | NULL | |
| sale_price | decimal(10,2) | YES | | NULL | |
| description | varchar(250) | YES | | NULL | |
| company_id | bigint(20) | YES | | NULL | |
+--------------------+---------------+------+-----+---------+-------+
Para que Laravel no intente tratar la columna idnumber como un autoincremento estándar de enteros y permita gestionar los registros correctamente, el modelo Inventory.php se configura explícitamente de la siguiente manera:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Inventory extends Model
{
use HasFactory;
protected $table = 'inventory';
protected $primaryKey = 'idnumber';
public $incrementing = false;
protected $keyType = 'int';
public $timestamps = false;
protected $fillable = [
'product_id',
'available_quantity',
'sale_price',
'description',
'company_id',
];
public function product()
{
return $this->belongsTo(Product::class, 'product_id', 'idnumber');
}
public function company()
{
return $this->belongsTo(Company::class, 'company_id', 'idnumber');
}
2. Transición de PHP Procedural a Laravel MVC
En metodologías procedurales tradicionales (similares a arquitecturas tipo Moodle), es común unificar la lógica de creación y edición en un solo archivo (por ejemplo, edit.php?id=...), utilizando marcas de tiempo Unix generadas con time() como clave primaria y validando mediante condicionales si el registro existe para decidir un INSERT o un UPDATE.
Laravel desacopla este flujo mediante métodos HTTP semánticos dentro de un controlador de recursos (Resource Controller). Sin embargo, es perfectamente viable mantener la generación manual de la clave primaria si la lógica de negocio lo requiere, asistiéndonos de Artisan para generar el controlador base:
php artisan make:controller InventoryController --resource --model=Inventory
3. El Controlador y la Generación Manual de Claves Primarias
Debido a que la clave primaria idnumber no es autoincrementable por la base de datos, el controlador debe asignar explícitamente el valor único antes de invocar el método de creación (create). Aquí es donde evaluamos el uso de time() frente a alternativas para evitar colisiones sin perder la legibilidad temporal.
Implementación del controlador InventoryController.php gestionando la validación y la asignación manual del identificador:
<?php
namespace App\Http\Controllers;
use App\Models\Inventory;
use Illuminate\Http\Request;
class InventoryController extends Controller
{
public function index()
{
$inventories = Inventory::all();
return view('inventory.index', compact('inventories'));
}
public function create()
{
return view('inventory.create');
}
public function store(Request $request)
{
$validated = $request->validate([
'product_id' => 'required|integer',
'available_quantity' => 'required|integer',
'sale_price' => 'required|numeric',
'description' => 'nullable|string',
'company_id' => 'required|integer',
]);
$validated['idnumber'] = time();
Inventory::create($validated);
return redirect()->route('inventory.index')->with('success', 'Inventario creado correctamente');
}
public function show(Inventory $inventory)
{
return view('inventory.show', compact('inventory'));
}
public function edit(Inventory $inventory)
{
return view('inventory.edit', compact('inventory'));
}
public function update(Request $request, Inventory $inventory)
{
$validated = $request->validate([
'product_id' => 'required|integer',
'available_quantity' => 'required|integer',
'sale_price' => 'required|numeric',
'description' => 'nullable|string',
'company_id' => 'required|integer',
]);
$inventory->update($validated);
return redirect()->route('inventory.index')->with('success', 'Inventario actualizado correctamente');
}
public function destroy(Inventory $inventory)
{
$inventory->delete();
return redirect()->route('inventory.index')->with('success', 'Inventario eliminado correctamente');
}
}
4. Rutas y Consideraciones sobre Timestamps
Para registrar todas las acciones del controlador de recursos en el sistema de enrutamiento de Laravel, basta con declarar una sola línea en el archivo de rutas web:
use App\Http\Controllers\InventoryController;
Route::resource('inventory', InventoryController::class);
time() como clave primaria facilita formatear y revertir el identificador a un formato de fecha legible mediante date('d/m/Y', $timestamp). Sin embargo, si existe riesgo de inserciones concurrentes en el mismo segundo, considera usar milisegundos multiplicando microtime(true) * 1000 y dividiendo entre 1000 al momento de formatear la fecha.
La historia detrás de la nota
Aprender haciendo implica chocar con las convenciones estrictas de los frameworks modernos y entender cómo adaptarlas a estructuras heredadas sin romper el diseño base.