CakePHP 4 で PhpSpreadsheet を使い xlsx ファイルからデータを配列で読み込む

はじめに

前回の「CakePHP 4 で PhpSpreadsheet のダウンロード機能をコンポーネントに実装」に引き続き、今日も CakePHP 4 で PhpSpreadsheet を使う話題です。

今日は xlsx ファイルをアップロードして、そこからデータを読み込む方法をご紹介します。

CakePHP 4 に PhpSpreadsheet を導入する方法は、過去記事「CakePHP 4 で PhpSpreadsheet を使う方法」を参考にしてください。

CakePHP
4.0.3
PhpSpreadsheet
1.10.1
PHP
7.3.11
OS
Win 10 Home
目次
  1. xlsx ファイルから配列形式でデータを取得
  2. xlsx から1セルごとに値を取得
  3. アップロードした xlsx からデータを配列で取得
  4. おわりに

1. xlsx ファイルから配列形式でデータを取得

まずは xlsx 読込部分のサンプルをご紹介します。下記は xlsx からデータを配列で取得するものです。動作確認の前に /webroot/sample.xlsx を作成してください。

src/Controller/SampleController.php
<?php
declare(strict_types=1);

namespace App\Controller;

use PhpOffice\PhpSpreadsheet\IOFactory;

class SampleController extends AppController
{
  public function index()
  {
    $file = WWW_ROOT . 'sample.xlsx';
    $spreadsheet = IOFactory::load($file);
    $sheet = $spreadsheet->getActiveSheet();
    debug($sheet->toArray());
    exit;
  }
}

上記コードは便宜上 webroot に sample.xlsx を配置していますが、それを推奨しているわけではありません。

webroot は公開ディレクトリなので、もし xlsx ファイルを一般公開したくない場合には、別なディレクトリに配置してください。

toArray() 関数を使えば簡単に配列として読み込むことができます。公式ドキュメントにはさらっと説明があります。

罫線、背景色、セル結合、SUM() などを含むエクセルファイルで試しましたが、各値を読むことができました。計算や SUM() などはその結果が取得されます。

ただ、セルの表示分類を「数値」にした際、末尾に半角スペースが付きました。

2. xlsx から1セルごとに値を取得

全体を一気に配列として取得するのではなく、1セルずつ値を取得したい場合は下記ように実装できます。

src/Controller/SampleController.php
<?php
declare(strict_types=1);

namespace App\Controller;

use PhpOffice\PhpSpreadsheet\IOFactory;

class SampleController extends AppController
{
  public function index()
  {
    $file = WWW_ROOT . 'sample.xlsx';
    $spreadsheet = IOFactory::load($file);
    $sheet = $spreadsheet->getActiveSheet();
    $rows = [];
    foreach ($sheet->getRowIterator() as $excelRow) {
      $cellIterator = $excelRow->getCellIterator();
      $cellIterator->setIterateOnlyExistingCells(FALSE);
      $row = [];
      foreach ($cellIterator as $cell) {
          $row[] = $cell->getValue();
      }
      $rows[] = $row;
    }
    debug($rows);
    exit;
  }
}

上記の例だと配列に格納するだけなので toArray() と変わりませんが、セルの値に応じてデータを加工したい場面などで役立つと思います。

公式ドキュメントの下記を参考にしています。

3. アップロードした xlsx からデータを配列で取得

CakePHP 4 のファイルアップロード機能と組み合わせて、フォームから送信した xlsx ファイルから配列でデータを取得します。

CakePHP 4 でのファイルアップ方法ついては「CakePHP 4 でファイルアップロードを実装する方法」でも紹介しています。

templates/Sample/index.php
<form method="post" enctype="multipart/form-data">
  <div>
    <input type="file" name="my_file">
    <input type="hidden" name="_csrfToken" autocomplete="off" value="<?= $this->request->getAttribute('csrfToken') ?>">
    <button type="submit">アップロード</button>
  </div>
</form>
src/Controller/SampleController.php
<?php
declare(strict_types=1);

namespace App\Controller;

use PhpOffice\PhpSpreadsheet\IOFactory;

class SampleController extends AppController
{
  public function index()
  {
    if ($this->request->is('post')) {
      $myFile = $this->request->getData('my_file');
      $tmpFile = $myFile->getStream()->getMetadata('uri');

      $spreadsheet = IOFactory::load($tmpFile);
      $sheet = $spreadsheet->getActiveSheet();
      debug($sheet->toArray());
      exit;
    }
  }
}

特に説明はなくても大丈夫だと思いますが、getStream()->getMetadata('uri') で一時保存先のパスが取得できます。getMetadata() と引数なしで実行すると、uri 以外のデータも含めて配列で取得できます。

4. おわりに

個人的な話になりますが、データの入出力は csv ファイル より xlsx を使うことが多くなってきています。CSV だと文字コードの調整が面倒だったり、Office ソフトで開いたときに「0001」が「1」になってしまったりするためです。

PhpSpreadsheet を使えば、エクセルファイルの単純な読み書きなら、少ないコードで実装可能です。気になる方は是非トライしてみてください。