• ホームページ制作
  • BLOG
  • 【WP】自前のCSVインポート処理の決定版。大量データや多数のカスタムフィールドにも対応。

【WP】自前のCSVインポート処理の決定版。大量データや多数のカスタムフィールドにも対応。

2019/10/07


WordPressにCSVデータをインポートするために様々なプラグインがありますが、要件に応じた柔軟な対応は難しいものがあります。
例えば、「社内で管理している商品番号をキーにしたい」や「基幹システムから出力したCSVを毎日自動で取り込みたい」や「管理画面から『上書き禁止』設定をしたものはCSVインポートで上書かない」などなど。

こういう要件に関してはプラグインをカスタマイズをするよりも、自前でのCSVインポート処理を作ってしまうのが良いと思います。
そんなに難しくありませんし、とても柔軟に作ることが出来ます。

この記事では以下の流れで解説します。

  1. CSVファイルのアップロード処理
  2. インポート処理用のPHP作成
  3. 大量のデータや処理でメモリが足りなくなるケース対応
  4. 非同期処理で行う場合処理が完了したかどうか確認

比較的少量のデータや簡易な構成の場合は、上記の2の手順までで完結しますが、
CSVデータが増えた時のことを考えると4の手順まで実装しておくと安心かもしれません。
(4まで実装するとユーザーが使いやすいですし)

1.CSVファイルのアップロード処理

まずは、CSVファイルをアップロードする処理について書きます。
基幹システムなどでサーバーに置く場合はこの部分は不要ですね。

Points

  1. アップロード用固定ページ作成(非公開設定)
  2. アップロード処理について
  3. (必要なら)ファイル自体をUTF-8に変換
  4. インポート処理用PHPを呼び出して実行

アップロード用固定ページ作成(非公開設定)

CSVインポートする画面は管理者以外には使わせる必要はないので、非公開の固定ページを作成し、そこをCSVファイルをアップロードできる画面にします。
<input type=”file>でファイルをアップロードできますが、<form>に「enctype=”multipart/form-data”」を指定する必要があります。

key にランダムな文字列をセットし、後で照合することにより、この後作成するプログラムの不正な使用を防ぐことができます。
(アップロード用ページは非公開ページですのでここにkeyを書いちゃって大丈夫です)

アップロード処理について

<form>でファイルをアップロードしたら、それを受け取る処理を書く必要があります。
アップロードしたファイルの情報は変数「$_FILES」に格納されます。それを利用して例えば以下のように処理を書きます。

(必要なら)ファイル自体をUTF-8に変換

CSVファイルをエクセルで作成することが多いと思いますが、エクセルで保存すると通常は文字コードが「Shift-JIS」 になります。
最近はエクセルでも「UTF-8」でCSV保存することが出来ますが、これにはまた別の罠があります。。。(UTF-8 BOMあり で保存される)
WPでの通常扱うことのできる文字コードは「UTF-8 BOMなし」ですので、上記の場合は文字コードを変更する必要があります。
『文字コードって何?』というクライアントもいますので、こちら側の処理で変更してあげたほうが良い場合が多いです。

インポート処理用PHPを呼び出して実行

インポート処理を直接書いても良いのですが、それ用のPHPを作って呼び出したほうが後々便利なので、以下のようにしてPHPを呼び出します。

しかし、この書き方では、外部処理にしたことをあまり有効に活かせていません。
CSV処理が完了するまで、そのページは【処理中】となるからです。(時間のかかる処理ならブラウザの読み込み時間制限によりタイムアウトしてしまいます)
ですので、以下のように記述します。

『PHPの非同期処理』などで調べると「【プログラム】 > /dev/null &」という書き方がけっこう出てきますが、ログを取る場合は上記の書き方が良いです。

これで、とりあえずCSVインポート処理用のプログラムを実行できました。
次の部分ではプログラムの中身について書いていきます。

2.インポート処理用のPHP作成

WordPressで直接制御しないPHPを読み込んでいますので、WPの関数などが使えるように、まずはWPを読み込んでやります。

このプログラムの中でどのような処理にするかは案件によって変わるわけですが、ポイントとケースをいくつか記載します。

Points

  1. CSVファイルを読み込んで配列に入れる
  2. 分かりやすい配列の扱い方

Cases

  1. カスタム投稿に投稿する
  2. カスタムフィールドに値をセットする
  3. タクソノミーをセットする

CSVファイルを読み込んで配列に入れる

CSVの取り込みには、「SplFileObject」を使うと便利です。(PHP5.1以上)

上記の「$line」という配列に、CSVの1行がさらに配列の形で入っています。それぞれのセルの値を取りたい場合は配列番号で指定できます。

(画像1)

分かりやすい配列の扱い方

しかし、配列番号で今後の処理を書くのは、パッと見て何の項目か分からなくなり不具合の温床ともなります。
そこで次のようにすると分かりやすいと思います。(これはただの提案です。もっと良いやり方があるかもしれません。)

CSVにはhead行をつけていると思いますので、このheadの項目名で扱えるととても便利です。
head行を読み込んでそれを逆配列(キーと値を入れ替えた配列)にしておき、それを用いて各セルの値を指定します。
「array_flip」という関数で可能です。

ここから実際の処理を書いていくわけですが、ざっくりとよくあるケースの記述を書いていきます。

Case : カスタム投稿に投稿する

これが一番多いでしょうか。CSVの1行が1つのカスタム投稿になる、というものです。

投稿を追加するのは「wp_insert_post」というWordPressの関数を使いますが、この関数はただ追加するだけです。
CSVの中身を1度だけ入れる、というのならこれでポンポン入れていけば良いでしょうが、CSVで管理しているものを更新してそれを適宜インポートしたい、という場合はこのやり方ではだめです。

WordPressは投稿をIDで管理していますが、WordPressの持つIDとCSVの一意なidが一致することは意図的にそうしない限り、ないでしょう。
例えば商品を管理するCSVでは品番で一意に管理しているかもしれませんが、これをそのままWordPressのIDにすることは無理です。
ですので、CSVでの一意になる項目(複数のキーで一意になるのでも良い)で投稿を検索して、見つかったら更新(wp_update_post)し、見つからなかったら新規投稿(wp_insert_post)すれば良いわけです。

↓上記の「画像1」で1列目をスラッグとして一意に管理している場合の書き方(※スラッグで管理するのは実際はお勧めしません。WordPressの内部処理で思ったのと違うスラッグで投稿されることがあるからです)

Case : カスタムフィールドに値をセットする

CSVでインポートしたい、なんて時にはだいたいカスタムフィールドに値を入れたいものです。
「update_post_meta」を使って入れることが出来ます。「update_post_meta」は「update」なので更新専用と一瞬思いますが、対象が存在しないときは「add_post_meta」というカスタムフィールドを追加する処理を内部で行ってくれます。ですので「update_post_meta」だけで完結出来ます。

単一のデータを入れる場合はこれで良いですが、複数データ(繰り返しカスタムフィールド)を入れたいときには、一度消してから追加する、という流れです。
例えば、CSVのセルにカンマ区切りで入っているデータを繰り返しフィールドの形でWPに入れたい場合は以下のように行います。

Case : タクソノミーをセットする

タクソノミーをセットしたいことも多々あると思います。
これも難しくありません。「wp_set_object_terms」を使うことができます。

ちなみに、タクソノミーにもメタ情報を持たせることが出来ます。
「update_term_meta」を使えば「update_post_meta」と同じような感覚でタクソノミーのメタ情報をセットできます。

補足

ここまででCSVの中身をWPに入れる処理はたいてい出来ると思います。
インポートしたメタ情報(カスタムフィールド)についての補足ですが、WPの初期状態では管理画面からは見えませんが情報としては確かに入っています。
管理画面で視覚的に見るためにはカスタムフィールド用のプラグインなどを利用することが出来るでしょう。
おすすめは「Smart Custom Fields」です。有名なプラグインで「Advanced Custom Fields」というのがありますが、繰り返しフィールドの値の保存の仕方がWordPressの標準の概念と異なっているので、「Smart Custom Fields」を使う方が良いです(Advanced Custom Fieldsは、WordPress自体が初期には繰り返しフィールドに対応していなかったので、独自のやり方で繰り返しフィールドに対応してくれていたのでこの形になったのですが)。

3.大量のデータや処理でメモリが足りなくなるケース対応

さて、CSVで管理しているデータはたいていそこそこ大容量です。だからこそCSVでインポートしたいと思うわけですが。
10,000行くらいのCSVをインポートする場合など、環境によっては処理が完了せずにタイムアウトしてしまうことがあります。またメモリ不足で落ちてしまうこともあります。
『10,000件くらいで!?』と思うかもしれませんが、WPの関数「wp_update_post」や「update_post_meta」「update_term_meta」などは既存データの確認や排他処理などを裏で行っているため結構処理が重たいのです。

そんなときには、1つのプロセスで処理するCSVの行数を制限して段階的に処理していけば大丈夫です。

PHPは「exec」関数で、linuxの標準コマンドを実行できます。それを利用して非同期にPHPを実行します。

これではただの非同期実行ですから、行数を制限していることにはなりません。
実行時に標準引数を付けてやることにより、開始行数を設定するようにしましょう。そして自分を再帰的に呼び出します。例えばこんな風に書けます。(セキュリティ的なことは考慮していません。)

4.非同期処理で行う場合処理が完了したかどうか確認

上記の3の書き方で非同期処理を行った場合、ユーザー(管理者)としては処理が完了したかどうか分かりません。
いつ終わったのか、そもそも正常に終わったのか、気になることでしょう。
そこで、書き込みしているログファイルを管理画面で見ることが出来れば便利だ!と気づきました。
非公開のページを作り、そこでログファイルを読み込めば良いのです。

しかし、ログファイルも結構なサイズになります。それを全部読み込むのは処理的に大変です。
そういう場合は、最後の数十行だけを表示すれば良いのです。
linuxの標準コマンド「tail」を使うことにより簡単に実装出来ます。

上記の「$output」に返ってきた値が入っていますので、それを適当に表示させてあげればOKです。

南本貴之