ldc-crm/app/Traits/Controllers/ExportDatas.php

230 lines
7.4 KiB
PHP
Raw Normal View History

<?php
namespace App\Traits\Controllers;
2025-12-02 09:48:28 +01:00
use App\Enums\ExportFormat;
use App\Lib\Helpers\FileHelper;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;
use ZipArchive;
trait ExportDatas
{
/**
* Check if the export needs to be in a .zip file.
* .zip is needed for :
* - csv with any files
* - pdf with files other than pictures
*
2025-12-02 09:48:28 +01:00
* @param ExportFormat $format Requested format.
* @param \Illuminate\Database\Eloquent\Collection $models Collection of models to export.
* @param string $fileFieldName Name of the field containing the file.
* @return boolean
*/
public function needsZip(
2025-12-02 09:48:28 +01:00
ExportFormat $format,
\Illuminate\Database\Eloquent\Collection $models,
string $fileFieldName = 'file'
): bool {
2025-12-02 09:48:28 +01:00
if ($format->equals(ExportFormat::csv)) {
return $models->whereNotNull($fileFieldName)->pluck($fileFieldName)->isNotEmpty();
} elseif ($format->equals(ExportFormat::pdf)) {
return $models
->whereNotNull($fileFieldName)
->whereNotIn('file_extension', ['jpg', 'jpeg', 'png'])
->isNotEmpty();
}
return false;
}
/**
* Return a StreamedResponse with a zip file.
*
2025-12-02 09:48:28 +01:00
* @param ExportFormat $format
* @param \Illuminate\Database\Eloquent\Collection $models
* @param string $file
* @return \Symfony\Component\HttpFoundation\StreamedResponse
*/
public function returnExportZip(
2025-12-02 09:48:28 +01:00
ExportFormat $format,
\Illuminate\Database\Eloquent\Collection $models,
string $file
): \Symfony\Component\HttpFoundation\StreamedResponse {
2025-12-02 09:48:28 +01:00
return new StreamedResponse(function () use ($file, $models, $format) {
// Create zip.
$zip = new ZipArchive();
$zipName = $this->createExportZip($zip, $file, $format);
// Add attachments.
2025-12-02 09:48:28 +01:00
$this->addAttachmentsToZip($zip, $models, $format);
$zip->close();
readfile($zipName);
unlink($zipName);
}, 200, $this->getFileHeaders($format, true));
}
/**
* Create the zip folder for the export with the main file inside.
*
2025-12-02 09:48:28 +01:00
* @param ZipArchive $zip
* @param string $file
* @param ExportFormat $format
* @return string|false
*
* @throws \Exception If the zip opening failed.
*/
2025-12-02 09:48:28 +01:00
private function createExportZip(ZipArchive &$zip, string $file, ExportFormat $format): string|false
{
$zipName = tempnam(Storage::disk('private')->path('export'), 'zip');
if (!$zip->open($zipName, ZipArchive::OVERWRITE)) {
throw new \Exception(__('Couldn\'t open zip file.'));
};
$zip->addFromString($format->fileName(), $file);
return $zipName;
}
/**
* Add attachments to zip
*
* @param ZipArchive $zip
2025-12-02 09:48:28 +01:00
* @param \Illuminate\Database\Eloquent\Collection $models
* @param ExportFormat $format
* @param string $fileFieldName Name of the field containing the file.
* @return void
*/
private function addAttachmentsToZip(
ZipArchive &$zip,
2025-12-02 09:48:28 +01:00
\Illuminate\Database\Eloquent\Collection $models,
ExportFormat $format,
string $fileFieldName = 'file'
) {
2025-12-02 09:48:28 +01:00
$modelsWithFilesToZip = $models->whereNotNull($fileFieldName)
->when($format->equals(ExportFormat::pdf), function ($models) {
return $models->whereNotIn('file_extension', ['jpg', 'jpeg', 'png']);
});
2025-12-02 09:48:28 +01:00
foreach ($modelsWithFilesToZip as $model) {
$filePath = FileHelper::getPrivateFilePath($model->{$fileFieldName});
if (Storage::disk('private')->exists($filePath)) {
$zip->addFile(
Storage::disk('private')->path($filePath),
2025-12-02 09:48:28 +01:00
$model->export_file_name
);
}
}
}
/**
* Get file headers to use with StreamedResponse.
*
2025-12-02 09:48:28 +01:00
* @param ExportFormat $format
* @param boolean $isZip
* @return array
*/
2025-12-02 09:48:28 +01:00
public function getFileHeaders(ExportFormat $format, bool $isZip = false): array
{
if ($isZip) {
return [
'Content-Type' => 'application/zip',
'Content-Disposition' => 'attachment; filename="export.zip"',
];
};
return $format->fileHeaders();
}
/**
* Transform Array into CSV.
*
* @param array $lines
* @param string $delimiter
* @param string $enclosure
* @param string $escape_char
* @return string|false
*/
private function array2csv(
array $lines,
string $delimiter = ',',
string $enclosure = '"',
string $escape_char = "\\"
) {
$f = fopen('php://temp', 'r+');
// UTF-8 BOM.
$BOM = "\xEF\xBB\xBF";
fwrite($f, $BOM);
foreach ($lines as $item) {
fputcsv($f, $item, $delimiter, $enclosure, $escape_char);
}
rewind($f);
$csvContent = stream_get_contents($f);
fclose($f);
return $csvContent;
}
/**
2025-12-02 09:48:28 +01:00
* Generate CSV with given models.
*
* @param \Illuminate\Database\Eloquent\Collection $models
* @return string|false
*/
private function generateCsv(\Illuminate\Database\Eloquent\Collection $models)
{
// CSV Header.
$class = get_class($models->first());
$csvlines[] = $class::getCsvHeaders();
foreach ($models as $model) {
$csvlines[] = $model->getFormattedCsvLine();
}
return $this->array2csv($csvlines, ';');
}
/**
* Generate PDF with models datas.
*
* @param \Illuminate\Database\Eloquent\Collection $models
2025-12-02 09:48:28 +01:00
* @param string $fileFieldName
* @return \Barryvdh\DomPDF\PDF
*/
2025-12-02 09:48:28 +01:00
private function generatePdf(
\Illuminate\Database\Eloquent\Collection $models,
string $fileFieldName = 'file'
): \Barryvdh\DomPDF\PDF {
// Convert pictures to base64.
2025-12-02 09:48:28 +01:00
foreach ($models->whereIn('file_extension', ['jpg', 'jpeg', 'png']) as $model) {
$filePath = FileHelper::getPrivateFilePath($model->{$fileFieldName});
$model->fileBase64 = sprintf(
'data:%s;base64,%s',
File::mimeType(Storage::disk('private')->path($filePath)),
base64_encode(Storage::disk('private')->get($filePath))
);
}
// Create temp folder if it doesn't exists. Needed to allow base64 pictures to be displayed in pdf.
if (!Storage::disk('private')->exists('temp')) {
Storage::disk('private')->makeDirectory('temp');
}
$modelClassName = class_basename($models->first());
return Pdf::setOptions(['tempDir' => Storage::disk('private')->path('temp')])
->loadView(
sprintf(
'export.pdf',
str($modelClassName)->lower()->plural()
),
[
'models' => $models,
'modelName' => str($modelClassName)->lower()
]
);
}
}