WordPress からカスタム投稿タイプの投稿データとそれに関連するデータを CSV で出力したい案件があり、テーマの中のテンプレートを使って実現してみました。

投稿データを CSV で出力できるプラグインはあるようですが、今回一定の条件で絞り込んだり、Advanced Custom Fields の「投稿オブジェクト」カスタムフィールドで紐付けた、別のカスタム投稿タイプの投稿データを一緒に出力する必要があったため、特定のページのテンプレートを作成する要領で実現できないかと考えました。

7年くらい前まで Movable Type を使っていましたが、Movable Type のテンプレートは静的ファイルを書き出すので、書き出すファイル名と拡張子を指定すれば、CSV などのテキストファイルを書き出すことも、CSS や JavaScript のファイルに MT タグを使って、セレクタや値を代入したり変数をループで処理したりすることができました。

一方、WordPress のテンプレートは動的に Web ページを表示するものなので、Movable Type のように簡単には行きません。

でも、以前 WordPress に FullCalendar を組み込んだ際、カレンダーに投稿データを読み込むための JSON 形式のファイルをテンプレートを使って作成したり、同じように Google Map に投稿データからマーカーを生成するための XML 形式のファイルをテンプレートを使って作成した経験がありましたので、まずは同様の方法で CSV 形式のファイルを作成してみました。

まず、CSV 用の固定ページを作成します。タイトルは何でもよく、本文は空で、任意のスラッグを設定します。ここでは仮に「custom-csv」とします。

続いて、この固定ページ用のテンプレートを作成します。この固定ページに適用するため、ファイル名を「page-custom-csv.php」とし、通常のテンプレートを記述する要領で、CSV として書き出すためのコードを書きます。

例として、カスタム投稿タイプの投稿のタイトルと、「フィールド1」から「フィールド5」まで、5つのカスタムフィールドの値を書き出すものとします。冒頭の絞り込みや「投稿オブジェクト」の出力は CSV の出力と直接関係ないのでここでは触れません。

また、「フィールド5」のカスタムフィールドはチェックボックスとします。チェックボックスはそのまま書き出すと複数の値が「,」で区切られ、CSV の区切り文字と同じになるので、ここでは「/」で区切ることにします。

<?php
$csv = null;

$args = array(
  'post_type' => 'custom-post',
  'post_status' => 'publish',
  'posts_per_page' => -1
);
$my_query = new WP_Query( $args );
if ( $my_query -> have_posts() ):

  $csv = 'タイトル,フィールド1,フィールド2,フィールド3,フィールド4,フィールド5';

  while ( $my_query -> have_posts() ): $my_query -> the_post();

    $csv .= get_the_title(). ',';
    $csv .= get_field('custom-field1'). ',';
    $csv .= get_field('custom-field2'). ',';
    $csv .= get_field('custom-field3'). ',';
    $csv .= get_field('custom-field4'). ',';
    $csv .= implode('/', get_field('custom-field5')). ',';
    $csv .= "\n";

  endwhile;
endif;
wp_reset_postdata();

echo $csv;
?>

とりあえず、これで固定ページ「custom-csv」を表示すると、次のような CSV 形式のデータが表示されます。(ブラウザ上では1行で表示されますが、ソースコード上では改行されます。)

タイトル,フィールド1,フィールド2,フィール3,フィールド4,フィールド5
タイトルA,フィールドデータ1A,フィールドデータ2A,フィールドデータ3A,フィールドデータ4A,フィールドデータ5A
タイトルB,フィールドデータ1B,フィールドデータ2B,フィールドデータ3B,フィールドデータ4B,フィールドデータ5B
タイトルC,フィールドデータ1C,フィールドデータ2C,フィールドデータ3C,フィールドデータ4C,フィールドデータ5C

しかし、これは CSV 形式で表示しているだけなので、このままでは CSV ファイルとして扱うことができません。

以前同様の方法で作成した FullCalendar 用の JSON 形式のデータや Google Map 用の XML 形式のデータは、同じテーマの中で使用するのでこのままでよかったのですが、CSV の場合はダウンロードして Excel 等で利用できるようにする必要があります。

表示されたソースコードをコピーしてテキストエディタに貼り付け、.csv で保存すれば扱うことができますが、Windows の Excel で開くためには文字コードも変換しなければなりませんし、そんな面倒なことはできませんので。

そこで、文字コードを UTF-8 から Shift-JIS に変換し、CSV ファイルとしてダウンロードできるようにします。「php csv ダウンロード」でググってみたらたくさんのヒントがありました。先のコードの先頭に HTTP ヘッダを3行追加し、最後の出力処理で文字コードを変換します。

<?php
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=custom.csv');
header('Content-Transfer-Encoding: binary');

$csv = null;

$args = array(
  'post_type' => 'custom-post',
  'post_status' => 'publish',
  'posts_per_page' => -1
);
$my_query = new WP_Query( $args );
if ( $my_query -> have_posts() ):

  $csv = 'タイトル,フィールド1,フィールド2,フィールド3,フィールド4,フィールド5';

  while ( $my_query -> have_posts() ): $my_query -> the_post();

    $csv .= get_the_title(). ',';
    $csv .= get_field('custom-field1'). ',';
    $csv .= get_field('custom-field2'). ',';
    $csv .= get_field('custom-field3'). ',';
    $csv .= get_field('custom-field4'). ',';
    $csv .= implode('/', get_field('custom-field5')). ',';
    $csv .= "\n";

  endwhile;
endif;
wp_reset_postdata();

echo mb_convert_encoding($csv, 'SJIS', 'UTF-8');
return;
?>

HTTPヘッダの2行目の filename=custom.csv でダウンロードする際のファイル名を設定しています。

これで、任意のページに固定ページ「custom-csv」へのリンクを設置すれば、  そのリンクから custom.csv をダウンロードするダイアログが表示されます。