2 de julio de 2026

Cómo exportar archivos Excel en Laravel 13: guía completa con Laravel Excel

Foto de Marco Orta Marco Orta | 11 min de lectura
Compartir
Datos de una base de datos Laravel siendo exportados a una hoja de cálculo Excel

Generar un Excel descargable es uno de los requisitos más habituales en cualquier panel de administración: exportar el listado de pedidos, un reporte de ventas o la base de clientes para que el equipo lo abra en su hoja de cálculo. En Laravel, el estándar sigue siendo el paquete maatwebsite/excel (versión 3.1.69 a abril de 2026), el mismo que usamos para importar archivos Excel.

En esta guía actualizada para Laravel 13 vas a ver cómo crear una clase de exportación, añadir encabezados y estilos, controlar el formato de las celdas, exportar a distintos formatos (XLSX, CSV) y, lo más importante para producción, mover exportaciones pesadas a cola para no reventar la memoria del servidor.

1. Tener Laravel 13 instalado

Si todavía no tienes el proyecto listo, revisa la guía dedicada: Cómo instalar Laravel 13. Recuerda que Laravel 13 requiere PHP 8.3 como mínimo (8.4 en la práctica desde Laravel 13.3).

2. Instalación del paquete maatwebsite/excel

Instala el paquete vía Composer:

composer require maatwebsite/excel

El paquete registra automáticamente su service provider y el facade Excel. Si quieres ajustar la configuración por defecto (formato de fechas, disco de almacenamiento, etc.), publica el archivo de config:

php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider" --tag=config

Esto crea config/excel.php, donde puedes tocar cosas como el csv delimiter o el disco por defecto.

3. Creación de la clase de exportación

Toda la lógica de exportación vive en una clase dedicada dentro de app/Exports. Genérala con Artisan asociándola a un modelo:

php artisan make:export PedidosExport --model=Pedido

Esto crea app/Exports/PedidosExport.php con una clase que implementa el concern FromCollection:

<?php

namespace App\Exports;

use App\Models\Pedido;
use Maatwebsite\Excel\Concerns\FromCollection;

class PedidosExport implements FromCollection
{
    public function collection()
    {
        return Pedido::all();
    }
}

El método collection() devuelve los datos que se volcarán en el archivo. Ya con esto tienes un Excel funcional, pero le faltan encabezados y formato — lo vamos a mejorar con los concerns de las siguientes secciones.

4. Las fuentes de datos: FromCollection, FromQuery, FromView

maatwebsite/excel te deja elegir de dónde salen los datos según el caso. Estas son las cuatro opciones principales:

  • FromCollection — devuelves una colección de Eloquent o un array. Sencillo, pero carga todo en memoria. Ideal para volúmenes pequeños.
  • FromQuery — devuelves una query sin ejecutar y el paquete la procesa en chunks automáticamente. La opción recomendada para tablas grandes.
  • FromView — renderizas una vista Blade con una <table> HTML. Perfecto cuando necesitas control total del layout o estilos con HTML.
  • FromArray — para datos que ya tienes en un array plano.

Ejemplo con FromQuery, mucho más eficiente en memoria:

<?php

namespace App\Exports;

use App\Models\Pedido;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\Exportable;

class PedidosExport implements FromQuery
{
    use Exportable;

    public function query()
    {
        return Pedido::query()->where('estado', 'completado');
    }
}

5. Encabezados y mapeo de columnas

Un Excel sin encabezados no sirve de mucho. Añade el concern WithHeadings para la fila de títulos y WithMapping para decidir exactamente qué columnas y en qué formato se exporta cada fila:

<?php

namespace App\Exports;

use App\Models\Pedido;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;

class PedidosExport implements FromQuery, WithHeadings, WithMapping
{
    public function query()
    {
        return Pedido::query()->with('cliente');
    }

    public function headings(): array
    {
        return ['ID', 'Cliente', 'Total', 'Estado', 'Fecha'];
    }

    public function map($pedido): array
    {
        return [
            $pedido->id,
            $pedido->cliente->nombre,
            number_format($pedido->total, 2),
            ucfirst($pedido->estado),
            $pedido->created_at->format('d/m/Y'),
        ];
    }
}

Fíjate en el with('cliente'): cargar la relación por adelantado evita el problema N+1 cuando exportas miles de filas. El método map() es también el sitio ideal para formatear fechas y montos como los quiere ver el usuario final.

6. Estilos y formato de celdas

Para que el reporte se vea profesional, aplica ancho automático, negrita en los encabezados y formato numérico correcto:

use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithStyles;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;

class PedidosExport implements FromQuery, WithHeadings, WithMapping, ShouldAutoSize, WithStyles, WithColumnFormatting
{
    // ...query(), headings() y map() como antes...

    public function styles(Worksheet $sheet)
    {
        return [
            1 => ['font' => ['bold' => true]], // encabezados en negrita
        ];
    }

    public function columnFormats(): array
    {
        return [
            'C' => NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1, // columna Total
        ];
    }
}
  • ShouldAutoSize ajusta el ancho de las columnas al contenido.
  • WithStyles te da acceso al objeto Worksheet de PhpSpreadsheet para estilos avanzados (colores, bordes, alineación).
  • WithColumnFormatting aplica formatos de número, moneda o fecha por columna.

7. Descargar y almacenar el archivo

Con la clase lista, dispararla desde un controlador es trivial. El facade Excel ofrece dos operaciones principales:

<?php

namespace App\Http\Controllers;

use App\Exports\PedidosExport;
use Maatwebsite\Excel\Facades\Excel;

class PedidoController extends Controller
{
    // Descarga directa al navegador
    public function export()
    {
        return Excel::download(new PedidosExport, 'pedidos.xlsx');
    }

    // Guardar en un disco de Laravel (local, s3, etc.)
    public function store()
    {
        Excel::store(new PedidosExport, 'reportes/pedidos.xlsx', 's3');

        return back()->with('status', 'Reporte generado');
    }
}

Y la ruta correspondiente:

use App\Http\Controllers\PedidoController;

Route::get('/pedidos/export', [PedidoController::class, 'export'])->name('pedidos.export');

Excel::download() fuerza la descarga en el navegador; Excel::store() escribe el archivo en cualquier disco configurado en config/filesystems.php — útil para reportes programados o para enviarlos por email después.

8. Exportar a diferentes formatos (XLSX, CSV)

El mismo export sirve para varios formatos: solo indica la extensión y el tipo de escritor como tercer argumento:

use Maatwebsite\Excel\Excel as ExcelWriter;

// CSV
Excel::download(new PedidosExport, 'pedidos.csv', ExcelWriter::CSV);

// XLSX (por defecto si no indicas nada)
Excel::download(new PedidosExport, 'pedidos.xlsx', ExcelWriter::XLSX);

Si lo que necesitas es una conversión rápida de datos JSON a CSV sin montar todo un export en Laravel, puedes usar mi conversor de JSON a CSV para pruebas puntuales.

9. Exportaciones grandes: procesamiento en cola

Aquí está la diferencia entre un tutorial y código de producción. Exportar 100 mil filas de forma síncrona agota la memoria y da timeout. La solución es doble: usar FromQuery (que ya procesa en chunks) e implementar ShouldQueue para mover el trabajo a un worker en segundo plano.

use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Illuminate\Contracts\Queue\ShouldQueue;

class PedidosExport implements FromQuery, WithHeadings, ShouldQueue
{
    // ...
}

Y lo lanzas a la cola con queue():

public function export()
{
    (new PedidosExport)->queue('reportes/pedidos.xlsx', 's3');

    return back()->with('status', 'El reporte se está generando; te avisaremos cuando esté listo.');
}

El paquete divide la exportación en jobs que se procesan de forma independiente, manteniendo el uso de memoria bajo control. Combínalo con colas de Laravel sobre Redis y notifica al usuario por email o broadcasting cuando el archivo esté listo en el disco.

10. Múltiples hojas en un mismo archivo

Para reportes complejos —por ejemplo, una hoja por mes o por sucursal— implementa WithMultipleSheets:

use Maatwebsite\Excel\Concerns\WithMultipleSheets;

class ReporteAnualExport implements WithMultipleSheets
{
    public function sheets(): array
    {
        $sheets = [];

        foreach (range(1, 12) as $mes) {
            $sheets[] = new PedidosPorMesSheet($mes);
        }

        return $sheets;
    }
}

Cada elemento del array es a su vez una clase de exportación (que puede implementar WithTitle para nombrar la pestaña). Así generas un único .xlsx con doce hojas de un solo download().

Conclusión

Exportar a Excel en Laravel 13 es directo con maatwebsite/excel: creas una clase de exportación, eliges la fuente de datos adecuada (FromQuery para volúmenes grandes), añades encabezados y estilos con los concerns, y disparas la descarga desde el controlador. La clave para producción es no exportar nunca de forma síncrona datasets grandes: FromQuery + ShouldQueue mantiene tu app estable con cualquier volumen.

Para seguir construyendo tu panel de administración:

Compartir

Buscar

Etiquetas

Tutorial Laravel PHP Buenas Prácticas Desarrollo Web JavaScript IA Laravel 13 Seguridad SEO Expresiones Regulares Manipulación de Texto Frontend OpenAI VS Code