Importar columnas calculadas en Laravel Excel

Spread the love

Por un momento imagina que tienes este sencillo archivo de Excel que usa formulas y que quieres importar a un modelo en Laravel.

Archivo de excel con columna calculada
Figura 1

Digamos que haces una prueba usando Laravel-excel.

Excel::import(new AdditionImport, base_path('tests/Files/import-formulas.xlsx'));

Y resulta que en lugar de importar el archivo recibes un error como el siguiente…

PDOException: SQLSTATE[HY000]: General error: 1366 Incorrect integer value: ‘=A1+B1’ for column ‘total’ at row 1

Error con columnas calculadas

Si observas el error, las columnas que tienen formulas no se están procesando!

Esto se debe a que esta característica esta deshabilitada por defecto, por motivos de rendimiento.

Para resolver este sencillo problema solo tienes que decirle al paquete maatwebsite/excel que use formulas mientras realiza el import de datos, esto lo logras agregando el contrato WithCalculatedFormulas a la clase que va importar el archivo de excel.

Para que sea mas claro esto, que te parece si lo vemos usando el ejemplo.

Crear modelo

Este paso es muy sencillo solo tenemos que ejecutar el comando make:model

$php artisan make:model Addition -m

El -m va a crear una migración que solo tenemos que modificar.

Editar migración

La migración que fue creada, la encontramos en directorio database/migrations, solo tienes que editar el archivo como sigue.

    public function up()
    {
        Schema::create('additions', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->integer('addend_1');
            $table->integer('addend_2');
            $table->integer('total');
            $table->timestamps();
        });
    }

Lo único que hace nuestra migración es crear una tabla con tres columnas, que nos van a servir para almacenar los valores que proviene de nuestro archivo Excel de ejemplo.

Ejecutar migración

No olvides ejecutar el comando migrate!

$ php artisan migrate

Crear clase para importar

Esta parte también es muy sencilla con Laravel-excel, solo ejecutamos el comando make:import.

$php artisan make:import AdditionImport --model=Addition

Este comando va a crear la clase AdditionImport en el directorio app/Imports/AdditionImport.php. vamos a editarlo para que pueda importar los datos del archivo de Excel a nuestro modelo Addition.

namespace App\Imports;

use App\Addition;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithCalculatedFormulas;

class AdditionImport implements ToModel, WithCalculatedFormulas
{
    /**
    * @param array $row
    *
    * @return \Illuminate\Database\Eloquent\Model|null
    */
    public function model(array $row)
    {
        return new Addition([
            'addend_1' => $row[0],
            'addend_2' => $row[1],
            'total' => $row[2],
        ]);
    }
}

Es importante que no se te olvide agregar el contrato WithCalculatedFormulas de esta forma le dices a Laravel Excel que requieres usar formulas durante la importación.

Probar la clase AdditionImport

Bien, ahora ya puedes probar la clase en un controller o una ruta, para este caso voy a utilizar una ruta con Closure.

Route::get('import', function () {
    Excel::import(
        new AdditionImport,
        base_path('tests/Files/import-formulas.xlsx')
    );

    return response()->json(['message' => 'file import complete']);
});

Y eso es todo.

Tests

Te dejo la prueba que utilice para comprobar esta característica de Laravel Excel.

    /**
     * @test
     * @dataProvider casesDataProvider
     */
    public function it_imports_an_excel_file_with_calculation_by_formulas($op1, $op2, $total): void
    {
        Excel::import(new AdditionImport, base_path('tests/Files/import-formulas.xlsx'));

        $this->assertDatabaseHas('additions',[
            'addend_1' => $op1,
            'addend_2' => $op2,
            'total' => $total,
        ]);
    }

    public function casesDataProvider(): array
    {
        return [
            [1,2,3],
            [1,3,4],
            [2,4,6],
            [3,3,6]
        ];
    }

Lo único que hace la prueba es comprobar que cada caso en casesDataProvider se encuentre en la base de datos después de hacer la importación de los datos.

La prueba de integración quedo como sigue.

    /** @test */
    public function it_imports_an_excel_file():void
    {
        $response = $this->get('import');
        $response->assertOk();
        $response->assertJson(['message' => 'file import complete']);
    }

Si te gusto este articulo compártelo en tus redes sociales.

Si tienes alguna duda o sugerencia, no olvides dejar tus comentarios.