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
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
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();
}
}
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;
}
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
{
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
) {
}
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'
];
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'
]
];
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
);
}
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
{
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,
];
}
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,
];
}
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
{
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');
}
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
{
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');
}
}
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');
}
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
);
}
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>
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:
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)