DEV Community

Cover image for Exportando dados em planilhas no Laravel e Livewire sem sujeira e sem raizes brancas
Salustiano Muniz
Salustiano Muniz

Posted on • Edited on

Exportando dados em planilhas no Laravel e Livewire sem sujeira e sem raizes brancas

Esses dias eu tive que criar "só um botãozinho" que exportasse os dados filtrados pelo usuário na página e exportasse para uma planilha para ser aberta no Excel. E depois de uma boa pesquisa, encontrei o pacote Laravel Excel, que facilita muito o trabalho. Neste artigo quero compartilhar minha experiência na implementação, pois apesar de o pacote ter documentação com exemplos, tive um pouco de trabalho pra juntar tudo e fazer funcionar da forma que eu precisava.

Antes de começar

O projeto que estou trabalhando usa Livewire rodando em Laravel 8 e PHP 8.0 e por isso os exemplos de código contidos no artigo usarão estas tecnologias, mas o pacote não depende do Livewire, podendo ser usado com Laravel a partir da versão 5.8, e com versões do PHP a partir da versão 7.2.

Configuração

Instalando o pacote

O primeiro passo é instalar o pacote através do composer.

composer require maatwebsite/excel
Enter fullscreen mode Exit fullscreen mode

Criando a classe de exportação

Com o pacote instalado, o próximo passo é criar a classe exportadora, que vai cuidar de configurar o arquivo a ser exportado. O comando que cria a classe de exportação aceita o parâmetro --model, usado para informar o Model de onde sairão as informações.

php artisan make:export TransactionExport --model=AccountTransaction
Enter fullscreen mode Exit fullscreen mode

A classe gerada pelo comando do artisan terá a seguinte estrutura:

 <?php

namespace App\Exports;  

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

class TransactionExport implements FromCollection  
{  
    public function collection()
    {
        return AccountTransaction::all();
    }

}
Enter fullscreen mode Exit fullscreen mode

Utilização

Passando dados para a exportação

Por padrão, a classe de exportação seleciona todos os registros do Model para exportar, mas é possível passar outra fonte de dados, que pode ser uma Collection ou um array através do método mágico __constructor da classe. No exemplo abaixo, é feito com uma collection

public function __construct(
    public Collection $transactions
) {
}

public function collection(): Collection
{
    return $this->transactions;
}
Enter fullscreen mode Exit fullscreen mode

Adicionando o cabeçalho

Apesar de neste ponto já ser possível baixar o arquivo, ele ainda não vai ter cabeçalho, mas o pacote permite adicionar cabeçalho ao arquivo utilizando a interface do pacote Laravel Excel WithHeadings à chamada da classe:

class TransactionExport implements FromCollection, WithHeadings
{
Enter fullscreen mode Exit fullscreen mode

Depois disso, precisamos passar os dados do cabeçalho no __constructor através de um array

public function __construct(
    public Collection $transactions,
    public array $headings  
) {  
}
Enter fullscreen mode Exit fullscreen mode

Eu optei por usar os nomes das colunas como estão no banco de dados no cabeçalho e porque poderia reaproveitar esta informação para selecionar as colunas no método de exportação

$columns  =  [
    'description',
    'financial_category_id',
    'transaction_status_id',
    'accrual_month',
    'due_date',
    'value',
    'document_number',
    'notes',
    'financial_account_id',
    'supplier_id',
    'cost_center_id',
    'payment_date',
    'final_value',
    'payment_method_id'
];
Enter fullscreen mode Exit fullscreen mode

Mas como estes nomes de colunas não seriam bons, criei entradas respectivas no arquivo de tradução resources\lang\pt_BR\dashboard.php:

return [
    ...
    'account_transaction' => [  
        'description' => 'Descrição',  
        'financial_category_id' => 'Categoria Financeira',  
        'transaction_status_id' => 'Status da Transação',  
        'accrual_month' => 'Mês de Vigência',  
        'due_date' => 'Data de Vencimento',  
        'value' => 'Valor',  
        'document_number' => 'Número do Documento',  
        'notes' => 'Observações',  
        'financial_account_id' => 'Conta Financeira',  
        'supplier_id' => 'Fornecedor',  
        'cost_center_id' => 'Centro de Custo',  
        'payment_date' => 'Data do Pagamento',  
        'final_value' => 'Valor Recebido',  
        'payment_method_id' => 'Forma de Pagamento'
    ]
];
Enter fullscreen mode Exit fullscreen mode

E usando o método headings da interface do pacote Laravel Excel WithHeadings, usei a função array_map para fazer com que o array de nomes do cabeçalho fosse traduzido:

public function headings(): array  
{ 
    return array_map(  
        fn($columnName) => __('dashboard.account_transaction.'.$columnName),  
        $this->headings  
    );  
}
Enter fullscreen mode Exit fullscreen mode

Personalizando a planilha

Nosso trabalho está quase completo, mas tanto os dados quanto a aparência da planilha podem ser melhor exibidos, e é isso que faremos a seguir.

Formatando dados

Os vêm do jeito que estão no banco de dados, ou seja, com os IDs das propriedades que têm relacionamento e as datas "cruas", mas podemos formatar estes dados com as interfaces do pacote Laravel Excel ColumnFormatting e WithMapping

class TransactionExport implements
    FromCollection,  
    WithColumnFormatting,  
    WithMapping,  
    WithHeadings
{
Enter fullscreen mode Exit fullscreen mode

E implementamos seus métodos. Começando pelo map, para ordenar e substituir os IDs pelos respectivos nomes das entidades relacionadas com as colunas:

use PhpOffice\PhpSpreadsheet\Shared\Date;

...

public function map($row): array  
{  
  return [  
  $row->description,  
  $row->financialCategory->name,  
  $row->transactionStatus->name,  
  Date::dateTimeToExcel($row->accrual_month),  
  Date::dateTimeToExcel($row->due_date),  
  $row->value,  
  $row->document_number,  
  $row->notes,  
  $row->financialAccount->name,  
  $row->supplier->name,  
  $row->costCenter?->name,  
  $row->payment_date ? Date::dateTimeToExcel($row->payment_date) : null,  
  $row->final_value,  
  $row->paymentMethod?->name,  
  ];  
}
Enter fullscreen mode Exit fullscreen mode

A classe PhpOffice\PhpSpreadsheet\Shared\Date é um "bônus" que o pacote Laravel Excel nos dá para facilitar a manipulação de dados. Como o pacote tem dependência com o PhpSpreadsheet, podemos usar métodos deste pacote.

O segundo passo para ajustar as linhas da planilha é resolvida pelo método columnFormats, que ficará assim:

use PhpOffice\PhpSpreadsheet\Style\NumberFormat;

...

public function columnFormats(): array  
{
    return [  
        'D' => 'mm/yyyy',  
        'E' => NumberFormat::FORMAT_DATE_DDMMYYYY,  
        'F' => NumberFormat::FORMAT_CURRENCY_USD_SIMPLE,  
        'L' => NumberFormat::FORMAT_DATE_DDMMYYYY,  
        'M' => NumberFormat::FORMAT_CURRENCY_USD_SIMPLE,  
    ];  
}
Enter fullscreen mode Exit fullscreen mode

Como é visto no código de exemplo, o array deve ser composto pela letra que corresponde à coluna que precisamos formatar e uma máscara com a regra de formatação. Aqui mais uma vez usamos um método do pacote PhpSpreadsheet. Neste caso, o PhpOffice\PhpSpreadsheet\Style\NumberFormat, que tem inúmeras máscaras para formatação, mas você pode usar uma personalizada, assim como eu fiz na primeira entrada do array retornado pelo método.

Estilizando o cabeçalho

Agora só falta destacar o cabeçalho para que nossa planilha fique pronta para ser exportada e lida. E o Laravel Excel tem uma Interface para nos ajudar: WithStyles:

class TransactionExport implements  
    FromCollection,  
    WithColumnFormatting,  
    WithMapping,  
    WithHeadings,  
    WithStyles
{
Enter fullscreen mode Exit fullscreen mode

Com a interface adicionada, hora de implementar o método styles:

use PhpOffice\PhpSpreadsheet\Style\Fill;

...

public function styles(Worksheet $sheet)  
{  
  $sheet->getStyle('A1:N1')
        ->getFill()  
        ->setFillType(Fill::FILL_SOLID)  
        ->getStartColor()  
        ->setRGB('51d2b7');  

  $sheet->getStyle('A1:N1')  
        ->getFont()  
        ->setBold(true)  
        ->getColor()  
        ->setRGB('ffffff');  
}
Enter fullscreen mode Exit fullscreen mode

Aqui, a primeira declaração é responsável por estilizar a cor de fundo do cabeçalho, e a segunda é responsável por formatar a fonte, deixando o cabeçalho com o fundo verde e a fonte em negrito e branca. Além destes atributos, também é possível mudar outros como borda, alinhamento, etc. Os métodos e parâmetros obedecem os padrões da documentação de formatação do PhpSpreadsheet

Pra arrematar, indico usar a interface ShouldAutoSize, que ajusta o tamanho das colunas automagicamente.

class TransactionExport implements  
    FromCollection,  
    WithColumnFormatting,  
    WithMapping,  
    WithHeadings,  
    WithStyles,  
    ShouldAutoSize  
{
Enter fullscreen mode Exit fullscreen mode

Classe de exportação

No final, a classe de exportação ficará assim:

<?php

namespace App\Exports;

use App\Models\AccountTransaction;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;

class TransactionExport implements
    FromCollection,
    WithColumnFormatting,
    WithMapping,
    WithHeadings,
    WithStyles,
    ShouldAutoSize
{
    public function __construct(
        public Collection $transactions, public array $headings
    ) {
    }

    public function collection()
    {
        return $this->transactions;
    }

    public function columnFormats(): array
    {
        return [
            'D' => 'mm/yyyy',
            'E' => NumberFormat::FORMAT_DATE_DDMMYYYY,
            'F' => NumberFormat::FORMAT_CURRENCY_USD_SIMPLE,
            'L' => NumberFormat::FORMAT_DATE_DDMMYYYY,
            'M' => NumberFormat::FORMAT_CURRENCY_USD_SIMPLE,
        ];
    }

    public function map($row): array
    {
        return [
            $row->description,
            $row->financialCategory->name,
            $row->transactionStatus->name,
            Date::dateTimeToExcel($row->accrual_month),
            Date::dateTimeToExcel($row->due_date),
            $row->value,
            $row->document_number,
            $row->notes,
            $row->financialAccount->name,
            $row->supplier->name,
            $row->costCenter?->name,
            $row->payment_date ? Date::dateTimeToExcel($row->payment_date) : null,
            $row->final_value,
            $row->paymentMethod?->name,
        ];
    }

    public function headings() : array
    {
        return array_map(
            fn($columnName) => __('dashboard.account_transaction.' . $columnName),
            $this->headings
        );
    }

    public function styles(Worksheet $sheet)
    {
        $sheet->getStyle('A1:N1')
            ->getFill()
            ->setFillType(Fill::FILL_SOLID)
            ->getStartColor()
            ->setRGB('51d2b7');

        $sheet->getStyle('A1:N1')
            ->getFont()
            ->setBold(true)
            ->getColor()
            ->setRGB('ffffff');
    }
}
Enter fullscreen mode Exit fullscreen mode

Criando o método de exportação

A partir daqui, nossa classe está pronta para receber, formatar e exportar nossos dados em uma planilha, que faremos através do Livewire com o método runQueryBuilder, o mesmo que busca os dados para exibir na página, com a diferença de que na chamada da página, ela é terminada com o método paginate para enviar dados paginados.


public function runQueryBuilder()  
{
    return $this->client  
        ->accountTransactions()  
        ->when(
            $this->transactionType,  
            fn($q) => $q->where('transaction_type', $this->transactionType)  
        )
        ->when(  
            $this->financialAccount,  
            fn($q) => $q->where('financial_account_id', $this->financialAccount)  
        )
        ->when(  
            $this->timePeriod,  
            fn($q) => $q->whereDate('due_date', '>=', $this->initialDueDate)
                        ->whereDate('due_date', '<=', $this->finalDueDate);
        )
        ->orderBy('due_date');  
}
Enter fullscreen mode Exit fullscreen mode

E finalmente o método de exportar, que recebe os dados e chama o método estático download da facade Maatwebsite\Excel\Facades\Excel para criar e baixar a planilha.

public function export(): BinaryFileDownload
{
    $columns = [  
        'description',  
        'financial_category_id',  
        'transaction_status_id',  
        'accrual_month',
        'due_date',  
        'value',  
        'document_number',  
        'notes',  
        'financial_account_id',  
        'supplier_id',  
        'cost_center_id',  
        'payment_date',  
        'final_value',  
        'payment_method_id'  
    ];  

    $data = $this->runQueryBuilder()->get($columns);  
    $filename = 'transactions_'
        . strtolower(TransactionTypeEnum::search($this->transactionType))
        .'_'. date('Ymd_his') .'.xls';

    return Excel::download(
        new TransactionExport($data, $columns),
        $filename
    );  
}
Enter fullscreen mode Exit fullscreen mode

Criando o botão de download

Com todo o código do backend criado, agora resta criar o botão que disparará a ação:

<button
    wire:click="export"
    wire:loading.attr="disabled"
    class="btn btn-primary btn-block text-uppercase">  
    <b>{{__('dashboard.Export')}}</b>
</button>
Enter fullscreen mode Exit fullscreen mode

Resultado

E finalmente temos nosso "só um botãozinho". Ao clicar no botão de exportar, o download será iniciado em instantes e você terá um arquivo em formato XLS, que ao ser aberto em um programa de edição de planilhas - no meu caso, usei o Google Spreadsheets - será exibido assim:
Imagem da planilha concluída com estilos e formatações aplicadas

Você também tem a opção de exportar o arquivo em outros formatos, mas lembre-se que alguuns deles não usa estilização, portanto parte deste trabalho será desnecessário.

E atenção, se liga aí que é hora da revisão

Na tele-aula de hoje, vimos que o pacote Laravel Excel pode ser usado para facilitar o processo de exportação de dados em forma de planilhas. Vimos também algumas formas de manipular estes dados e estilizar a planilha para melhorar sua leitura e apresentação. Por fim, implementamos tudo em um componente Livewire que nos permitiu fazer uso dos métodos desenvolvidos com um clique, baixamos o arquivo e visualizamos este em um editor de planilhas.

Este artigo foi uma forma de compartilhar minha experiência com o Laravel Excel, que me ajudou a facilitar um processo que tenho certeza que não sou só eu que vejo como complexo. A documentação do pacote tem várias outras opções de configuração de exportação de arquivos que podem ser exploradas, mas me concentrei em demonstrar as que usei. Recomendo a quem se interessar que dê uma lida e teste os exemplos.

Por hoje é isso. Nos vemos na próxima. Até lá e bons estudos.

Top comments (0)