You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用PHP和JavaScript动态替换HTML表格中的占位符

问题描述

我开发了一款发票模板设计器,可用于设计发票模板,模板创建完成后,其HTML会保存到数据库中待用。现在用户使用该模板创建发票时遇到了问题:模板是带有占位符的表格(表格中包含[course_no_0]、[product_0]这类带索引的占位符),由于表格是动态生成的,无法直接通过PHP循环访问表格单元格,请问该如何替换表格单元格的值?需要相关思路及示例。

现有代码
if (!empty($_GET['invoice_id']) && $_GET['invoice_id']) {
    $invoice_id = $_GET['invoice_id'];
    $invoiceValues = getInvoice($invoice_id);       
    $invoiceItems = getInvoiceItems($invoice_id);   
}

$invoiceDate = date("d/M/Y, H:i:s", strtotime($invoiceValues['order_date']));

$template_name = getinvoicetemplatename();

$template = get_template($template_name[0]);

// 替换模板变量
$token = array(
    '[Business_Name]'          => 'http://www.codexworld.com',
    '[Business_Address_1]'     => 'CodexWorld',
    '[City]'                   => $userName,
    '[State]'                  => $userEmail,
    '[Postal_Code]'            => 'http://www.codexworld.com',
    '[Business_Phone_Number]'  => 'CodexWorld',
    '[Business_Email_Address]' => $userName,
    '[Client_Name]'            => $userEmail,
    '[Client_Address_line_1]'  => 'http://www.codexworld.com',
    '[Invoice_Number]'         => 'CodexWorld',
    '[Date]'                   => $invoiceDate
);

foreach ($token as $key => $value){
    $output = str_replace($key, $value, $template[0]);
}

$i = 0; 
foreach ($invoiceItems as $invoiceItem) {
    $i++;

    $output = str_replace("[course_no_{$i}]", $invoiceItem["item_code"], $output);
    $output = str_replace("[product_{$i}]", $invoiceItem["item_name"], $output);
    $output = str_replace("[delegates_no_{$i}]", $invoiceItem["order_item_quantity"], $output);
    $output = str_replace("[price_{$i}]", $invoiceItem["order_item_price"], $output);
    $output = str_replace("[total_{$i}]", $invoiceItem["order_item_final_amount"], $output);
}
模板HTML字符串
<div id="items-list">
    <table id="table-invoice" class="table table-bordered table-condensed  items-table" width="100%">
        <thead>
            <tr id="table_invoice_header">
                <th id="table_invoice_header1">Course No</th>
                <th id="table_invoice_header2">Course Name</th>
                <th id="table_invoice_header3">No of Delegates</th>
                <th id="table_invoice_header4" width="100">Price</th>
                <th id="table_invoice_header5" width="100">Total</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td class="total-rown">[course_no_0] </td>
                <td class="total-rown">[product_0] </td>
                <td class="total-rown">[delegates_no_0] </td>
                <td class="total-rown">[price_0] [curr]</td>
                <td class="total-rown">[total_0] [curr]</td>
            </tr>
            <tr>
                <td class="total-rown">[course_no_1] </td>
                <td class="total-rown">[product_1] </td>
                <td class="total-rown">[delegates_no_1] </td>
                <td class="total-rown">[price_1] [curr]</td>
                <td class="total-rown">[total_1] [curr]</td>
            </tr>
            <tr>
                <td class="total-rown">[course_no_2] </td>
                <td class="total-rown">[product_2] </td>
                <td class="total-rown">[delegates_no_2] </td>
                <td class="total-rown">[price_2] [curr]</td>
                <td class="total-rown">[total_2] [curr]</td>
            </tr>
            <tr>
                <td class="total-rown">[course_no_3] </td>
                <td class="total-rown">[product_3] </td>
                <td class="total-rown">[delegates_no_3] </td>
                <td class="total-rown">[price_3] [curr]</td>
                <td class="total-rown">[total_3] [curr]</td>
            </tr>
            <tr>
                <td class="total-rown">[course_no_4] </td>
                <td class="total-rown">[product_4] </td>
                <td class="total-rown">[delegates_no_4] </td>
                <td class="total-rown">[price_4] [curr]</td>
                <td class="total-rown">[total_4] [curr]</td>
            </tr>
        </tbody>
        <tfoot>
            <tr class="totals-row">
                <td colspan="3" class="wide-cell"></td>
                <td id="tableheader"><strong>Sub Total</strong></td>
                <td class="total-rown sub-total" coslpan="2">[sub_total] [curr]</td>    
            </tr>
            <tr class="totals-row">
                <td colspan="3" class="wide-cell"></td>
                <td class="total-rown" id="tableheader">Tax Rate</td>
                <td class="total-rown tax-rate" coslpan="2">[tax_rate]&nbsp;%</td>
            </tr>
            <tr class="totals-row">
                <td colspan="3" class="wide-cell"></td>
                <td class="total-rown">Tax Amount</td>
                <td class="total-rown tax-amount" coslpan="2">[tax_amount] [curr]</td>
            </tr>
            <tr class="totals-row">
                <td colspan="3" class="wide-cell"></td>
                <td id="tableheader1"><strong>Total</strong></td>
                <td class="total-rown invoice-total" coslpan="2">[total] [curr]</td>
            </tr>
        </tfoot>
    </table>
</div>
解决方案

核心思路

  1. 修正基础变量替换逻辑:原代码每次替换token都从原始模板重新处理,导致只有最后一个token生效,需先初始化$output为模板内容,再循环替换所有token。
  2. 对齐占位符索引:模板占位符从0开始,但原代码$i先自增再替换,导致第一个数据项匹配错误,需调整索引起始值。
  3. 清理多余空行:模板有固定行数,若实际发票项少于模板行数,需移除未被替换的占位符行,避免显示无效内容。
  4. 批量替换优化:使用strtr()或批量str_replace()提升替换效率,减少重复操作。

修正后的代码示例

if (!empty($_GET['invoice_id']) && $_GET['invoice_id']) {
    $invoice_id = $_GET['invoice_id'];
    $invoiceValues = getInvoice($invoice_id);       
    $invoiceItems = getInvoiceItems($invoice_id);   
}

$invoiceDate = date("d/M/Y, H:i:s", strtotime($invoiceValues['order_date']));

$template_name = getinvoicetemplatename();
$template = get_template($template_name[0]);

// 初始化输出为模板内容
$output = $template[0];

// 替换全局静态变量
$token = array(
    '[Business_Name]'          => 'http://www.codexworld.com',
    '[Business_Address_1]'     => 'CodexWorld',
    '[City]'                   => $userName,
    '[State]'                  => $userEmail,
    '[Postal_Code]'            => 'http://www.codexworld.com',
    '[Business_Phone_Number]'  => 'CodexWorld',
    '[Business_Email_Address]' => $userName,
    '[Client_Name]'            => $userEmail,
    '[Client_Address_line_1]'  => 'http://www.codexworld.com',
    '[Invoice_Number]'         => 'CodexWorld',
    '[Date]'                   => $invoiceDate,
    // 补充合计相关变量替换
    '[sub_total]'              => $invoiceValues['sub_total'],
    '[tax_rate]'               => $invoiceValues['tax_rate'],
    '[tax_amount]'             => $invoiceValues['tax_amount'],
    '[total]'                  => $invoiceValues['total'],
    '[curr]'                   => $invoiceValues['currency']
);

// 批量替换全局变量,效率更高
$output = strtr($output, $token);

// 替换发票明细项,索引从0开始
foreach ($invoiceItems as $i => $invoiceItem) {
    $output = str_replace("[course_no_{$i}]", $invoiceItem["item_code"], $output);
    $output = str_replace("[product_{$i}]", $invoiceItem["item_name"], $output);
    $output = str_replace("[delegates_no_{$i}]", $invoiceItem["order_item_quantity"], $output);
    $output = str_replace("[price_{$i}]", $invoiceItem["order_item_price"], $output);
    $output = str_replace("[total_{$i}]", $invoiceItem["order_item_final_amount"], $output);
}

// 移除未被替换的空行(匹配包含未替换占位符的<tr>行)
$output = preg_replace('/<tr>(.*?)\[course_no_\d+\](.*?)<\/tr>/s', '', $output);

代码说明

  • 全局变量替换:使用strtr()一次性替换所有静态变量,比循环str_replace()更高效。
  • 明细项索引对齐:直接使用foreach的键值$i作为占位符索引,和模板的0起始索引匹配。
  • 空行清理:通过正则表达式匹配包含未替换占位符的表格行,直接移除,避免显示无效内容。
  • 补充合计变量:添加了模板中存在的[sub_total]、[curr]等合计相关变量的替换,完善模板内容。

内容的提问来源于stack exchange,提问作者Landry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 18:39:22