A quick demo using PHPOffice/PhpSpreadsheet. 

https://github.com/PHPOffice/PhpSpreadsheet

https://phpspreadsheet.readthedocs.io/en/develop/

This does not require PHPExcel module that currently uses PHPOffice/PhpExcel as a manually included library, rather this includes the newer PHPOffice/PhpSpreadsheet package and directly invokes that package to generate the spreadsheet.

Composer based requirements

See composer documentation.

"require": {
"phpoffice/phpspreadsheet": "~1"
},

This will install the external library in the vendors directory and create the required autoloads. Obviously, install via composer!

composer require drupal/mymodule

Controller

This example creates a forced file download.

modules/custom/mymodule/src/Controller/MyController.php

namespace Drupal\mymodule\Controller;

use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Controller\ControllerBase;

class MyController extends ControllerBase {

  public function demo() {
    $response = new Response();
    $response->headers->set('Pragma', 'no-cache');
    $response->headers->set('Expires', '0');
    $response->headers->set('Content-Type', 'application/vnd.ms-excel');
    $response->headers->set('Content-Disposition', 'attachment; filename=demo.xlsx');

    // Generate and output code will be inserted here.

    $response->setContent($content);
    return $response;
  }

}

Spreadsheet generation

Misc demo content to populate a Spreadsheet and export this.

modules/custom/mymodule/src/Controller/MyController.php

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;

class MyController extends ControllerBase {

  public function demo() {
    $response = new Response();
    $response->headers->set('Pragma', 'no-cache');
    $response->headers->set('Expires', '0');
    $response->headers->set('Content-Type', 'application/vnd.ms-excel');
    $response->headers->set('Content-Disposition', 'attachment; filename=demo.xlsx');

    $spreadsheet = new Spreadsheet();

    //Set metadata.
    $spreadsheet->getProperties()
      ->setCreator('Test')
      ->setLastModifiedBy('Test')
      ->setTitle("PHPExcel Demo")
      ->setLastModifiedBy('Test')
      ->setDescription('A demo to show how to use PHPExcel to manipulate an Excel file')
      ->setSubject('PHP Excel manipulation')
      ->setKeywords('excel php office phpexcel lakers')
      ->setCategory('programming');

    // Get the active sheet.
    $spreadsheet->setActiveSheetIndex(0);
    $worksheet = $spreadsheet->getActiveSheet();

    //Rename sheet
    $worksheet->setTitle('My File name');

    /*
    * TITLE
    */
    //Set style Title
    $styleArrayTitle = array(
      'font' => array(
        'bold' => true,
        'color' => array('rgb' => '161617'),
        'size' => 12,
        'name' => 'Verdana'
      ));

    $worksheet->getCell('A1')->setValue('TEST PHPEXCEL');
    $worksheet->getStyle('A1')->applyFromArray($styleArrayTitle);

    /*
     * HEADER
     */
    //Set Background
    $worksheet->getStyle('A3:E3')
      ->getFill()
      ->setFillType(Fill::FILL_SOLID)
      ->getStartColor()
      ->setARGB('085efd');

    //Set style Head
    $styleArrayHead = array(
      'font' => array(
        'bold' => true,
        'color' => array('rgb' => 'ffffff'),
      ));

    $worksheet->getCell('A3')->setValue('C1');
    $worksheet->getCell('B3')->setValue('C2');
    $worksheet->getCell('C3')->setValue('C3');

    $worksheet->getStyle('A3:E3')->applyFromArray($styleArrayHead);

    for ($i = 4; $i < 10; $i++) {
      $worksheet->setCellValue('A' . $i, $i);
      $worksheet->setCellValue('B' . $i, 'Test C2');
      $worksheet->setCellValue('C' . $i, 'Test C3');
    }

    // This inserts the SUM() formula with some styling.
    $worksheet->setCellValue('A10', '=SUM(A4:A9)');
    $worksheet->getStyle('A10')
      ->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
    $worksheet->getStyle('A10')
      ->getBorders()->getTop()->setBorderStyle(Border::BORDER_THICK);

    // This inserts the formula as text.
    $worksheet->setCellValueExplicit(
      'A11',
      '=SUM(A4:A9)',
      DataType::TYPE_STRING
    );

    // Get the writer and export in memory.
    $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
    ob_start();
    $writer->save('php://output');
    $content = ob_get_clean();

    // Memory cleanup.
    $spreadsheet->disconnectWorksheets();
    unset($spreadsheet);


    $response->setContent($content);
    return $response;
  }
}

And don't forget to define your router yml to call the controller

modules/custom/mymodule/mymodule.routing.yml

mymodule.demo_excel:
  path: '/demo-excel'
  defaults:
    _controller: '\Drupal\mymodule\Controller\MyController::demo'
    _title: 'Demo Excel2007 Export'
  requirements:
    _permission: 'access content'

Comments

alan d.’s picture

Credit to rk.lubo for most of the demo spreadsheet code posted above ( found here ). I just converted this into the newer PHPOffice/PhpSpreadsheet format and extended it with a couple additional examples.


Alan Davison
jaypan’s picture

Nice little example.

There is a BinaryFileResponse as well, would that maybe be better than a Response? Or is there a reason that Response is better?

Contact me to contract me for D7 -> D10/11 migrations.

alan d.’s picture

Mostly just to avoid writing to a temp file :)

StreamedResponse works with BinaryFileResponse, but the content length is apparently not calculated.

For the project at hand, I'll probably end up with:

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

$response = new Response($content);
// Does any hard work with non-ASCII filenames.
$disposition = $response->headers->makeDisposition(
  ResponseHeaderBag::DISPOSITION_ATTACHMENT,
  'demo.xlsx'
);
$response->headers->set('Content-Disposition', $disposition);

// No caching.
$response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate');
// No caching with HTTP 1.0 clients.
$response->headers->set('Pragma', 'no-cache');
// Flag as already expired to enforce caching.
$response->headers->set('Expires', '0');
// File type and length.
$response->headers->set('Content-Length', strlen($content));
$response->headers->set('Content-Type', 'application/vnd.ms-excel');
return $response;

Alan Davison
dinesh_kesarkar’s picture

itsnadeem’s picture

we can import excel data into the content using phpspreadsheet

jshosseini’s picture

Is there any solution to manually install the library in drupal 8?

riopiedra’s picture

I copied and pasted the code of the example, and it was given error although everything was looking "normal". Finally the problem was in the following part:

    //Set metadata.
    $spreadsheet->getProperties()
      ->setCreator('Test')
      ->setLastModifiedBy('Test')
      ->setTitle("PHPExcel Demo")
      ->setLastModifiedBy('Test')
      ->setDescription('A demo to show how to use PHPExcel to manipulate an Excel file')
      ->setSubject('PHP Excel manipulation')
      ->setKeywords('excel php office phpexcel lakers')
      ->setCategory('programming');

Drupal was giving error apparently because my editor (Notepad++ in Windows) was introducing something else at the end of each row. So the solution was to write all this part in one row:

    //Set metadata.
    $spreadsheet->getProperties()->setCreator('Test')->setLastModifiedBy('Test')->setTitle("PHPExcel Demo")->setLastModifiedBy('Test')->setDescription('A demo to show how to use PHPExcel to manipulate an Excel file')->setSubject('PHP Excel manipulation')->setKeywords('excel php office phpexcel lakers')->setCategory('programming');

After that, the example worked as expected.

Thank you!

mmjvb’s picture

See https://stackoverflow.com/questions/8195839/choose-newline-character-in-...

or google how to change it:

Using Notepad++ to change end of line characters (CRLF to LF)

  1. Click on Search > Replace (or Ctrl + H)
  2. Find what: \r\n.
  3. Replace with: \n.
  4. Search Mode: select Extended.
  5. Replace All.
garima singh’s picture

how to remove empty rows in phpspreadsheet

dravenk’s picture

I had been releasing the phpexcel module beta1 version. The phpexcel module is now using services to process Excel files and It uses composer to automatically manage dependencies. E.g
Import data from an Excel file.

$phpexcel = \Drupal::service('phpexcel');
$filepath = 'modules/contrib/phpexcel/tests/Functional/data/phpexcel.test.multi_sheet.xlsx';
$result = $phpexcel->import($filepath);
print_r($result);

Export data as an Excel file.

$headers = ['Header 1', 'Header 2'];
$data = [
  ['Data 1.1', 'Data 1.2'],
  ['Data 2.1', 'Data 2.2'],
  ['Data 3.1', 'Data 3.2'],
];
$phpexcel = \Drupal::service('phpexcel');
$filepath = 'sites/default/files/test.xlsx';
$phpexcel->export($headers, $data, $filepath);