Tag Archive PHPWord

Byphunsanit

PHPWord: รวมไฟล์ word เป็นไฟล์เดียว

เพราะว่าเอกสารที่่ต้องออกมาเป็น report มีความซับซ้อน ทำให้ต้องมีการแยก template เอกสารออกไปไฟล์ย่อยๆ แล้วประกอบขึ้นมาใหม่เป็นเอกสารที่ครบสมบูรณ์ ปัญหาคือ PHPWord มันไม่สามารถรวมเอกสารเป็นไฟล์เดียวกันได้

พระเอกของงานนี้คือ krustnic/DocxMerge จะทำหน้าที่รวมเอกสารให้

<?php
ini_set('max_execution_time', 0);
ini_set('memory_limit', '-1');

include '../vendor/autoload.php';
use DocxMerge\DocxMerge;

$fileName = 'result_' . date('Y-m-d_H-i-s') . '.docx';
$fileNameTemp = sys_get_temp_dir() . $fileName;
$templates = [
    'header.docx',
    'body.docx',
    'footer.docx',
];

/* merge document */
$dm = new DocxMerge();
$dm->merge($templates, $fileNameTemp);

/* download */
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Expires: 0');
readfile($fileNameTemp);

ความจริงส่วนที่ทำหน้าที่รวบรวมไฟล์เวิร์ดแล้วรวมเป็นไฟล์เดียวทำงานง่ายๆ

$dm = new DocxMerge();
$dm->merge($templates, $fileNameTemp);

โดยระบุไฟล์ต้นฉบับเข้าไปใน array $templates และระบุชื่อไฟล์ที่จะรวมไฟล์ ซึ่งควรจะสร้างไว้ใน temporary folder ที่ได้จาก function sys_get_temp_dir ก่อน เพราะทุกครั้งที่จะทำงานจะมีการสร้างไฟล์ขึ้นมา 2 ไฟล์ คือ ชื่อไฟล์ที่ระบุให้สร้างและไฟล์ชื่อสุ่มนามสกุล .tmp ที่จะกลายเป็นขยะตกค้างอยู่ จากกนั้นค่อยคัดลอกหรือ download มาใช้

Byphunsanit

PHPWord: กรอกข้อมูลใส่ template

เพราะว่า word นั้นมีความสามารถปรับแต่งรูปแบบได้ซับซ้อนมากกว่า excel อยู่มาก อย่างน้อยแค่ตำแหน่งของส่วนต่างๆ ในแบบฟอร์มก็มีมากกว่า excel ที่เลือกตำแหน่งได้แค่ว่าจะใส่ column / row ไหน

การใช้ PHP สร้างไฟล์ word ขึ้นมาการสร้างไฟล์แม่แบบขึ้นก่อน โดยการสร้างตัว word template ขึ้นมาก่อนโดยใช้โปรแกรม Microsoft Word สร้างเอกสารไปตามปกติ จึงง่ายกว่า จากนั้นก็แก้เพียงแต่ในส่วนที่่ต้องการจะแทนค่าให้ใส่ ${key name} ลงไป (ระวังเรื่องช่องว่างด้วยนะครับ) หรือส่วนที่ต้องการ repeat ก็เพิ่ม ${key name} … ${/key name} เข้าไปเท่านั้นจะง่ายกว่า ไฟล์ในตัวอย่างนี้สามารถโหลดได้จาก template.docx

จากนั้นก็เขียน code

<?php
ini_set('max_execution_time', 0);
ini_set('memory_limit', '-1');

include '../vendor/autoload.php';

use PhpOffice\PhpWord;

$title = 'Template Render ' . date('Y-m-d H:i:s');

/* load template */
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('template.docx');

/* fill data */
$templateProcessor->setValue('header', $title);

/* random data */
$scores = [];
for ($a = 0; $a < 10; $a++) {
    $scores[$a + 1] = rand(0, 100);
}

/* clone table row and fill */
$templateProcessor->cloneRow('student_id', count($scores));
foreach ($scores as $key => $value) {
    $templateProcessor->setValue('score#' . $key, htmlspecialchars($value, ENT_COMPAT, 'UTF-8'));
    $templateProcessor->setValue('student_id#' . $key, htmlspecialchars($key, ENT_COMPAT, 'UTF-8'));
}

/* clone all block */
$templateProcessor->cloneBlock('repeater', 5);

header("Content-Description: File Transfer");
header('Content-Disposition: attachment; filename="' . $title . '.docx"');
header('Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document');
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Expires: 0');
$templateProcessor->saveAs('php://output');

อธิบาย code

  • การทำงานโดยจะโหลด ตัว PHPWord มาใช้ก่อน จะสังเกตุว่าจะมีการใช้ namespaces แทนที่จะใช้ include เข้ามาเหมือน PHPExcel
  • กำหนดจุดที่จะกรอกข้อมูลโดยใส่ ${key name} ใน word เช่น ${header} จากนั้นใน PHP ก็ส่งค่าตัวแปรไปแทนที่โดยใช้
    $templateProcessor->setValue('header', $title);
  • สามารถ repeat ตารางได้โดยการใส่ ${key name} ใน column ที่ต้องการคัดลอกแถวทั้งแถว เช่น ${student_id} และสั่งให้คัดลอกแถวโดยใช้
    $templateProcessor->cloneRow('student_id', จำนวนครั้ง);

    แถวในตารางที่ใส่ ${student_id} จะเพิ่มขึ้นมาทั้งแถวรวมทั้งคอลัมน์อื่นด้วย

  • สามารถเติิมช้อมูลให้คอลัมน์ที่เพิ่มมาใหม่นี้โดยใช้
    foreach ($scores as $key => $value) {
        $templateProcessor->setValue('score#' . $key, htmlspecialchars($value, ENT_COMPAT, 'UTF-8'));
        $templateProcessor->setValue('student_id#' . $key, htmlspecialchars($key, ENT_COMPAT, 'UTF-8'));
    }

    สังเกตุว่าจะมี

    #' . $key

    เพิ่มขึ้นมาเพิื่อจะแทนที่ลำดับที่เพิ่มขึ้นมานั่นเอง

  • ในรูปแบบตารางที่ซับซ้อนเช่น ตาราง “ใบขออนุญาตสอบซ่อม” ที่ต้องทำซ้ำหลายแถวและหลายคอลัมน์พร้อมๆกัน ทั้งตารางจะไม่สามารถใช้
    $templateProcessor->cloneRow('student_id', จำนวนครั้ง);

    ได้ ต้องเปลี่ยนไปใช้

    $templateProcessor->cloneBlock('repeater', จำนวนครั้ง);

    แทน แต่จะไม่สามารถใส่ตัวแปรลงไปแทนที่ได้ใน version นี้ (PHPWord version 0.13.0)