league/csvを使ってPHPでお手軽CSV読み込み

Wataru SendoPosted by

この記事はAdvent Calendar 2018の12日目の記事です。
こんにちは!新規ゲーム開発プロジェクトでサーバーサイドエンジニアをしているsengokです。

※この記事はGRIPHONE Advent Calendar 2018 12日目の記事です。
https://qiita.com/advent-calendar/2018/griphone
https://adventar.org/calendars/3147

ゲームの開発・運用ではクエストに現れる敵のパラメータやイベントの報酬など様々なマスタデータをCSVで管理しているというケースが少なくないかと思います。
今回の記事では、PHPで簡単にCSVを取り扱うことができるthephpleague/csv(以下、league/csv)というライブラリを紹介したいと思います。

league/csvとは?

league/csvはPHPで作られたCSVを扱うためのシンプルなライブラリです。
PHP7系への対応はもちろん、テストコードやドキュメントもきちんと書かれているためとっつきやすいのかなと思います。

Github : https://github.com/thephpleague/csv
Document : https://csv.thephpleague.com/

league/csvの導入

league/csvはcomposer経由で簡単に導入することができます。

composer require league/csv && composer install

これで導入完了です!

composerについては次の記事が簡単にまとめてくれているので使い方が分からない方は参照してみてください。
PHP開発でComposerを使わないなんてありえない!基礎編 – Qiita

league/csvの使い方

早速、簡単なサンプルコードを使ってleague/csvの使い方を紹介したいと思います。
アプリケーションロジックを記述する際は、CSVを書き出すよりも読み込んで値を利用する場面の方が多いかと思いますので、今回は読み込みにフォーカスして説明したいと思います。
説明のために下記の構造のCSVファイルを用意しました。

monster.csv

id name hp atk def
1 slime 100 10 10
2 goblin 200 150 20
3 phoenix 1000 300 300

CSVのヘッダー名を取得する

まずはCSVのヘッダー名を取得してみます。
今回のmonster.csvの例では、1行目がヘッダー名に相当するため1行目の取得を行ってみます。

サンプルコード

<?php
require_once __DIR__ . '/vendor/autoload.php';
use League\Csv\Reader;
use League\Csv\Statement;

$csv = Reader::createFromPath('./csv/monster.csv', 'r');
// ここでヘッダーのオフセットは0、つまり1行目がヘッダーですよーと指定しています
$csv->setHeaderOffset(0);
$header = $csv->getHeader();

print_r($header);

結果

Array
(
    [0] => id
    [1] => name
    [2] => hp
    [3] => atk
    [4] => def
)

CSVから全レコードを取得する

次にCSVのヘッダー以外の全レコードを取得します。
サンプルコードは先程の続きから書かれていると思ってください。

サンプルコード

$records = $csv->getRecords();
foreach ($records as $record) {
  print_r($record);
}

結果

Array
(
    [id] => 1
    [name] => slime
    [hp] => 100
    [atk] => 10
    [def] => 10
)
Array
(
    [id] => 2
    [name] => goblin
    [hp] => 200
    [atk] => 150
    [def] => 20
)
Array
(
    [id] => 3
    [name] => phoenix
    [hp] => 1000
    [atk] => 300
    [def] => 300
)

先程指定したヘッダー名はArrayのKeyに指定されています。

先頭からN件取得する

次は、全レコードを取得するのではなく、ある件数だけ取得するようにしてみます。
CSVレコードに対して何か条件を指定して取得する場合には、Statementを活用します。

サンプルコード

$stmt = new Statement();

// limit 2件という条件をmonster.csvに適用した状態でレコードを下さいねーという意味です
$records = $stmt->limit(2)->process($csv);
foreach ($records as $record) {
  print_r($record);
}

結果

Array
(
    [id] => 1
    [name] => slime
    [hp] => 100
    [atk] => 10
    [def] => 10
)
Array
(
    [id] => 2
    [name] => goblin
    [hp] => 200
    [atk] => 150
    [def] => 20
)

並び替えて取得する(Hp降順)

次にHpで降順にレコードを並び替えて取得してみます。
Hpを降順にするための評価式が書かれた無名関数が登場して少し複雑に見えますが、感覚は先程同様です。

サンプルコード

$stmt = new Statement();
// Hp降順の無名関数を作って
$func = function (array $recordA, array $recordB) {
  return $recordB['hp'] <=> $recordA['hp'];
};

// orderByに入れてあげましょう
$records = $stmt->orderBy($func)->process($csv);
foreach ($records as $record) {
  print_r($record);
}

結果

Array
(
    [id] => 3
    [name] => phoenix
    [hp] => 1000
    [atk] => 300
    [def] => 300
)
Array
(
    [id] => 2
    [name] => goblin
    [hp] => 200
    [atk] => 150
    [def] => 20
)
Array
(
    [id] => 1
    [name] => slime
    [hp] => 100
    [atk] => 10
    [def] => 10
)

条件に当てはまるレコードを取得する

最後は一番使いそうなwhere条件です!
orderByの時と同じで簡単ですね!

サンプルコード

$stmt = new Statement();
$func = function (array $row) {
  return $row['atk'] > 100;
};
$records = $stmt->where($func)->process($csv);
foreach ($records as $record) {
  print_r($record);
}

結果

Array
(
    [id] => 2
    [name] => goblin
    [hp] => 200
    [atk] => 150
    [def] => 20
)
Array
(
    [id] => 3
    [name] => phoenix
    [hp] => 1000
    [atk] => 300
    [def] => 300
)

おわりに

今回は、league/csvを使って簡単にCSVの読み込みを行う方法について説明しました。
今回紹介した内容でもCSVを取り扱う、かなりのケースに対応できるのではないかなと思います。

CSVのライブラリというと、弊社含め、例えばレコードのグルーピングを行う仕組みを作る、読み込み時にキャッシュする仕組みを作るなど、
実際の開発・運用現場では工夫して使われているところが多いかと思いますが、league/csvのようにシンプルさを求めたライブラリと比較してみてうちはどうかな?と改めて見てみるのも面白いかもしれませんね!

それでは明日の記事もお楽しみに!