Criando um Serviço de Exportação de Arquivos em XML, XLSX, CSV e PDF no Laravel 11
Neste artigo, vamos mostrar como criar um serviço de exportação de arquivos nos formatos XML, XLSX, CSV e PDF utilizando o framework Laravel. Utilizaremos as melhores práticas e pacotes mais atuais para garantir uma solução robusta e eficiente.
Estrutura do Projeto
Para manter nosso código organizado e flexível, usaremos o padrão Strategy. Este padrão nos permite definir uma família de algoritmos, encapsulá-los e torná-los intercambiáveis. Vamos começar criando a interface ExportStrategy
e suas implementações para cada formato de exportação.
Passo 1: Instalar Dependências
Vamos instalar os pacotes necessários para lidar com os diferentes formatos de exportação:
composer require maatwebsite/excel
composer require barryvdh/laravel-dompdf
composer require spatie/array-to-xml
Passo 2: Publicar o Arquivo de Configuração do Maatwebsite Excel
Primeiro, precisamos publicar o arquivo de configuração do pacote Maatwebsite Excel:
php artisan vendor:publish --provider="Maatwebsite\Excel\ExcelServiceProvider" --tag=config
Passo 3: Configurar o Delimitador CSV
Abra o arquivo config/excel.php
e configure o delimitador CSV para ponto e vírgula (;
):
config/excel.php
<?php
return [
'exports' => [
'csv' => [
'delimiter' => ';', // Configurando o delimitador para ponto e vírgula
'enclosure' => '"',
'line_ending' => "\r\n",
'use_bom' => true,
'include_separator_line' => false,
'excel_compatibility' => false,
'output_encoding' => '',
'test_auto_detect' => true,
],
// Outras configurações...
],
// Outras configurações...
];
Passo 4: Implementar a Interface ExportStrategy
Vamos criar a interface ExportStrategy
que define o método export
.
app/Services/Export/Contracts/ExportStrategy.php
<?php
namespace App\Services\Export\Contracts;
interface ExportStrategy
{
public function export(array $data, string $filename): string;
}
Passo 5: Implementar as Classes de Exportação
app/Services/Export/ExportToXlsx.php
<?php
namespace App\Services\Export;
use App\Exports\GenericExport;
use App\Services\Export\Contracts\ExportStrategy;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Facades\Excel;
class ExportToXlsx implements ExportStrategy
{
public function export(array $data, string $filename = 'export.xlsx'): string
{
Excel::store(new GenericExport($data), $filename, 'public', \Maatwebsite\Excel\Excel::XLSX);
return Storage::url($filename);
}
}
app/Services/Export/ExportToCsv.php
<?php
namespace App\Services\Export;
use App\Exports\GenericExport;
use App\Services\Export\Contracts\ExportStrategy;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Facades\Excel;
class ExportToCsv implements ExportStrategy
{
public function export(array $data, string $filename = 'export.csv'): string
{
Excel::store(new GenericExport($data), $filename, 'public', \Maatwebsite\Excel\Excel::CSV);
return Storage::url($filename);
}
}
app/Services/Export/ExportToPdf.php
<?php
namespace App\Services\Export;
use App\Services\Export\Contracts\ExportStrategy;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Support\Facades\Storage;
class ExportToPdf implements ExportStrategy
{
public function export(array $data, string $filename = 'export.pdf'): string
{
$pdf = Pdf::loadView('exports.pdf', compact('data'));
Storage::put("public/{$filename}", $pdf->output());
return Storage::url($filename);
}
}
app/Services/Export/ExportToXml.php
<?php
namespace App\Services\Export;
use App\Services\Export\Contracts\ExportStrategy;
use Illuminate\Support\Facades\Storage;
use Spatie\ArrayToXml\ArrayToXml;
class ExportToXml implements ExportStrategy
{
public function export(array $data, string $filename = 'export.xml'): string
{
$xml = ArrayToXml::convert(['item' => $data]);
if (! Storage::put("public/{$filename}", $xml)) {
throw new \Exception("Failed to save XML file");
}
return Storage::url($filename);
}
}
Passo 6: Criar a Classe de Exportação Genérica
Vamos criar uma classe genérica que implementa a interface FromArray
do pacote Maatwebsite Excel.
app/Exports/GenericExport.php
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\{ FromArray, WithHeadings };
class GenericExport implements FromArray, WithHeadings
{
public function __construct(protected array $data, protected array $headers = [])
{
}
public function headings(): array
{
return $this->headers;
}
public function array(): array
{
return $this->data;
}
}
Passo 7: Criar o Service Provider Personalizado
Vamos criar um Service Provider para registrar nossas estratégias de exportação.
app/Providers/ExportServiceProvider.php
<?php
namespace App\Providers;
use App\Services\Export\Contracts\ExportStrategy;
use Illuminate\Support\ServiceProvider;
use App\Services\Export\{
ExportToCsv,
ExportToPdf,
ExportToXlsx,
ExportToXml
};
class ExportServiceProvider extends ServiceProvider
{
public $bindings = [
ExportStrategy::class => ExportToCsv::class,
ExportStrategy::class => ExportToPdf::class,
ExportStrategy::class => ExportToXlsx::class,
ExportStrategy::class => ExportToXml::class,
];
public function register(): void
{
//
}
public function boot(): void
{
//
}
}
Passo 8: Registrar o Service Provider
Abra o arquivo bootstrap/providers.php
e adicione o ExportServiceProvider
à lista de providers.
bootstrap/providers.php
<?php
return [
// Outros providers
App\Providers\ExportServiceProvider::class,
];
Passo 9: Criar o Serviço de Exportação
app/Services/ExportService.php
<?php
namespace App\Services;
use App\Services\Export\Contracts\ExportStrategy;
class ExportService
{
public function __construct(protected ExportStrategy $strategy)
{
}
public function setStrategy(ExportStrategy $strategy)
{
$this->strategy = $strategy;
}
public function export(array $data, string $filename): string
{
if (!$this->strategy) {
throw new \Exception("Formato de exportação não definido");
}
return $this->strategy->export($data, $filename);
}
}
Passo 10: Criar o Controller
app/Http/Controllers/ExportController.php
<?php
namespace App\Http\Controllers;
use App\Services\ExportService;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
class ExportController extends Controller
{
public function __construct(protected ExportService $exportService)
{
}
public function export(Request $request)
{
$data = [
['Nome' => 'João', 'Idade' => 25, 'Email' => 'joao@example.com'],
['Nome' => 'Maria', 'Idade' => 30, 'Email' => 'maria@example.com'],
['Nome' => 'José', 'Idade' => 35, 'Email' => 'jose@example.com'],
];
$format = $request->input('format', 'xlsx');
$strategies = config('export.strategies');
try {
if (!array_key_exists($format, $strategies)) {
throw new \InvalidArgumentException('Formato não suportado', Response::HTTP_BAD_REQUEST);
}
$strategy = $strategies[$format];
$this->exportService->setStrategy(new $strategy());
$filename = sprintf('%s.%s', 'export', $format);
$url = $this->exportService->export($data, $filename);
return response()->json(['url' => $url]);
} catch (\InvalidArgumentException $e) {
Log::error('Invalid Argument Exception', [
'code' => $e->getCode(),
'message' => $e->getMessage(),
'trace' => $e->getTrace(),
]);
return response()->json(['error' => $e->getMessage()], $e->getCode());
} catch (\Throwable $e) {
Log::error('Internal Server Error', [
'code' => $e->getCode(),
'message' => $e->getMessage(),
'trace' => $e->getTrace(),
]);
return response()->json(['error' => 'Internal Server Error'], Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
}
Passo 11: Configurar a Rota
routes/web.php
<?php
use App\Http\Controllers\ExportController;
Route::get('export', [ExportController::class, 'export']);
Passo 12: Criar a View para PDF
resources/views/exports/pdf.blade.php
<!DOCTYPE html>
<html>
<head>
<title>Exportação PDF</title>
<style>
table {
width: 100%;
border-collapse: collapse;
}
table, th, td {
border: 1px solid black;
}
th, td {
padding: 8px;
text-align: left;
}
</style>
</head>
<body>
<h1>Export PDF</h1>
<table>
<thead>
<tr>
@if(isset($headers) && is_array($headers))
@foreach($headers as $header)
<th>{{ $header }}</th>
@endforeach
@else
@foreach(array_keys($data[0]) as $header)
<th>{{ $header }}</th>
@endforeach
@endif
</tr>
</thead>
<tbody>
@foreach($data as $row)
<tr>
@foreach($row as $cell)
<td>{{ $cell }}</td>
@endforeach
</tr>
@endforeach
</tbody>
</table>
</body>
</html>
Passo 13: Criar Link Simbólico para o Storage
Para garantir que os arquivos exportados sejam acessíveis publicamente, precisamos criar um link simbólico do diretório de storage público. Execute o comando abaixo no terminal:
php artisan storage:link
Este comando cria um link simbólico do diretório storage/app/public
para o diretório public/storage
, permitindo que os arquivos armazenados em storage/app/public
sejam acessíveis a partir da web.
Conclusão
Seguindo essas etapas, você criou um serviço de exportação de arquivos em diferentes formatos no Laravel utilizando o padrão Strategy. Esta abordagem permite adicionar novos formatos de exportação de forma fácil e organizada, mantendo o código limpo e sustentável. Com o arquivo de configuração config/excel.php
, configuramos o delimitador CSV para ponto e vírgula, centralizando a configuração e simplificando o código das classes de exportação. Além disso, criamos um link simbólico para garantir que os arquivos exportados sejam acessíveis publicamente. Você pode ver o código completo no GitHub em https://github.com/bholiveiradev/laravel-export-service