phpexcelphpexcelphpspreadsheetphpoffice

Adding multiple links to one cell using PHPSpreadsheet


I am trying to export some data gathered by webforms, and match it up with one or more files included for each webform. My chosen format is .xls not .xlsx for backwards compatibility.

I've learned here, and on the internet in general, that multiple links IS possible if we use shapes or images/thumbnails to add hyperlinks to, but I can't seem to make it work with PHPSpreadsheet and xls files.

So far I've managed to set hyperlink to individual cells, but I can't seem to make it work on drawings.

Working code for cells:

$coordinates = $sheet->getCellByColumnAndRow($column,$row)->getCoordinate(); // get coordinate like "A1", "B5" etc.
$sheet->setCellValueByColumnAndRow($column,$row,$cellValue); // set link text
$sheet->getStyle($coordinates)->getFont()->setUnderline('single'); // set underline like links have
$sheet->getStyle($coordinates)->getFont()->getColor()->setRGB('#0000FF'); // set default link color
$sheet->getCellByColumnAndRow($column,$row)->getHyperlink()->setUrl('http://www.test.com'); // setting url or local link

This works great, but in my spreadsheet I would like to have multiple links in one cell if there are more than one file sent in by a single user.

Attempt at making it work for drawings:

// create a new drawing object
$drawing = new Drawing();

// set properties
$drawing->setName('Testname');
$drawing->setDescription('Test description');
$drawing->setPath($url); // put your path and image here
$drawing->setCoordinates($coordinates);
$drawing->setOffsetX($width);
$drawing->setHeight($height);

//$drawing->getHyperlink()->setUrl('http://www.test.com'); // error: Call to a member function setUrl() on null
//$drawing->getHyperlink()->setTooltip('tooltip works?'); // error: Call to a member function setTooltip() on null

// Connect drawn image to the spreadsheet
$drawing->setWorksheet($sheet);

The images works great, and I can place multiple images in one cell, but when I try to add hyperlink to each image, PHPSpreadsheet fails me. Are there any other way, perhaps with shapes or other things that I haven't thought about that might do the trick?

And if adding hyperlinks to multiple shapes / images with hyperlinks inside one cell is impossible with standard PHPSpreadsheet, is there a way to force one or more excel functions into one cell, achieving the same thing somehow?


Solution

  • I found a workaround. I answered my own question for future reference and hopefully to help others. :)

    The solution was to add a new row for each extra link I needed, and merge all other cells vertically in the columns that was not the link column. This made it possible to seemingly make 2 or more cells inside one cell, not affecting the other columns. E.g. one result that needed 3 links for the file cell, would be taking up 3 rows in the spreadsheet, but all the other columns corresponding to that result would be merged individually vertically, making it look like one row with a file cell containing 3 cells.

    Because of the limitation on one link per cell, this is what needed to be done:

    require_once '/vendor/autoload.php';
    
    // set folder to unzip the corresponding files
    $filesFolder = 'Files';
    
    // Create new Spreadsheet object
    $spreadsheet = new Spreadsheet();
    
    // set active sheet
    $sheet = $spreadsheet->getActiveSheet();
    
    // get form results data prepped for my .xls file
    list($header, $allRows) = getResults(); // privat function setting 2x arrays with info gathered
    
    // set headers
    $sheet->fromArray([$header], NULL, 'A1');
    
    $fileFieldIndex = 3; // the column index of the files 
    $counter = 2; // Start below the headers
    foreach($allRows as $row => $innerArray){
      // Add some data
      $sheet->fromArray([$innerArray], NULL, 'A'.$counter);
      
      // fetching fileinfo
      $aFileInfo = getFileInfo(); // privat function setting 2x array with fileinfo corresponding to this specific result
      // loop through and add rows for each extra file link
      foreach($aFileInfo as $innerRow => $fileInfo) {
        if($innerRow>=1){
          // on second or more occurrence of files, add extra row
          $counter++; // update counter
          
          // Add one row starting at column 'A'
          $sheet->fromArray([$innerArray], NULL, 'A'.$counter);
          // set link text
          $sheet->setCellValueByColumnAndRow($fileFieldIndex,$counter,$fileInfo['filename']);
    
          // get coordinates (using only numbers to get the letter/number combination)
          $coordinates = $sheet->getCellByColumnAndRow($fileFieldIndex,$counter)->getCoordinate();
          // separate letter-column and number-row
          preg_match_all('/(\d)|(\w)/', $coordinates, $matches);
          $letterColumnFiles = implode($matches[2]);
    
          // loop through columns
          // Get the highest column letter referenced in the worksheet
          $highestColumn = $sheet->getHighestColumn();
          $prevRow = ($counter-1);
          // stop when you reach the highest column
          for ($col = 'A'; $col != $highestColumn; ++$col) {
              if($col != $letterColumnFiles){
                // merge cell with cell above if not the column with the files
                $topCell = $col.$prevRow;
                $sheet->getStyle($topCell)->getAlignment()->setVertical('top');
                $sheet->mergeCells("$topCell:$col$counter");
              }
          }
          // merge highest column too, we wouldn't want to forget this one
          $sheet->getStyle($highestColumn.$prevRow)->getAlignment()->setVertical('top');
          $sheet->mergeCells("$highestColumn$prevRow:$highestColumn$counter");
        }
        // get coordinate like "A1", "B5" etc. needed for getStyle
        $coordinates = $sheet->getCellByColumnAndRow($fileFieldIndex,$counter)->getCoordinate();
        // set underline like links have
        $sheet->getStyle($coordinates)->getFont()->setUnderline('single');
        // set default link color
        $sheet->getStyle($coordinates)->getFont()->getColor()->setRGB('#0000FF');
        // setting local link to specified local folder
        $sheet->getCellByColumnAndRow($fileFieldIndex,$counter)->getHyperlink()->setUrl($filesFolder.'\\'.$fileInfo['filename']);
      }
    }