nhance/app/Helpers/ExcelMergeHelper.php
2024-12-23 09:20:17 +05:30

218 lines
9.6 KiB
PHP

<?php
namespace App\Helpers;
use Exception;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class ExcelMergeHelper
{
protected static $logger;
public static function initialize()
{
self::$logger = \Config\Services::mylogger();
}
public static function mergeExcelFiles(array $filesToMerge, string $outputPath)
{
try {
self::initialize();
if (empty($filesToMerge)) {
throw new Exception("No files provided for merging.");
}
// Validate files exist and are readable
foreach ($filesToMerge as $fileData) {
if (!file_exists($fileData['file_path'])) {
throw new Exception("File not found: " . $fileData['file_path']);
}
if (!is_readable($fileData['file_path'])) {
throw new Exception("File not readable: " . $fileData['file_path']);
}
}
self::$logger->logme('error',"Creating new spreadsheet...");
$mergedSpreadsheet = new Spreadsheet();
$firstSheet = true;
foreach ($filesToMerge as $fileIndex => $fileData) {
$filePath = $fileData['file_path'];
$sheetsToMerge = $fileData['sheets'];
self::$logger->logme('error',"Processing file $fileIndex: $filePath");
try {
self::$logger->logme('error',"Attempting to load file: $filePath");
$reader = IOFactory::createReaderForFile($filePath);
$reader->setReadDataOnly(true); // This can help with memory usage
self::$logger->logme('error',"Reader created successfully");
$currentSpreadsheet = $reader->load($filePath);
self::$logger->logme('error',"File loaded successfully");
$sheetCount = $currentSpreadsheet->getSheetCount();
self::$logger->logme('error',"File $filePath has $sheetCount sheets.");
if (empty($sheetsToMerge)) {
$sheetsToMerge = range(0, $sheetCount - 1);
}
foreach ($sheetsToMerge as $sourceSheetIndex) {
self::$logger->logme('error',"Processing sheet index: $sourceSheetIndex");
if ($sourceSheetIndex < 0 || $sourceSheetIndex >= $sheetCount) {
self::$logger->logme('error',"Invalid sheet index $sourceSheetIndex in file $filePath");
continue;
}
try {
$sourceSheet = $currentSpreadsheet->getSheet($sourceSheetIndex);
self::$logger->logme('error',"Source sheet loaded");
$baseName = $sourceSheet->getTitle();
self::$logger->logme('error',"Processing sheet: $baseName");
$newSheetName = $baseName;
$counter = 1;
while ($mergedSpreadsheet->sheetNameExists($newSheetName)) {
$newSheetName = $baseName . '_' . $fileIndex . '_' . $counter;
$counter++;
}
self::$logger->logme('error',"New sheet name will be: $newSheetName");
if ($firstSheet) {
$targetSheet = $mergedSpreadsheet->getActiveSheet();
$targetSheet->setTitle($newSheetName);
$firstSheet = false;
self::$logger->logme('error',"Using first sheet");
} else {
$targetSheet = new Worksheet($mergedSpreadsheet, $newSheetName);
$mergedSpreadsheet->addSheet($targetSheet);
self::$logger->logme('error',"Created new sheet");
}
self::$logger->logme('error',"Starting content copy for sheet: $newSheetName");
self::copyWorksheetContent($sourceSheet, $targetSheet);
self::$logger->logme('error',"Content copied successfully for sheet: $newSheetName");
} catch (Exception $e) {
self::$logger->logme('error',"Error processing sheet $sourceSheetIndex: " . $e->getMessage());
self::$logger->logme('error',"Error trace: " . $e->getTraceAsString());
continue;
}
}
// Free up memory
$currentSpreadsheet->disconnectWorksheets();
unset($currentSpreadsheet);
self::$logger->logme('error',"Cleaned up spreadsheet resources");
} catch (Exception $e) {
self::$logger->logme('error',"Error processing file $filePath: " . $e->getMessage());
self::$logger->logme('error',"Error trace: " . $e->getTraceAsString());
continue;
}
self::$logger->logme('error',"Current sheet names in merged spreadsheet:");
foreach ($mergedSpreadsheet->getSheetNames() as $name) {
self::$logger->logme('error'," - $name");
}
}
if ($mergedSpreadsheet->getSheetCount() === 0) {
throw new Exception("No sheets were successfully merged.");
}
self::$logger->logme('error',"Setting active sheet to index 0");
$mergedSpreadsheet->setActiveSheetIndex(0);
self::$logger->logme('error',"Preparing to save merged file to: $outputPath");
// Ensure output directory exists
$outputDir = dirname($outputPath);
if (!is_dir($outputDir)) {
mkdir($outputDir, 0777, true);
}
if (!is_writable($outputDir)) {
throw new Exception("Output directory is not writable: $outputDir");
}
if (ob_get_length()) {
ob_end_clean();
}
self::$logger->logme('error',"Creating Excel writer");
$writer = new Xlsx($mergedSpreadsheet);
self::$logger->logme('error',"Saving file...");
$writer->save($outputPath);
// Free up memory
$mergedSpreadsheet->disconnectWorksheets();
unset($mergedSpreadsheet);
self::$logger->logme('error',"Merge successful. File saved at: $outputPath");
return $outputPath;
} catch (Exception $e) {
self::$logger->logme('error',"Excel Merge Error: " . $e->getMessage());
self::$logger->logme('error',"Error trace: " . $e->getTraceAsString());
throw $e;
}
}
private static function copyWorksheetContent($sourceSheet, $targetSheet)
{
try {
self::$logger->logme('error',"Starting copyWorksheetContent");
// Get the highest row and column indexes
$highestRow = $sourceSheet->getHighestRow();
$highestColumn = $sourceSheet->getHighestColumn();
self::$logger->logme('error',"Copying data from range A1:{$highestColumn}{$highestRow}");
// Copy cell values only first for better performance
foreach ($sourceSheet->getRowIterator() as $row) {
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(true);
foreach ($cellIterator as $cell) {
$column = $cell->getColumn();
$rowIndex = $cell->getRow();
$targetSheet->setCellValue($column . $rowIndex, $cell->getValue());
}
}
self::$logger->logme('error',"Basic cell values copied");
// Copy column dimensions
foreach ($sourceSheet->getColumnDimensions() as $columnId => $sourceDim) {
$targetSheet->getColumnDimension($columnId)
->setWidth($sourceDim->getWidth())
->setVisible($sourceDim->getVisible())
->setAutoSize($sourceDim->getAutoSize());
}
self::$logger->logme('error',"Column dimensions copied");
// Copy row dimensions
foreach ($sourceSheet->getRowDimensions() as $rowId => $sourceDim) {
$targetSheet->getRowDimension($rowId)
->setRowHeight($sourceDim->getRowHeight())
->setVisible($sourceDim->getVisible());
}
self::$logger->logme('error',"Row dimensions copied");
// Copy basic worksheet properties
$targetSheet->setShowGridlines($sourceSheet->getShowGridlines());
$targetSheet->setShowRowColHeaders($sourceSheet->getShowRowColHeaders());
self::$logger->logme('error',"Worksheet properties copied");
} catch (Exception $e) {
self::$logger->logme('error',"Error in copyWorksheetContent: " . $e->getMessage());
self::$logger->logme('error',"Error trace: " . $e->getTraceAsString());
throw $e;
}
}
}