Jeroen Moors

Jeroen Moors

independent operations manager

PHPWord - Templates with repeating rows

PHPWord is a great library if you want to manipulate Word documents from PHP.

One of the easiest ways to create a document is to use a existing document as a template and the setValue() function to replace tags inside this document.

Unfortunately currently one very useful thing is missing: dynamically adding rows to a table. I’m not a rock-star programmer, I know some of them in person, but I’m not. Still I wrote a quick hack to overcome this issue.

This is how it works:

  1. Create a template Word document, containing the tables you would like to use.
    For each table, add one row to use as a template with the tags you would like to replace. Your template would be something like this: 
    First name Last name ${first_name} ${last_name}
  2. Call the cloneRow($tag, $numberOfRows) function before setValue() to duplicate your template row. Provide any tag on that row as $tag and the required number of rows as $number.
    Calling cloneRow('first_name', 3) would transform the working copy of your template into something like this: 
    First name Last name ${first_name#1} ${last_name#1} ${first_name#2} ${last_name#2} ${first_name#3} ${last_name#3}
  3. Now call setValue($tag, $value) adding #rowNumber to the original tags.
    To finish the example:  $doc->setValue(‘first_name#1’, ‘Jeroen’); $doc->setValue(‘last_name#1’, ‘Moors’); $doc->setValue(‘first_name#2’, ‘John’); $doc->setValue(‘last_name#2’, ‘Doe’); $doc->setValue(‘first_name#3’, ‘Jane’); $doc->setValue(‘last_name#3’, ‘Roe’); Would result in: First name Last name Jeroen Moors John Doe Jane Roe

To get this thing working you’ll have to replace the Template.php file found in side the PHPWord directory by this one Template.php.
Warning: don’t copy-paste it from the block below, as this might result in syntax errors.

     * Clone a table row
     * @param mixed $search
     * @param mixed $numberOfClones
	public function cloneRow($search, $numberOfClones) {
        if(substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
            $search = '${'.$search.'}';

		$tagPos 	 = strpos($this->_documentXML, $search);
		$rowStartPos = strrpos($this->_documentXML, "<w:tr", ((strlen($this-="">_documentXML) - $tagPos) * -1));
		$rowEndPos   = strpos($this->_documentXML, "", $tagPos) + 7;

		$result = substr($this->_documentXML, 0, $rowStartPos);
		$xmlRow = substr($this->_documentXML, $rowStartPos, ($rowEndPos - $rowStartPos));
		for ($i = 1; $i <= $numberOfClones; $i++) {
			$result .= preg_replace('/${(.*?)}/','${\1#'.$i.'}', $xmlRow);
		$result .= substr($this->_documentXML, $rowEndPos);

		$this->_documentXML = $result;

12.09.2013 – Thanks to Robert Pecha a bug has been fixed, please download the latest version of Template.php

Any feedback is welcome!