<?php namespace Home\Service; require_once 'vendor/autoload.php'; // \PhpOffice\PhpWord\TemplateProcessor /** * 自定義 克隆多行 * Class TemplateProcessor * @package Home\Service */ class TemplateProcessor extends \PhpOffice\PhpWord\TemplateProcessor { /** * 查找字符、字符串第n次出現的位置 * todo 自行添加 * @param string $str 原始字符串 * @param string $find 需要查找的字符、字符串 * @param int $start 開始搜索的位置 * @param int $num 第幾次出現的字符、字符串 * @return bool|int 返回第n次出現的位置 */ public function strPosNum($str = '', $find = '', $start = 0, $num = 1) { $pos = false; if ($num > 0) { $pos = strpos($str, $find, $start); for ($i = 1; $i < $num; $i++) { $pos = strpos($str, $find, $pos + 1); } } return $pos; } /** * 找到下面幾行的最后行尾的結束位置 * todo 自行添加 * @param $offset 開始搜索的位置 * @param int $rowNum 復制幾行 * @return bool|int */ public function findRowEndWithRowNum($offset, $rowNum = 0) { // </w:tr> 行尾標簽 return $this->strPosNum($this->tempDocumentMainPart, '</w:tr>', $offset, $rowNum) + 7; } /** * 克隆多行 * todo 自行添加 * 如果clone的第一行是合並單元格的 就clone變量選擇第一個合並單元格的 如下 * --------------------- * | | B | * | A |--------| * | | C | D | * --------------------- * @param $search 搜索 這個要選擇克隆多行的第一行的變量 * @param $numberOfClones 克隆的個數 * @param int $rowNum 克隆這行下面幾行(包括當前行) */ public function cloneMultiRow($search, $numberOfClones = 0, $rowNum = 1) { // 搜索的字符串替換成word中使用的變量 $search = static::ensureMacroCompleted($search); // 查找 當前變量 字符串首次出現的位置 $tagPos = strpos($this->tempDocumentMainPart, $search); if (!$tagPos) { throw new \PhpOffice\PhpWord\Exception\Exception('Can not clone row, template variable not found or variable contains markup.'); } // 找到行的開始位置 $rowStart = $this->findRowStart($tagPos); // 需要行尾的結束位置 $rowEnd = $this->findRowEndWithRowNum($tagPos, $rowNum); // 找到 行開始和行結束中間的 xml字符串 $xmlRow = $this->getSlice($rowStart, $rowEnd); // 碰到合並單元格的 // Check if there's a cell spanning multiple rows. if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) { // $extraRowStart = $rowEnd; $extraRowEnd = $rowEnd; while (true) { $extraRowStart = $this->findRowStart($extraRowEnd + 1); $extraRowEnd = $this->findRowEndWithRowNum($extraRowEnd + 1, $rowNum); // If extraRowEnd is lower then 7, there was no next row found. if ($extraRowEnd < 7) { break; } // If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row. $tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd); if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) && !preg_match('#<w:vMerge w:val="continue"\s*/>#', $tmpXmlRow)) { break; } // This row was a spanned row, update $rowEnd and search for the next row. $rowEnd = $extraRowEnd; } $xmlRow = $this->getSlice($rowStart, $rowEnd); } // 獲取 tempDocumentMainPart 中 開頭到 查找行開始位置的字符串 $result = $this->getSlice(0, $rowStart); // 獲取 clone變量 之后的xml 並拼接 $result .= implode($this->indexClonedVariables($numberOfClones, $xmlRow)); // 獲取 行結尾到 xml的結束的字符串 並拼接 $result .= $this->getSlice($rowEnd); $this->tempDocumentMainPart = $result; } }
使用
$templateFile = 'info.docx';
$templateProcessor = new TemplateProcessor($templateFile);
// 日志
$logList = $data['log_list']; $logListLen = count($logList);
// 重點:5,是循環的多行的行數 $templateProcessor->cloneMultiRow('id', $logListLen, 5);
$templateProcessor->saveAs("b.docx");
word文件中
老早寫的,只是給自己備個份,word其實就是xml文件,可以把word文件修改下后綴名為.zip,解壓可以看到xml文件;看PHPWord的源碼,也可以看到 模板替換主要是 使用的正則