$infos) { $data[]['Macros disponibles :'] = ($infos['type']=='list') ? '{{#'.$macro.'}}{{/'.$macro.'}} : '.$infos['desc'] : '{{'.$macro.'}} : '.$infos['desc']; } $stream = Excel::exportArray($data, null ,'Sans titre'); return $stream; } //Remplacement du tag d'image par l'image concernée //dans le fichier modèle public static function add_image($worksheet, $macro, $value, $cellCoord, $scale=2){ $value = substr($value,2); //On supprime le tag $cellVal = str_replace('{{'.$macro.'}}', '', $worksheet->getCell($cellCoord)->getValue()); $worksheet->getCell($cellCoord)->setValue($cellVal); preg_match_all('~[A-Z]+|\d+~', $cellCoord, $cellMatches); $cellMatches = reset($cellMatches); $cellIndex = $cellMatches[0]; $rowIndex = $cellMatches[1]; //On récupère les infos de l'image list($width, $height) = getimagesize($value); //On définit la taille de la cellule à celle de l'image $cellWidth = $worksheet->getColumnDimension($cellIndex); $rowHeight = $worksheet->getRowDimension($rowIndex); //La largeur d'une colonne est définie en unité (Microsoft). 1px correpond à peu près à 0.2 unité $cellWidth->setWidth(($width/$scale)*0.2); //0.75 correspond à 1px en pt, et la hauteur d'une ligne est définie en pt $rowHeight->setRowHeight(($height/$scale)*0.75); //On ajoute l'image dans la feuille $drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing(); $drawing->setName($macro); $drawing->setPath($value); $drawing->setResizeProportional(true); $drawing->setWidthAndHeight($width/$scale,$height/$scale); $drawing->setCoordinates($cellCoord); $drawing->setWorksheet($worksheet); } //Remplace les données d'un jeu de données en fonction //des tags rencontrés et fournis par le jeu de données public static function replace_data($worksheet, $data=array(), $cellCoord, $cellContent, $imgScale=2){ //Pour chaque élément de l'entité foreach ($data as $tag => $value) { if(strpos($cellContent, '{{'.$tag.'}}') !== false){ $cellVal = $worksheet->getCell($cellCoord)->getValue(); //Ajout des images if(substr($value,0,2)=='::'){ ExcelExport::add_image($worksheet, $tag, $value, $cellCoord, $imgScale); continue; } if(is_numeric($value) || preg_match("/\d+(?:\.\d{1,2})? [€,$,£,₽,¥]/",$value)) $worksheet->getStyle($cellCoord)->getNumberFormat()->setFormatCode(PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER); $cellVal = str_replace('{{'.$tag.'}}', $value, $cellVal); $worksheet->getCell($cellCoord)->setValue($cellVal); } } } //Récupère et gère la structure du remplacement //des données dans le fichier template fourni public static function from_template($source, $data, $return){ require(LIB_PATH.'PhpSpreadsheet'.SLASH.'vendor'.SLASH.'autoload.php'); $destination = File::dir().'tmp'.SLASH.'template.'.time().'-'.rand(0,100).'.xlsx'; $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load(File::dir().$source); $spreadsheet->getActiveSheet()->getPageMargins()->setTop(0.2); $spreadsheet->getActiveSheet()->getPageMargins()->setRight(0.2); $spreadsheet->getActiveSheet()->getPageMargins()->setLeft(0.2); $spreadsheet->getActiveSheet()->getPageMargins()->setBottom(0.2); $spreadsheet->getActiveSheet()->getPageMargins()->setFooter(0.5); $spreadsheet->getActiveSheet()->getPageMargins()->setHeader(0.5); $spreadsheet->getActiveSheet()->getPageSetup()->setHorizontalCentered(true); //Pour chaque feuille dans le classeur Excel foreach ($spreadsheet->getAllSheets() as $wrkSheetIdx => $worksheet) { //On récupère la zone de travail (pour ne pas se perdre dans des cellules vide) //Avec la colonne max et la ligne max $maxCol = 'A'; $maxRow = 0; foreach ($worksheet->getCoordinates() as $coord) { preg_match_all('~[A-Z]+|\d+~', $coord, $matches); $matches = reset($matches); $currCol = PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($matches[0]); $currRow = $matches[1]; if($maxCol < $currCol) $maxCol = $currCol; if($maxRow < $currRow) $maxRow = $currRow; } //On parcours une fois le contenu du la feuille //pour avoir les différents bords des boucles, si //il y en a dans le fichier. $rows = $worksheet->toArray('', true, true, true); $loopDatas = array('startLine' => 0,'endLine' => 0); $finalValues = array(); $loopStart = $loopEnd = false; foreach ($rows as $rowIdx => $cell) { foreach ($cell as $cellIdx => $content) { if(empty($content) && PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($cellIdx)>$maxCol) continue; foreach ($data as $macro => $value) { if(!is_array($value)) continue; $entityCount = count($value); if(strpos($content, '{{/'.$macro.'}}') !== false) { $loopDatas['endLine'] = $rowIdx; $loopDatas['endColumn'] = $cellIdx; $loopDatas['endCoord'] = $cellIdx.$rowIdx; $loopDatas['totalRow'] = $loopDatas['endLine'] - $loopDatas['startLine']; $loopDatas['totalCol'] = letters_to_numbers($loopDatas['endColumn']) - letters_to_numbers($loopDatas['startColumn']); $loopEnd = true; } if(!$loopEnd && strpos($content, '{{#'.$macro.'}}') !== false) { $loopDatas['startLine'] = $rowIdx; $loopDatas['startColumn'] = $cellIdx; $loopDatas['startCoord'] = $cellIdx.$rowIdx; $loopStart = true; } } } } //Définition type de boucle if($loopDatas['startLine'] != 0 && $loopDatas['endLine'] != 0) $loopDatas['loopType'] = ($loopDatas['startLine'] != $loopDatas['endLine']) ? 'vertical' : 'horizontal'; //Récupération des infos en fonction du type de boucle if(isset($loopDatas['loopType'])){ if($loopDatas['loopType'] == 'vertical'){ //On parcourt par ligne puis par colonne foreach ($rows as $rowIdx => $cell) { foreach ($cell as $cellIdx => $content) { if($rowIdx > $loopDatas['startLine'] && $rowIdx < $loopDatas['endLine']) $finalValues[$rowIdx][$cellIdx][] = $content; } } } else { //On parcourt par colonne puis par ligne for($col = $loopDatas['startColumn']; $col <= $loopDatas['endColumn']; ++$col){ if($col == $loopDatas['startColumn'] || $col == $loopDatas['endColumn']) continue; for($row = $loopDatas['startLine']; $row <= $maxRow; ++$row) { $content = $worksheet->getCell($col.$row)->getValue(); $finalValues[$col][$row][] = $content; } } } } //On remplace les données à l'intérieur //des boucles si des boucles sont présentes if(isset($finalValues) && !empty($finalValues)){ //Pour chaque value du jeu de données foreach ($data as $macro => $value) { if(!is_array($value)) continue; $worksheet->getCell($loopDatas['startCoord'])->setValue(''); $worksheet->getCell($loopDatas['endCoord'])->setValue(''); //Pour chaque entité foreach ($value as $i => $entity) { $rowIterator = $colIterator = 0; if($loopDatas['loopType'] == 'vertical'){ unset($finalValues[$loopDatas['endLine']]); //Pour chaque ligne foreach ($finalValues as $rIdx => $cell) { $lineIdx = ($loopDatas['startLine']+1)+(($loopDatas['totalRow']-1)*$i)+$rowIterator; //On ajoute 1 car on insère AVANT la ligne, et non APRÈS $rowWhereInsert = $i==0 ? $loopDatas['endLine']+1 : $rowWhereInsert += 1; $worksheet->insertNewRowBefore($rowWhereInsert, 1); //Pour chaque cellule foreach ($cell as $cIdx => $content) { $currCoord = $cIdx.$lineIdx; $lineRef = $lineIdx-($loopDatas['totalRow']-1); //Dans le cas où $i vaut 0, cas particulier, on remplace directement les données dans la feuille $referentCell = $i==0 ? $currCoord : $cIdx.$lineRef; $worksheet->setCellValue($currCoord, $content[0]); $worksheet->duplicateStyle($worksheet->getStyle($referentCell),$currCoord); //On remplace les données ExcelExport::replace_data($worksheet, $entity, $currCoord, $content[0], 4); } $rowIterator++; } } else { //Pour chaque colonne foreach ($finalValues as $cIdx => $col) { $colIdx = numbers_to_letters((letters_to_numbers($loopDatas['startColumn'])+1)+(($loopDatas['totalCol']-1)*$i)+$colIterator); //On ajoute 1 car on insère AVANT la ligne, et non APRÈS $colWhereInsert = $i==0 ? numbers_to_letters(letters_to_numbers($loopDatas['endColumn'])+1) : numbers_to_letters(letters_to_numbers($colWhereInsert)+1); $worksheet->insertNewColumnBefore($colWhereInsert, 1); //Pour chaque cellule foreach ($col as $rIdx => $content) { $currCoord = $colIdx.$rIdx; //Dans le cas où $i vaut 0, cas particulier, on remplace directement les données dans la feuille $referentCell = $i==0 ? $currCoord : $cIdx.$rIdx; $worksheet->setCellValue($currCoord, $content[0]); $worksheet->duplicateStyle($worksheet->getStyle($referentCell),$currCoord); //On remplace les données ExcelExport::replace_data($worksheet, $entity, $currCoord, $content[0], 4); } $colIterator++; } } } } } //On remplace le reste des tag présents //sur la feuille de calcul du fichier template foreach ($worksheet->getRowIterator() as $row) { $cellIterator = $row->getCellIterator(); foreach ($cellIterator as $cell) { $cellVal = $cell->getValue(); $cellIndex = $cell->getColumn(); if(empty($cellVal) && PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($cellIndex)>$maxCol) continue; foreach ($data as $macro => $value) { if(is_array($value)) continue; //Ajout des valeurs if(strpos($cellVal, '{{'.$macro.'}}') !== false){ //Ajout des images if(substr($value,0,2)=='::'){ ExcelExport::add_image($worksheet, $macro, $value, $cell->getCoordinate(), 2); continue; } if(is_numeric($value)) $worksheet->getStyle($cell->getCoordinate())->getNumberFormat()->setFormatCode(PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER_00); $cellVal = str_replace('{{'.$macro.'}}', $value, $cellVal); $cell->setValue($cellVal); } } } } } $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx'); $writer->save($destination); if($return!='stream') return $destination; $stream = file_get_contents($destination); unlink($destination); return $stream; } //Copie l'intégralité de la ligne depuis des //positions données à l'endroit voulu public static function copy_full_row(&$ws_from, &$ws_to, $row_from, $row_to) { $ws_to->getRowDimension($row_to)->setRowHeight($ws_from->getRowDimension($row_from)->getRowHeight()); $lastColumn = $ws_from->getHighestColumn(); ++$lastColumn; for($c = 'A'; $c != $lastColumn; ++$c) { $cell_from = $ws_from->getCell($c.$row_from); $cell_to = $ws_to->getCell($c.$row_to); $cell_to->setXfIndex($cell_from->getXfIndex()); // black magic here $cell_to->setValue($cell_from->getValue()); } } }