PHP
phpword

phpwordでテンプレートを読み込んだものをループする

遭遇したケース

phpで帳票を出力する事があり、WORD出力指定。
phpwordを使って出力。
ただ帳票出力なので表が細かい。
数百人分一括出力が必要。(1人1ページ、各名前や住所、数字等を変数で出力)

挫折した方法

  • 丁寧に1行1行出力していく→表の細かさで挫折
  • XMLで<w:body>内を抜き出してループで連結し、ヘッダとフッタをくっつけて出力→うまく保存されず
  • なにかライブラリ内にそういう機能ないのか?!→見つからず

採用した方式

人単位でループするたびにテンプレート読み込み、テンプレートに設置した変数にsetValueしていく。
ループ最後にユニークIDで人数分のファイルを出力。
その後全ループ終了後(ファイル出力終了後)にファイルを連結してダウンロードさせる。
docxの連結はDocxMergeというライブラリを使用。
https://github.com/krustnic/DocxMerge

ここでは個別にファイルを出力する方法は割愛します。
phpwordをcomposerで導入した人はそのままcomposerでインストールしてください。
個別に設定する方はダウンロードしてrequireすれば多分大丈夫です。

Merge Exampleがちょっと不親切なので・・

require "vendor/autoload.php";
use DocxMerge\DocxMerge;

$dm = new DocxMerge();
$dm->merge( [
    "templates/TplPage1.docx",
    "templates/TplPage2.docx"
], "/tmp/result.docx" );

こんなサンプルがありますが、まあなんとなく、最初に連結するファイルを指定して、最後に連結完了後のファイル名を指定しているんだろうな、とわかると思います。
まあ正しいのですが・・最初の連結対象ファイルは配列でした。
なので文字列を生成するのではなく、ループ中にファイル名を含むパスを生成してください。

var_dump($ids);
array(4) {
  [0]=>
  string(17) "/path/to/001.docx"
  [1]=>
  string(17) "/path/to/002.docx"
  [2]=>
  string(17) "/path/to/003.docx"
  [3]=>
  string(17) "/path/to/004.docx"
}

こんな配列を作って、

$dm = new DocxMerge();
$dm->merge([$ids], '/path/to/result.docx');

をすれは完了かと思いましたが、ライブラリがエラー吐いてきます。
copyの値が配列だよとかなんとか。
ソースを見る限り、配列になっているようには思えなかったのですが、
DocxMerge.phpの
public function merge( \$docxFilesArray, \$outDocxFilePath ) {
の下でvar_dump($docxFilesArray)してみると、

array(1) {
  [0]=>
  array(4) {
    [0]=>
    string(17) "/path/to/001.docx"
    [1]=>
    string(17) "/path/to/002.docx"
    [2]=>
    string(17) "/path/to/003.docx"
    [3]=>
    string(17) "/path/to/004.docx"
  }
}

何故か配列が変わっています。
なぜ配列が変わっている!と調べるもの面倒だったので、2箇所修正したら動きました。

DocxMerge.php: 30

if ( !copy( $docxFilesArray[0], $outDocxFilePath ) ) {
↓
if ( !copy( $docxFilesArray[0][0], $outDocxFilePath ) ) {

DocxMerge.php: 36-37

for( $i=1; $i<count( $docxFilesArray ); $i++ ) {
  $docx->addFile( $docxFilesArray[$i], "part".$i.".docx", "rId10".$i );
↓
for( $i=1; $i<count( $docxFilesArray[0] ); $i++ ) {
  $docx->addFile( $docxFilesArray[0][$i], "part".$i.".docx", "rId10".$i );

これで連結成功しました。

連結してみると、ディレクトリ内に大量のゴミが・・

自分でゴミは片付けましょう。

$gomi = /path/to/*.tmp';
foreach (glob($gomi) as $val) {
   unlink($val);
}