phpdomphpword

Add nested table into word by phpword library and DOM object


I have a Full HTML text which needs to be converted accordingly to phpword and added finally in word. HTML code is :

$html = '<table style="height:500px;width:600px;"><thead><tr><th>Lor<strong>em</strong></th><th colspan="2"><p>ipsum</p><p>dolr</p></th></tr></thead><tbody><tr><td colspan="2"><table><tbody><tr><td>1</td><td>2</td><td>3</td></tr><tr><td>4</td><td>5</td><td>6</td></tr></tbody></table></td><td style="height:50px;padding:5px;text-align:center;vertical-align:top;">cell value</td></tr><tr><td>datetime</td><td>touch</td><td>inputtypes</td></tr><tr><td colspan="3">Merged third row</td></tr><tr><td>fourth row 1</td><td>fourth row 2</td><td>fourth row 3</td></tr><tr><td>fifth row 1</td><td>fifth row 2</td><td>fifth row 3</td></tr></tbody></table>';

here is my PHP code

$section = $phpWord->addSection();
processTableToWord($section, $html);

function processTableToWord($section, $table_data) {
  $table_style = new \PhpOffice\PhpWord\Style\Table;
  $table_style->setBorderColor('aaaaaa');
  $table_style->setBorderSize(1);
  $table_style->setUnit('auto');
  $table_style->setWidth('100px');
  $clm_width = countColumns($table_data);
  $table = $section->addTable($table_style);

  $dom = new \DOMDocument;
  $dom->loadHTML($table_data);
  $tbody = $dom->getElementsByTagName('tbody')->item(0);
  $thead = $dom->getElementsByTagName('thead')->item(0);
  $thead_rows = $thead->getElementsByTagName('tr');
  foreach ($thead_rows as $thead_row) {
    $thead_cells = $thead_row->getElementsByTagName('th');
    $table->addRow();
    foreach ($thead_cells as $thead_cell) {
      $thead_text = $thead_cell->nodeValue;
      // dump(htmlspecialchars($thead_text));
      $table->addCell($clm_width)->addText($thead_text, ['bold' => true], 'a_table');
    }
  }
  $rows = $tbody->getElementsByTagName('tr');
  foreach ($rows as $row) {
    $cells = $row->getElementsByTagName('td');
    $table->addRow();
    foreach ($cells as $cell) {
      // dump($dom->saveHTML($cell));
      $width = $cell->getAttribute('width');
      $height = $cell->getAttribute('height');
      $text = $cell->nodeValue;
      $style_arrs = [];
      if (!empty($cell->getAttribute('style'))) {
        $style_arrs = processStyleAttributes($cell->getAttribute('style'));
      }
      $colspan = $cell->getAttribute('colspan');
      
      if ($colspan > 1) {
        if (isset($style_arrs['vertical-align']) && !empty($style_arrs['vertical-align'])) {
          $table->addCell($colspan * $clm_width, ['gridSpan' => $colspan, 'valign' => $style_arrs['vertical-align']])->addText($text, [], 'a_table');
        }else {
          $table->addCell($colspan * $clm_width, ['gridSpan' => $colspan])->addText($text, [], 'a_table');
        }
      }else {
        if (isset($style_arrs['vertical-align']) && !empty($style_arrs['vertical-align'])) {
          $table->addCell($clm_width, ['valign' => $style_arrs['vertical-align']])->addText($text, [], 'a_table');
        }else {
          $table->addCell($clm_width)->addText($text, [], 'a_table');
        }
      }
    }
  }
}

Here is the output for this: enter image description here

I am actually doing the right thing for outer table but when comes to inner Table, Its rendering with outer table and breaking everything. Need someone help in fixing the code.

I got about $cells = $row->getElementsByTagName('td'); Here it is adding second table td's also into main one. So there should be something doing it here. Thank you


Solution

  • I am to achieve by multiple attempts by looping through self function

    function processTableToWord($section, $table_data, $nested = FALSE) {
      $table_style = new \PhpOffice\PhpWord\Style\Table;
      $table_style->setBorderColor('aaaaaa');
      $table_style->setBorderSize(1);
      $table_style->setUnit('auto');
      $clm_width = countColumns($table_data);
      $table = $section->addTable($table_style);
    
      $dom = new \DOMDocument;
      $dom->loadHTML($table_data);
      $tbody = $dom->getElementsByTagName('tbody')->item(0);
      $thead = $dom->getElementsByTagName('thead')->item(0);
      $thead_rows = !empty($thead) ? $thead->getElementsByTagName('tr') : [];
      foreach ($thead_rows as $thead_row) {
        $thead_cells = $thead_row->getElementsByTagName('th');
        $table->addRow();
        foreach ($thead_cells as $thead_cell) {
          $thead_text = $thead_cell->nodeValue;
          $table->addCell($clm_width)->addText($thead_text, ['bold' => true], 'a_table');
        }
      }
      $rows = $tbody->getElementsByTagName('tr');
      foreach ($rows as $row) {
        $cells = $row->getElementsByTagName('td');
        $table->addRow(1500);
        foreach ($cells as $cell) {
          $haveNestedTbl = FALSE;
          $nested_tbl = '';
          $nestedTable = $cell->getElementsByTagName('table')->item(0);
          if ($nestedTable) {
            $haveNestedTbl = TRUE;
            $nested_tbl = $dom->saveHTML($nestedTable);
            $cell->removeChild($nestedTable);
          }
          $width = $cell->hasAttribute('width') ? $cell->getAttribute('width') : '';
          $height = $cell->hasAttribute('height') ? $cell->getAttribute('height') : '';
          $text = $cell->nodeValue;
          $style_arrs = [];
          if ($cell->hasAttribute('style')) {
            $style_arrs = processStyleAttributes($cell->getAttribute('style'));
          }
          $colspan = $cell->getAttribute('colspan');
          if ($colspan > 1) {
            if (isset($style_arrs['vertical-align']) && !empty($style_arrs['vertical-align'])) {
              $innerCell = $table->addCell($colspan * $clm_width, ['gridSpan' => $colspan, 'valign' => $style_arrs['vertical-align']]);
              $innerCell->addText($text, [], 'a_table');
            }else {
              $innerCell = $table->addCell($colspan * $clm_width, ['gridSpan' => $colspan]);
              $innerCell->addText($text, [], 'a_table');
            }
          }else {
            if (isset($style_arrs['vertical-align']) && !empty($style_arrs['vertical-align'])) {
              $innerCell = $table->addCell($clm_width, ['valign' => $style_arrs['vertical-align']]);
              $innerCell->addText($text, [], 'a_table');
            }else {
              $innerCell = $table->addCell($clm_width);
              $innerCell->addText($text, [], 'a_table');
            }
          }
          if ($haveNestedTbl) {
            processTableToWord($innerCell, $nested_tbl);
          }
        }
      }
    }
    

    Here after assigining nested table to a variable, have to delete it from DOM to not further process it in another rows. Here is the screenshot of the output enter image description here