Tag Archive แม่แบบ

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)

แก้ validation error ใน YII 2

ฟอร์มของ yii จะมีการแจ้งเตือนถ้าหากพบว่าข้อมูล input ที่เรากรอกใน form ไม่ถูกต้อง โดยจะทำกรอบอินพุต ป้าย label เป็นสีแดงและมีข้อความแสดงใน help-block เพิ่มขึ้นมา บางครั้งก็ทำให้ฟอร์มที่จัดไว้แน่นๆ ไม่ใช่แบบบรรทัดละกล่องข้อความตามแบบเว็บสมัยใหม่ เวลาเจอความผิดพลาด มันก็จะถีบตัวอื่นออกไป หรือดูอัดแน่นเกินไปจนดูไม่สวย

ก็สามารถเอาออกได้โดยใช้ form template เหมือนเดิม โดยยังสามารถทำ validation ได้ตามปกติ

วิธีการคือ

  1. เปิดไฟล์ _form.php ใน view เป้าหมาย
  2. เปลี่ยน
    <?php $form = ActiveForm::begin(['id' => 'login-form']); ?>
    

    เป็น

    <?php $form = ActiveForm::begin([
        'fieldConfig' => [
            'template' => "{label}\n{input}\n{hint}"
        ],
        'id' => 'login-form'
    ]); ?>
    

อย่าลืมเปลี่ยน id นะครับ

ถ้าใช้

use yii\bootstrap\ActiveForm;

ให้เปลี่ยน template เป็น

'template' => "{label}\n{beginWrapper}\n{input}\n{hint}\n{endWrapper}"

YII 2 : Horizontal / inline form

ตามปกติ form ของ yii ที่ใช้ GII generate ออกมาจะเป็นรูปแบบ label บรรทัดหนึ่ง input อีกบรรทัดหนึ่ง อ่านง่าย สวยงามพอสมควร แต่อาจจะไม่ถูกใจบรรดาหัวหน้าอนุรักษ์นิยมทั้งหลาย

yii มีอีกทางเลือกให้ใช้ทำฟอร์ม คือ มี Horizontal form ให้เลือกใช้ yii bootstrap activeform (Horizontal form) อีกตัว

วิธีการคือ

  1. เปิดไฟล์ _form.php ใน view เป้าหมาย
  2. เปลี่ยน use yii\widgets\ActiveForm; เป็น use yii\bootstrap\ActiveForm; ถ้าไม่เปลี่ยนจะเจอ error Setting unknown property: yii\widgets\ActiveForm::layout
  3. เปลี่ยน
    $form = ActiveForm::begin();

    เป็น

    $form = ActiveForm::begin([
        'fieldConfig' => [
            'horizontalCssClasses' => [
                'label' => 'col-sm-2',
                'offset' => 'col-sm-offset-2',
                'wrapper' => 'col-sm-10'
            ]
        ],
        'layout' => 'horizontal'
    ]);
  4. สำเร็จแล้ว ฟอร์มจะเปลี่ยนเป็นรูปแบบเหมือนกับ Bootstrap Horizontal form โดย

    • layout คือ จะวางฟอร์มเป็นแบบไหน มีให้เลือก คือ default, horizontal และ inline
    • label คือ ค่า grid ของ label
    • wrapper คือ ความกว้างของช่อง input
    • offset คือ การเว้นระยะของพวก checkbox ตั้งไว้เท่ากับ label จะได้ตรงกันพอดี

    เสร็จแล้ว หรือจะใช้การจัด code แบบของผมก็ได้ครับ ไม่อยากสลับ php เข้าออก ไปๆ มาๆ

    <?php
    
    use yii\helpers\Html;
    use yii\bootstrap\ActiveForm;
    
    ?>
    
    <div class="user-form">
    <?php
    $form = ActiveForm::begin([
        'fieldConfig' => [
            'horizontalCssClasses' => [
                'label' => 'col-sm-2',
                'offset' => 'col-sm-offset-2',
                'wrapper' => 'col-sm-10'
            ]
        ],
        'layout' => 'horizontal'
    ]);
    
    echo $form->field($model, 'username')->textInput(),
    $form->field($model, 'email')->textInput(),
    $form->field($model, 'status')->checkbox(array('label'=>'enable')),
    '<div class="form-group">',Html::submitButton($model->isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']);
    ActiveForm::end();
    echo'</div>';