ria/app/Helpers/excel_helper.php
2025-04-10 13:02:26 +00:00

415 lines
15 KiB
PHP
Executable File

<?php
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Reader\Exception as SpreadsheetReaderException;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Borders;
if (!function_exists('generate_excel')) {
function generate_excel($headlines, $xldata)
{
$headersMain = $headlines['header1'];
$headersSub = $headlines['header2'];
$company = $headlines['company_name'];
$filename = $headlines['file_name'];
$type = $headlines['type'];
$footer = false;
$mergeEndCol = $headlines['merge_end_column'];
// $type = 1 means $data is single
// $type = 2 means $data is multiple array
// Create new Spreadsheet object
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// Set worksheet title
$sheet->setTitle('Sheet 1');
// A1 Start here... should be compnay name ....
$sheet->setCellValue('A1', $company);
// Get last column letter based on headers count
// $lastColumnLetter = getExcelColumnName(count($headers) - 1);
$colCount = count($headersMain);
// Calculate end column by character math (works up to 'ZZ')
$lastColumnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($colCount);
$sheet->mergeCells("A1:{$lastColumnLetter}1");
// Style A1
$sheet->getStyle("A1")->getFill()
->setFillType(Fill::FILL_SOLID)
->getStartColor()->setRGB('ADD8E6'); // Light blue
$sheet->getStyle("A1")->getAlignment()
->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER)
->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
$sheet->getStyle("A1")->getFont()->setBold(true)->setSize(14);
$sheet->getStyle("A1")->getFont()
->getColor()
->setRGB(Color::COLOR_WHITE);
// /. A1 ends here
$currentRow = 2; // Start at row 2
if($type == 1){
$startCell = 'A' . $currentRow;
$lastcolrow = callHeader($sheet, $headersMain, $startCell,$headersSub);
$lastcolrow2 = callSkipRow($lastcolrow);
$lastcolrow3 = callData($sheet,$headersMain,$xldata,$lastcolrow2,$mergeEndCol);
$lastcolrow4 = callSkipRow($lastcolrow3);
if($footer){
$lastcolrow5 = callFooter($sheet, $headersMain, $xldata, $lastcolrow4,'','',$mergeEndCol);
}
}else if($type == 2){
// $final_Footer=[];
// foreach($xldata as $i => $xl){
// echo $i;
// echo "<pre>";
// print_r($xl[$i]);
// echo "</pre>";
// // echo count($xl[$i])."**";
// // $forFinalFooter
// }
// foreach ($xldata as $i => $xl) {
// $forFinalFooter[] = $xl[$i];
// }
$final_Footer = isset($headlines['final_Footer']) && !empty($headlines['final_Footer']) ? $headlines['final_Footer'] : [];
foreach ($xldata as $index => $data) {
if (!empty($data)) {
$startCell = 'A' . $currentRow;
$lastcolrow = callHeader($sheet, $headersMain, $startCell,$headersSub);
$lastcolrow2 = callSkipRow($lastcolrow);
$lastcolrow3 = callData($sheet, $headersMain, $data, $lastcolrow2);
$lastcolrow4 = callSkipRow($lastcolrow3);
$lastcolrow5 = callFooter($sheet, $headersMain, $data, $lastcolrow4,'Total Amount Rs','228B22',$mergeEndCol);
$lastcolrow6 = callSkipRow($lastcolrow5);
// Update currentRow for next iteration
$currentRow = (int) filter_var($lastcolrow6, FILTER_SANITIZE_NUMBER_INT);
} else {
$startCell = 'A' . $currentRow;
$lastcolrow = callHeader($sheet, $headersMain, $startCell,$headersSub);
$lastcolrow2 = callSkipRow($lastcolrow);
$currentRow = (int) filter_var($lastcolrow2, FILTER_SANITIZE_NUMBER_INT);
}
}
if(!empty($final_Footer)){
$startCell = 'A' . $currentRow;
callFooter($sheet, $headersMain, $final_Footer[0], $startCell,'Final Net Salary Amount Rs','DC143C',$mergeEndCol);
// echo "cr".$currentRow;die;
}
// $final_Footer
}
// Create Excel writer
$writer = new Xlsx($spreadsheet);
$columnCount = count($headersMain);
for ($col = 1; $col <= $columnCount; $col++) {
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
$sheet->getColumnDimension($columnLetter)->setAutoSize(true);
}
$highestColumn = $sheet->getHighestColumn(); // e.g., 'E'
$highestRow = $sheet->getHighestRow(); // e.g., 10
// Build the full range (e.g., 'A1:E10')
$range = 'A1:' . $highestColumn . $highestRow;
// Apply border to that range
$sheet->getStyle($range)->applyFromArray([
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['argb' => 'FF000000'],
],
],
]);
try {
// Save Excel file to the specified path
ob_clean();
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header("Content-Disposition: attachment; filename=\"$filename\"");
header('Cache-Control: max-age=0');
$writer->save('php://output'); // stream to browser
exit; // Return true if file was successfully saved
} catch (\Exception $e) {
// Log or handle the exception
return false; // Return false if there was an error saving the file
}
}
}
function callHeader($sheet, $headersMain, $startCell, $headersSub = []) {
$rowNumber = (int) filter_var($startCell, FILTER_SANITIZE_NUMBER_INT);
$startCol = 'A';
$colCount = count($headersMain);
// Calculate end column by character math (works up to 'ZZ')
$endCol = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($colCount);
// Check if sub-header has any non-empty value
$hasSubHeader = !empty(array_filter($headersSub, function ($val) {
return trim($val) !== '';
}));
// 1. Write Main Header
$sheet->fromArray([$headersMain], null, $startCell);
$mainHeaderRange = "{$startCol}{$rowNumber}:{$endCol}{$rowNumber}";
// Style Main Header
$sheet->getStyle($mainHeaderRange)->applyFromArray([
'font' => [
'bold' => true,
'color' => ['rgb' => 'FF0000'],
'size' => 12,
],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
]
]);
if ($hasSubHeader) {
// 2. Write Sub Header
$subHeaderRow = $rowNumber + 1;
$sheet->fromArray([$headersSub], null, "A{$subHeaderRow}");
$subHeaderRange = "{$startCol}{$subHeaderRow}:{$endCol}{$subHeaderRow}";
$sheet->getStyle($subHeaderRange)->applyFromArray([
'font' => [
'bold' => true,
'color' => ['rgb' => 'FF0000'],
'size' => 12,
],
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER,
]
]);
return 'A' . ($subHeaderRow + 1);
} else {
return 'A' . ($rowNumber + 1);
}
}
function callData($sheet, $headers, $data, $startCell) {
$startRow = (int) filter_var($startCell, FILTER_SANITIZE_NUMBER_INT);
$startCol = 'A';
$sheet->fromArray($data, null, $startCell);
$dataRowCount = count($data);
$lastDataRow = $startRow + $dataRowCount;
// $dataColCount = count($headers);
// $lastDataColumn = getExcelColumnName($dataColCount - 1);
// $dataRange = "{$startCol}{$startRow}:{$lastDataColumn}{$lastDataRow}";
// Set font color to black
// $sheet->getStyle($dataRange)->getFont()->getColor()->setRGB('000000');
return 'A' . ($lastDataRow + 1); // return next row
}
function callSkipRow($startCell){
$row = (int) filter_var($startCell, FILTER_SANITIZE_NUMBER_INT);
return 'A' . ($row + 1); // just skip a line
}
// function callFooter($sheet, $headers, $data, $startCell,$mergeEndindex) {
// $row = (int) filter_var($startCell, FILTER_SANITIZE_NUMBER_INT);
// $startCol = preg_replace('/[0-9]/', '', $startCell);
// // Determine numeric columns dynamically
// // $numericIndexes = [];
// // foreach ($data as $index => $val) {
// // foreach ($data as $rowData) {
// // if (isset($rowData[$index]) && is_numeric($rowData[$index])) {
// // $numericIndexes[] = $index;
// // break;
// // }
// // }
// // }
// if (!empty($data)) {
// // Look at the first row
// $firstRow = $data[0];
// foreach ($firstRow as $key => $value) {
// foreach ($data as $row) {
// if (isset($row[$key]) && is_numeric($row[$key])) {
// $numericIndexes[] = $key; // use the key (could be column name)
// break;
// }
// }
// }
// }
// // Get first numeric column index for placing totals
// if (empty($numericIndexes)) return 'A' . ($row + 1); // Nothing to total
// sort($numericIndexes);
// $firstNumericIndex = $numericIndexes[0];
// $colCount = count($headers);
// // Calculate end column by character math (works up to 'ZZ')
// if($mergeEndindex == ''){
// $mergeEndCol = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($colCount - 1);
// }else{
// $mergeEndCol = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex((int)$mergeEndindex);
// }
// // echo gettype($row);die;
// // Merge from A to column before first numeric column (for "TOTAL" label)
// $sheet->mergeCells("A{$row}:{$mergeEndCol}{$row}");
// $sheet->setCellValue("A{$row}", 'TOTAL');
// // Style for TOTAL label
// $sheet->getStyle("A{$row}:{$mergeEndCol}{$row}")->applyFromArray([
// 'font' => [
// 'bold' => true,
// 'size' => 14,
// 'color' => ['rgb' => '228B22']
// ],
// 'alignment' => [
// 'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
// 'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER
// ]
// ]);
// // Total each numeric column and write to corresponding cell
// foreach ($numericIndexes as $index) {
// $total = 0;
// foreach ($data as $rowData) {
// $value = $rowData[$index] ?? 0;
// if (is_numeric($value)) {
// $total += $value;
// }
// }
// $colLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex((int)$index);
// $sheet->setCellValue("{$colLetter}{$row}", $total);
// }
// return 'A' . ($row + 1); // Return next available row
// }
function callFooter($sheet, $headers, $data, $startCell,$label,$code,$mergeEndIndex = null) {
$row = (int) filter_var($startCell, FILTER_SANITIZE_NUMBER_INT);
$startCol = preg_replace('/[0-9]/', '', $startCell);
$numericKeys = [];
$columnKeys = array_keys($data[0] ?? []);
$columnKeyToIndex = array_flip($columnKeys); // key => index
// Detect numeric columns
foreach ($columnKeys as $key) {
foreach ($data as $rowData) {
if (isset($rowData[$key]) && is_numeric($rowData[$key])) {
$numericKeys[] = $key;
break;
}
}
}
// If no numeric columns, skip total row
if (empty($numericKeys)) {
return $startCol . ($row + 1);
}
// Determine where to end the merge for the TOTAL label
if ($mergeEndIndex === null || $mergeEndIndex === '') {
$firstNumericKey = $numericKeys[0];
$firstNumericIndex = $columnKeyToIndex[$firstNumericKey];
$mergeEndIndex = $firstNumericIndex > 0 ? $firstNumericIndex - 1 : 0;
}
// Write totals for each numeric column
foreach ($numericKeys as $colKey) {
$total = 0;
foreach ($data as $rowData) {
$val = $rowData[$colKey] ?? 0;
if (is_numeric($val)) {
$total += $val;
}
}
$colIndex = $columnKeyToIndex[$colKey];
$colLetter = Coordinate::stringFromColumnIndex($colIndex + 1);
$sheet->setCellValue("{$colLetter}{$row}", $total);
$sheet->getStyle("{$colLetter}{$row}")->getNumberFormat()->setFormatCode('#,##0.00');
$sheet->getStyle("{$colLetter}{$row}")->applyFromArray([
'font' => [
'bold' => true,
'size' => 12
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
'vertical' => Alignment::VERTICAL_CENTER
]
]);
}
$mergeEndCol = Coordinate::stringFromColumnIndex((int)$mergeEndIndex + 1);
$mergeRange = "{$startCol}{$row}:{$mergeEndCol}{$row}";
// Merge cells and write $label name = "TOTAL"
$sheet->mergeCells($mergeRange);
$sheet->setCellValue("{$startCol}{$row}", $label);
// Style "TOTAL" label
$sheet->getStyle($mergeRange)->applyFromArray([
'font' => [
'bold' => true,
'size' => 14,
'color' => ['rgb' => $code]
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
'vertical' => Alignment::VERTICAL_CENTER
]
]);
// Return next row cell reference
return $startCol . ($row + 1);
}