phpでcsvを読み込んでyaml形式で出力する

### はじめに

csvを読み込んで、yaml形式で出力します。
コマンドを実行したときに、outputディレクトリにyamlファイルが生成されるところまでを作成します。

### 目的
yamlファイルを手動で書くのが面倒だったので、自動化するスクリプトを書いた方がいいと思い、開発しました！

今回はフレームワークは使用せず、生のphpでスクリプトを作成します。
githubに上げているのでどうぞ参考にしてください！
https://github.com/masahiro96848/php-yaml

### ディレクトリ構成
```
yamlCongig/
   ┣ input/
      └ sample.csv
   ┣ output/
      └ sample.yaml
   │ vendor
   Yaml.php(yamlに変換するスクリプト)
　　　　　　Map.php  (csvの列を指定するクラス)
       
```

### 使用方法
1. inputディレクトリ下にcsvファイルを作成。
2. コマンド(php Yaml.php csvのファイル名(引数))を実行
3. outputディレクトリにyamlファイルが生成。   

## Yamlとは？
YAMLは構造化データを人間の目にわかりやすいように表現できるように設計された言語です。
シーケンス 配列、マッピング　ハッシュ（key/value）など下記の記事で説明されています。
[【技術備忘録①】YAML記法と不明点記録。](https://qiita.com/t_o_d/items/bd58fb798c5087f0ef1a)
[YAML入門](https://qiita.com/mczkzk/items/7be753dc221c2f049670)

## CSVとは？
CSVファイルとは、「comma separated values」の略称を指し、その名の通り値や項目をカンマ（,）で区切って書いたテキストファイル・データのことをいいます。ファイルの拡張子は「.csv」となり、様々なソフトで開くことができるのが大きな特徴です。
[CSVファイルとは？作成方法と使えるツールも合わせて紹介！](https://shop-pro.jp/yomyom-colorme/66698)
[今さら聞けないIT用語CSVファイルとExcelファイルって何が違うの？](https://data.wingarc.com/csv-excel-2-19747)


## 作成過程
### 1. symfony/yamlをインストール
yamlに変換するときのライブラリを使用します。(Symfonyではデフォルトで入っています)
```
composer require symfony/yaml
```

### 2. CSVを作成
今回は下記のサンプルを使います。
``` sample.csv
テーブル,ジャンル,名前,内容,備考,,
テーブル1,映画,洋画,トランスポーター,ジェイソンステイサムかっこいいよね,,
テーブル2,プログラミング,"php",最新は8.1,最近はphpの方がいいね,,
テーブル3,スポーツ,サッカー,11人でする競技,ワールドカップ観たいね,,
```

### 3. Mapのクラスを作成
yamlファイルに出力したときに、keyとvalueの値を当てはめるMapクラスを作成。
このファイルは、csvの列の番号を指定して、マッピングする情報を格納するクラスになります。

```Map.php
<?php

class Map 
{
    public $valueMap;

    public function __construct()
    {
        $this->valueMap = self::valueMapping();
    }

    public function valueMapping() :array
    {
        return [
            # テーブル名
            'table-name' => 0,
            # 　ジャンル
            'genre' => 1,
            # 名前
            'name' => 2,
            # 内容
            'content' => 3
        ];
    }

}


?>
```


### 4. Yaml.phpを作成(実際にyamlを記載する情報を記載)
``` Yaml.php
<?php
require 'Map.php';
require __DIR__ . '/vendor/autoload.php';

use Symfony\Component\Yaml\Yaml;

if (count($argv) <= 1) {   # コマンドラインで引数があるかどうかの条件分岐
    echo '引数を指定してください';
    return;
}

$fileName = $argv[1];  # csvのファイル名の引数
$inputFilePath = './input/' . $fileName . '.csv';

$inputFileContents = readCSV($inputFilePath);

$valueMap = new Map(); # Mapをインスタンス化

$yamlContents = []; # $yamlContentsの変数に配列を格納

foreach ($inputFileContents as $row) {  # 読み込んだCSVをforeachで回して取得
    $tableName = $row[$valueMap->valueMap['table-name']];  # テーブルを取得する変数
    $genreMap = fetchGenre($row, $valueMap->valueMap);
    $nameMap = fetchName($row, $valueMap->valueMap);
    $contentMap = fetchContent($row, $valueMap->valueMap);
    $yamlContents['body'][$tableName]['genre'] = $genreMap;
    $yamlContents['body'][$tableName]['name'] = $nameMap;
    $yamlContents['body'][$tableName]['content'] = $contentMap;

}

// 配列をyamlに変換する処理
file_put_contents('./output/' . $fileName . '.yaml', Yaml::dump($yamlContents, 4));


// 値ごとの関数を作成
function fetchGenre(array $row, array $valueMap)
{
    $genre = $row[$valueMap['genre']];
    if ($genre === '') {
        return NULL;
    }
    return $genre;
}

function fetchName(array $row, array $valueMap)
{
    $name = $row[$valueMap['name']];
    if ($name === '') {
        return NULL;
    }
    return $name;   # 複数ある場合はexplode(',', $name);
}
function fetchContent(array $row, array $valueMap)
{
    $content = $row[$valueMap['content']];
    if ($content === '') {
        return NULL;
    }
    return $content;
}

// CSVを読み込み、1行ずつ読み込む関数
function readCSV(string $filePath): array
{
    $file = fopen($filePath, 'rb');
    $listOfRows = [];
    $headerSkip = false;  // headerをスキップしたかどうかを判定するflg
    while (($row = fgetcsv($file)) !== false) {
        if ($headerSkip === false) {
            $headerSkip = true;
            continue;
        }
        if ($row == [null]) {
            $listOfRows[] = [];
        } else {
            $listOfRows[] = $row;
        }
    } 
    return $listOfRows;
}


?>
```

## 5-1. yaml形式出力①
csvで読み込んだのをyaml形式で出力すると以下のようになります。

``` sample.yaml
body:
    テーブル1:
        genre: 映画
        name: 洋画
        content: トランスポーター
    テーブル2:
        genre: プログラミング
        name: php
        content: 最新は8.1
    テーブル3:
        genre: スポーツ
        name: サッカー
        content: 11人でする競技
```


## 5-2. yamlの中身の配列①
今回のyamlは配列でそれぞれ階層分けされているのですが、配列の中身は以下のようになります。
``` php
$yaml = [
    "body" =>   [
        "テーブル1" =>
            [
                "genre" => "映画",
                "name" => "洋画",
                "content" => "トランスポーター"
            ],
        "テーブル2" =>
            [
                "genre" => "プログラミング",
                "name" => "php, ruby",
                "content" => "最新は8.1",
            ],
        "テーブル3"=>
            [
                "genre" => "スポーツ",
                "name" => "サッカー",
                "content"=> "11人でする競技",
            ]
    ]
];
var_dump($yaml);
```

## 6-1 yaml形式出力②
ここで、テーブル2の中の名前に、rubyを追加して、複数の場合はどうなるのでしょうか？　
この場合、explodeメソッドを使って文字列を文字列により分割する処理を付け加えましょう。
return explode(',', $name)でreturnしてみると以下の結果になります。

``` sample.yaml
body:
    テーブル1:
        genre: 映画
        name:
            - 洋画
        content: トランスポーター
    テーブル2:
        genre: プログラミング
        name:
            - php
            - ruby
        content: 最新は8.1
    テーブル3:
        genre: スポーツ
        name:
            - サッカー
        content: 11人でする競技
```

keyの下の階層に　- をつけて表示されるようになりました。


6-2. yamlの中身の配列②
省略しますが。これをテーブル2を配列形式で表すと
```php
"name" => ["php", "ruby"]
```
になります。


### 学んだこと
- 普段、Laravelとかフレームワークを中心にやっていたので、生のphpに触れる機会があまりなかったので、配列やforeachを使ってみた。
- コードを読むのはなんとかなるけど、「設計やコードを書く」というはまだまだ出来てないと気づく。


## 参考記事
- [PHPオブジェクト指向入門（前半）](https://qiita.com/mpyw/items/41230bec5c02142ae691)
- [Yaml 公式ドキュメント](https://symfony.com/doc/current/components/yaml.html)
- [プログラマーのための YAML 入門 (初級編)](https://magazine.rubyist.net/articles/0009/0009-YAML.html)
- [CSVファイルとは？](https://shop-pro.jp/yomyom-colorme/66698)