dantepiazza/laravel-importer

Importador polimórfico de archivos Excel/CSV para Laravel

Maintainers

Package info

github.com/dantepiazza/laravel-importer

pkg:composer/dantepiazza/laravel-importer

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-07-30 00:49 UTC

This package is auto-updated.

Last update: 2026-07-30 01:08:38 UTC


README

Latest Version on Packagist License: MIT

Un importador de archivos Excel y CSV para Laravel, diseñado para ser 100% agnóstico y polimórfico. Permite procesar grandes volúmenes de datos en segundo plano (Queues) vinculando el proceso a cualquier modelo de tu aplicación.

Características Principales

  • Agnóstico y Polimórfico: Un solo motor para importar cualquier modelo (Affiliate, Product, User, etc.).
  • Procesamiento por Chunks: Maneja archivos de cientos de miles de filas sin agotar la memoria RAM.
  • Data Cleansing (Filters): Soporta limpieza de datos mediante métodos en el modelo o clases de filtrado dedicadas.
  • Silent Mode (Default): Por seguridad y performance, las importaciones no disparan eventos de Eloquent ni Observers por defecto.
  • Sistema de Cancelación: Permite abortar importaciones en tiempo real mediante un sistema de Cache/Redis.
  • Reintentos: una importación fallida se puede reintentar (Importer::retry()) hasta max_attempts veces sin tener que resubir el archivo.
  • Campos fijos por importación (extraFields): mergea valores de contexto (ej: la FK del padre — una página, un tenant) en cada fila importada, sin que tengan que venir en el archivo.
  • Eventos de Laravel: ImportStarted, ImportCompleted, ImportFailed, ImportRowFailed — para que la app consumidora reaccione (notificaciones, websockets, etc) sin acoplarse a los hooks del modelo.
  • Seguimiento Real-time: Persistencia del progreso (%), filas creadas, actualizadas, fallidas y logs de errores.
  • Logging Integrado: Todos los eventos del ciclo de vida se registran en el canal de log configurado.
  • Tabla namespaced: la tabla de tracking se llama importer_imports por defecto (configurable vía IMPORTER_TABLE) para no chocar con una tabla imports de dominio que la app consumidora ya tenga.

Requisitos

  • PHP >= 8.2
  • Laravel 10.x o 11.x
  • maatwebsite/excel ^3.1
  • Un driver de Queue configurado (database, Redis, etc.)
  • Un driver de Cache configurado (para el sistema de cancelación)

Instalación

  1. Instala el paquete vía composer:
composer require dantepiazza/laravel-importer
  1. Publica y ejecuta las migraciones:
php artisan vendor:publish --tag="importer-migrations"
php artisan migrate
  1. (Opcional) Publica el archivo de configuración:
php artisan vendor:publish --tag="importer-config"

Variables de Entorno

Agrega estas variables a tu .env según necesites:

# Disco de almacenamiento (debe estar en config/filesystems.php)
IMPORTER_DISK=local

# Cola donde se despachan los jobs
IMPORTER_QUEUE=default

# Filas que Maatwebsite lee por iteración (mantener <= 500 para archivos grandes)
IMPORTER_CHUNK_SIZE=200

# Cada cuántas filas se actualiza el progreso en DB
IMPORTER_PROGRESS_INTERVAL=50

# Cada cuántas filas se consulta el Cache para detectar cancelación
IMPORTER_CANCEL_INTERVAL=50

# Canal de log del paquete (null para deshabilitar)
IMPORTER_LOG_CHANNEL=stack

# Máximo de errores guardados en la columna errors
IMPORTER_MAX_ERRORS=100

# Nombre de la tabla de tracking (namespaced por defecto para no chocar
# con una tabla `imports` de dominio que ya tengas)
IMPORTER_TABLE=importer_imports

# Máximo de reintentos permitidos por importador::retry()
IMPORTER_MAX_ATTEMPTS=3

# Timeout del Job de procesamiento, en segundos
IMPORTER_JOB_TIMEOUT=3600

Configuración del Modelo

Cualquier modelo que desees importar debe implementar la interfaz Importable. Usa el trait CanBeImported para obtener implementaciones vacías de los hooks.

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use DantePiazza\LaravelImporter\Contracts\Importable;
use DantePiazza\LaravelImporter\Traits\CanBeImported;

class Product extends Model implements Importable
{
    use CanBeImported;

    public static function getImportConfig(): array
    {
        return [
            'unique_key'      => 'sku',
            'default_mapping' => [
                // Mapeo simple: columna Excel => campo del modelo
                'Code'         => 'sku',
                'Name'         => 'name',
                // Mapeo con filtro (método del modelo)
                'Description'  => ['field' => 'description', 'filter' => 'sanitizeHTML'],
                // Mapeo con clase de filtro dedicada (debe ser invocable con __invoke)
                'Date publish' => ['field' => 'published_at', 'filter' => \App\Filters\DateFilter::class],
            ],
        ];
    }

    // Ejemplo de filtro interno
    public function sanitizeHTML(mixed $value): mixed
    {
        return strip_tags((string) $value);
    }
}

Nota sobre el mapping: Las claves del mapping no distinguen mayúsculas/minúsculas ni espacios al inicio/final. 'Date publish', 'date publish' y ' DATE PUBLISH ' son equivalentes.

Uso Básico

Iniciar una Importación

use DantePiazza\LaravelImporter\Facades\Importer;
use App\Models\Product;

// Guardar el archivo primero
$path = $request->file('archivo')->store('imports');

$import = Importer::execute(
    modelClass:   Product::class,
    filePath:     $path,
    uniqueKey:    '',     // Opcional: sobreescribe el unique_key del modelo
    mapping:      [],     // Opcional: sobreescribe el default_mapping del modelo
    userId:       auth()->id(),
    silentMode:   true,   // Por defecto true
    extraFields:  [],     // Opcional: valores fijos que se mergean en cada fila
    importableId: null,   // Opcional: ID de una entidad relacionada (relación `importable`)
);

// $import->id lo usarás para consultar el progreso o cancelar

Campos fijos por importación (extraFields)

Útil cuando todas las filas importadas pertenecen a una misma entidad padre que no viene (ni debería venir) como columna en el archivo — por ejemplo, importar productos de un catálogo que pertenecen todos a una misma página/tienda:

$import = Importer::execute(
    modelClass:  Product::class,
    filePath:    $path,
    extraFields: ['page_id' => $page->id], // se mergea en cada fila, gana sobre el mapping
    importableId: $page->id,               // opcional: referencia en la relación `importable`
);

Cancelar una Importación

use DantePiazza\LaravelImporter\Facades\Importer;
use DantePiazza\LaravelImporter\Models\Import;

$import = Import::find($id);
Importer::cancel($import); // Lanza LogicException si ya está en estado terminal

Reintentar una Importación Fallida

$import = Import::find($id);

if ($import->is_retryable) {
    Importer::retry($import); // relee el archivo completo, resetea contadores/errores
}

Monitoreo y Estados

$import = Import::find($id);

$import->status;         // pending | processing | completed | failed | cancelled
$import->progress;       // 0.00 a 100.00
$import->total_rows;     // Total de filas detectadas
$import->processed_rows; // Filas procesadas hasta ahora
$import->created_count;  // Registros nuevos insertados
$import->updated_count;  // Registros existentes modificados
$import->failed_count;   // Filas que fallaron
$import->errors;         // Array de ['row' => N, 'message' => '...']

// Accessors de estado
$import->is_processing;  // bool
$import->is_completed;   // bool
$import->is_cancelled;   // bool
$import->is_terminal;    // bool — true si completed, failed o cancelled
$import->is_retryable;   // bool — true si status=failed y attempts < max_attempts
$import->failure_rate;   // float — % de filas fallidas sobre procesadas
$import->attempts;       // int — cuántas veces se procesó (1 la primera vez)

Eventos

El paquete dispara eventos estándar de Laravel en los puntos clave del ciclo de vida — registrá Listeners en tu EventServiceProvider (o vía Event::listen()) para reaccionar sin acoplarte a los hooks del modelo:

use DantePiazza\LaravelImporter\Events\ImportStarted;
use DantePiazza\LaravelImporter\Events\ImportCompleted;
use DantePiazza\LaravelImporter\Events\ImportFailed;
use DantePiazza\LaravelImporter\Events\ImportRowFailed;

Event::listen(ImportCompleted::class, function (ImportCompleted $event) {
    // $event->import->created_count, ->updated_count, ->failed_count...
    Notification::send($event->import->user, new ImportFinishedNotification($event->import));
});

Event::listen(ImportRowFailed::class, function (ImportRowFailed $event) {
    // $event->rowIndex, $event->message — útil para un feed de progreso en vivo (websockets)
});

Hooks Disponibles

En tu modelo podés definir lógica adicional:

// Se ejecuta UNA vez antes de procesar la primera fila
public function beforeImport(): void
{
    // Backup, reset de tabla, inicialización...
}

// Se ejecuta UNA vez al completar exitosamente (NO se llama si fue cancelada o falló)
public function afterImport(): void
{
    // Notificaciones, recálculo de agregados, limpieza de caché...
}

Clase de Filtro Personalizada

namespace App\Filters;

class DateFilter
{
    public function __invoke(mixed $value): ?string
    {
        if (empty($value)) return null;

        try {
            return \Carbon\Carbon::parse($value)->toDateString();
        } catch (\Exception) {
            return null;
        }
    }
}

Créditos

Dante Piazza Quiroga · Clousis

Licencia

La Licencia MIT (MIT). Consulte el archivo de licencia para más información.