I have a button on a modal and when clicked I want to generate an Excel workbook based off of a xls template file and then prompt it for the user to download/open.
The button uses javascript to open a new blank window and call the following controller Action
public function actionGenTSXls($proj, $leg, $driver){
// https://phpspreadsheet.readthedocs.io/en/develop/topics/recipes/
if(!is_numeric($proj) || !is_numeric($leg) || !is_numeric($driver)){
echo 'Invalid entry.';
die();
}
//Get the Project Model
$model = Projects::find()
->where(['=', 'ProjNo', $proj])
->one();
if (!$model) {
echo "There was a problem with the submitted request. (1)";
die();
}
//Get the Project Leg Model
$modelLeg = ProjectsLegs::find()
->where(['=', 'ProjId', $model->ProjId])
->andWhere(['=', 'LegNo', $leg])
->andWhere(['=', 'DriverId', $driver])
->one();
if (!$modelLeg) {
echo "There was a problem with the submitted request. (2)";
die();
}
$timestamp = date('Ymd\THis'); // Date/Time stamp
$filename = 'TripSheet_'.$model->ProjNo.'_'.$modelLeg->LegId.'_'.$timestamp.'.xls';
if(YII_ENV_DEV){
$filepath = Yii::$app->basePath.'\web\files\excel\\';
}else{
$filepath = \Yii::getAlias('@webroot').'/files/excel/';
}
$file = $filepath . $filename;
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment;filename="'.$filename.'"');
header('Cache-Control: max-age=0');
$sFile = Yii::$app->basePath.DIRECTORY_SEPARATOR.'templates'.DIRECTORY_SEPARATOR.'TS_Tmplt.xlsx';
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load( $sFile );
$spreadsheet->getProperties()
->setCreator("TS")
->setLastModifiedBy(User::findOne(Yii::$app->user->getId())->Dispatcher)
->setTitle("TS ".$model->ProjNo.'_'.$modelLeg->LegId)
->setSubject("TS ".$model->ProjNo.'_'.$modelLeg->LegId)
->setDescription("TS ".$model->ProjNo.'_'.$modelLeg->LegId);
//working on something with the spreadsheet/worksheet
$sheet = $spreadsheet->getSheetByName('Sheet1');
//Date
$sheet->setCellValue('K3', date("n/j/Y"));
$sheet->getStyle('K3')
->getNumberFormat()
->setFormatCode('m/d/yy');
//Order
if(!is_null($model->ProjNo)){$sheet->setCellValue('K4', $model->ProjNo);}
//Dispatcher
if(!is_null($model->DispatcherId)){$sheet->setCellValue('J11', User::findOne($modelLeg->DispatcherId)->Dispatcher);}
//Company Name
if(!is_null($model->ClientId)){
$sheet->setCellValue('E4', Clients::findOne($model->ClientId)->Company);
$sheet->getStyle('E4')->getAlignment()->setShrinkToFit(true);
}
//Start Date
if(!is_null($modelLeg->StartDt)){
$date = \DateTime::createFromFormat('Y-m-d H:i:s', $modelLeg->StartDt);
$cellVal = $date->format("n/j/Y g:i A");
$sheet->setCellValue('E8', $cellVal);
}
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xls');
$writer->save('php://output');
}
Now, in a general sense, it works. It create the xls, the user is prompted to open/download. But it generates an error in my yii log.
[error][yii\web\HeadersAlreadySentException] exception âyii\web\HeadersAlreadySentExceptionâ with message 'Headers already sent âphpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php on line 252â
Iâve been turning in circles for quite some time and thought Iâd get some experts advice and learn. I am a novice when it come to this aspect, so if you can explain so I can learn it would be greatly appreciated.
Iâve read that it can have to do with output buffering and one solution is to use ob_start() at the beginning of my code, but that doesnât seem to work and then the page return gibberish, I no longer get a xls file.
I decided to simplify my code to see what the issue was, so I generated the xls file, so I could temporarily eliminate that code and even so, with only 7 lines of code, I get the same error!
public function actionDownload(){
$filename = "TS.xls";
$file = "C:\Users\Dev\Downloads\TS.xls";
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load( $file );
$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter( $spreadsheet, 'Xls');
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment;filename="'.$filename.'"');
$writer->save("php://output");
}