diff --git a/docs/templates-processing.rst b/docs/templates-processing.rst index af03b24586..a1b0bc4d83 100644 --- a/docs/templates-processing.rst +++ b/docs/templates-processing.rst @@ -23,3 +23,17 @@ multirow from a single row in a template by using ``TemplateProcessor::cloneRow` See ``Sample_23_TemplateBlock.php`` for example on how to clone a block of text using ``TemplateProcessor::cloneBlock`` and delete a block of text using ``TemplateProcessor::deleteBlock``. + +It is also possible to repeat a block with macros in the block (aka foreach). + +Example: + +.. code-block:: php + + $templateProcessor = new TemplateProcessor('Template.docx'); + $templateProcessor->repeatBlock('REPEATME', array( + array('FORENAME' => 'John', 'LASTNAME' => 'Donut'), + array('FORENAME' => 'Cat', 'LASTNAME' => 'Stefano') + )); + +See ``Sample_23_TemplateBlock.php`` for example on how to repeat a block. \ No newline at end of file diff --git a/samples/Sample_23_TemplateBlock.php b/samples/Sample_23_TemplateBlock.php index ed986618ba..434319ad90 100644 --- a/samples/Sample_23_TemplateBlock.php +++ b/samples/Sample_23_TemplateBlock.php @@ -11,6 +11,12 @@ // Everything between ${tag} and ${/tag}, will be deleted/erased. $templateProcessor->deleteBlock('DELETEME'); +// Everything between ${tag} and ${/tag}, will be repeated and macros within the block will be filled. +$templateProcessor->repeatBlock('REPEATME', array( + array('FORENAME' => 'John', 'LASTNAME' => 'Donut'), + array('FORENAME' => 'Cat', 'LASTNAME' => 'Stefano') +)); + echo date('H:i:s'), ' Saving the result document...', EOL; $templateProcessor->saveAs('results/Sample_23_TemplateBlock.docx'); diff --git a/samples/resources/Sample_23_TemplateBlock.docx b/samples/resources/Sample_23_TemplateBlock.docx index 049d5ca415..af6fb31400 100644 Binary files a/samples/resources/Sample_23_TemplateBlock.docx and b/samples/resources/Sample_23_TemplateBlock.docx differ diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 7a5eaf55bb..f86007f93e 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -310,6 +310,38 @@ public function cloneRow($search, $numberOfClones) $this->tempDocumentMainPart = $result; } + public function repeatBlock($blockname, $replacements) + { + + $xmlBlock = null; + preg_match( + '/(<\?xml.*)(\${' . $blockname . '}<\/w:.*?p>)(.*)()/is', + $this->tempDocumentMainPart, + $matches + ); + + if (isset($matches[3])) { + $xmlBlock = $matches[3]; + $cloned = array(); + foreach ($replacements as $replacementArray) { + $localXmlBlock = $xmlBlock; + foreach ($replacementArray as $search => $replacement) { + $localXmlBlock = $this->setValueForPart(self::ensureMacroCompleted($search), $replacement, $localXmlBlock, self::MAXIMUM_REPLACEMENTS_DEFAULT); + } + + $cloned[] = $localXmlBlock; + } + + $this->tempDocumentMainPart = str_replace( + $matches[2] . $matches[3] . $matches[4], + implode('', $cloned), + $this->tempDocumentMainPart + ); + } + + return $xmlBlock; + } + /** * Clone a block. * diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index ea7395610d..ea03a9c520 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -224,6 +224,40 @@ public function testCloneDeleteBlock() $this->assertTrue($docFound); } + public function testRepeatBlock() + { + $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/repeat-block.docx'); + + $this->assertEquals( + array('REPEATME', 'FORENAME', 'LASTNAME', '/REPEATME'), + $templateProcessor->getVariables() + ); + + $docName = 'repeat-block-result.docx'; + $templateProcessor->repeatBlock('REPEATME', array( + array('FORENAME' => 'John', 'LASTNAME' => 'Donut'), + array('FORENAME' => 'Cat', 'LASTNAME' => 'Stefano') + )); + $templateProcessor->saveAs($docName); + $docFound = file_exists($docName); + $this->assertTrue($docFound); + + $actualDocumentZip = new \ZipArchive(); + $actualDocumentZip->open($docName); + $actualMainPartXml = $actualDocumentZip->getFromName('word/document.xml'); + + if (false === $actualDocumentZip->close()) { + throw new \Exception("Could not close zip file \"{$docName}\"."); + } + + $this->assertRegexp('/John/', $actualMainPartXml); + $this->assertRegexp('/Cat/', $actualMainPartXml); + $this->assertRegexp('/Donut/', $actualMainPartXml); + $this->assertRegexp('/Stefano/', $actualMainPartXml); + + unlink($docName); + } + /** * @covers ::cloneBlock * @test diff --git a/tests/PhpWord/_files/templates/repeat-block.docx b/tests/PhpWord/_files/templates/repeat-block.docx new file mode 100644 index 0000000000..983863daf4 Binary files /dev/null and b/tests/PhpWord/_files/templates/repeat-block.docx differ