PhpSpreadsheetの使い方

# はじめに
仕事で使ったときどう使ったかを解説します。
PhpSpreadsheetはPhpExcelの後継で、最近のPHPっぽくなってるらしいです。
https://github.com/PHPOffice/PhpSpreadsheet

今回は`xlsx`形式のファイルから情報を読みだして、`csv`形式のファイルに書き込みます。

# 使い方
## インストールと`require`
`composer require phpoffice/phpspreadsheet`をした後、普通に

```PHP
require_once "vendor/autoload.php";
```
で終わりです。

## 読む
### ファイルの読み込み
```PHP
$reader = new PhpOffice\PhpSpreadsheet\Reader\Xlsx();
$spreadsheet = $reader->load($file_name);
```

### ファイルからシートを取り出す
```PHP
$sheet = $spreadsheet->getSheetByName("シートの名前");
```
もしくは、

```PHP
//選択されているシートを取得
$sheet = $spreadsheet->getActiveSheet();

//2番目のシートを取得。Indexは0スタート
$sheet = $spreadsheet->getSheet(1);
```

### そのシートのセルの情報を取り出す
```PHP
//B2のセルの値
$sheet->getCell("B2")->getValue();
```

### 範囲のセルを配列で取り出す
空のセルには`null`が入ります

```PHP
$data = $sheet->rangeToArray("A2:A100");
```

### イテレータで情報を取り出す
イテレータが使えます。上の`rangeToArray`は範囲がわかってないと使えませんでしたが、~~これを使うと`Ctrl+Shift+↓`をしたみたいに空じゃないセルまでを取れます。~~←そんなことはなかった。`xlsx`ファイルあるあるのなぜか空のセルまでデータが入っているやつのせいで、`null`でもデータ自体は取れてしまう。

- 行ごと。この例だとB1～Bnまで

```PHP
foreach ($sheet->getRowIterator() as $row) {
    var_dump($sheet->getCell("B".$row->getRowIndex())->getValue());
}
```

- 列ごと。この例だとA1～n1まで

```PHP
foreach ($sheet->getColumnIterator() as $column) {
    var_dump($sheet->getCell($column->getColumnIndex() . "1")->getValue());
}
```

二つ組み合わせれば範囲を網羅できます。

### 結合されたセルを取得する
これ！！！これがしたかった！！！！

```PHP
var_dump($sheet->getMergeCells());
/*
array(
    "A1:A10" => string(6)"A1:A10",
    "B2:C10" => string(6)"B2:C10"
);
*/
```

ただの文字列しか取得できませんが、結合の始点と終点を取得することができます！
値そのものは、結合する前のセルに入っています（場所は直接わからない)。
`B2`から`C10`の範囲に`null`でないセルが1つだけ存在するはずなので、そこの値を取得しましょう。

## 書く
### 新しい`SpreadSheet`を作る
```PHP
$spread_sheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
```

この時点ではファイル形式は指定されません。後に出てくる`$writer`がなんのインスタンスかによって最終のファイル形式が決まります。

### 新しい`Sheet`を作る
```PHP
$sheet = $spreadsheet->createSheet(0);
```
`0`を入れないとシートが指定されなくて空白になるっぽい？

### 既存の`Sheet`を取得する
```PHP
$sheet = $spreadsheet->getSheet(0);
```
最後に保存するとき、`csv`じゃなくて`xlsx`を指定すると最初から1つシートがあって`createSheet`すると2枚目ができるので、`xlsx`ファイルを作りたいときはこっちのほうが良いっぽい

### 値をセットしていく
```PHP
$sheet->setCellValue("A1", "foo");
$sheet->setCellValue("B1", "bar");

$num = 2;
foreach ($data as $key => $value) {
    $sheet->setCellValue("A" . $num, $key);
    $sheet->setCellValue("B" . $num, $value);

    $num++;
}
```

### 配列から値をセットする
```PHP
$array = [
    [
        "a1",
        "b1",
        "c1",
        null,
        "e1"
    ],
    [
        "a2",
        "b2",
        "c2"
    ]
];

//fromArray(セットする配列, セットしない値, 開始場所のセル)
$sheet->fromArray($array, null, 'A1');
```
とすると、以下の画像のように値が入る。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71557/43905cac-fd14-7278-f454-79f110ee1386.png)

`fromArray()`の第二引数は`null`じゃなくても、

```PHP
$array = [
    [
        "a1",
        "b1",
        "c1",
        "秘密の値",
        "e1"
    ]
];

$sheet->fromArray($array, "秘密の値", 'A1');
```

みたいにすると以下の画像のように`秘密の値`がセットされない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71557/ad2f3f95-c561-e77f-e065-5fee50d9aece.png)


### ファイルを保存する
```PHP
$writer = new PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
$writer->setSheetIndex(0);
$writer->save("hoge.csv");
```

CSVのときはシートという概念がないので、保存するシートを指定しないと空白になるっぽい

```PHP
$writer = new PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$writer->save("hoge.xlsx");
```

`csv`じゃなくて`xlsx`の`writer`を使うと`xlsx`形式になる

# おわりに
これで基本的な動作は網羅できてる気がします。
メタデータのセットとかセル結合とかもできますが、エクセルを読むことはあっても書くことはないので省略しました。
PHPでExcelを使うのは悪手と言われますが、そんなに難しくないし業務の補助に使う程度なら全然ありだと思います。