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

原生PHP对接htmx:无需大型模板引擎实现HTML片段渲染

原生PHP实现类Twig/Blade的HTMX片段渲染方案

问题背景

在原生PHP后端为HTMX返回HTML片段时,希望实现类似Twig的renderBlock或Blade的@fragment的片段渲染功能,但不想仅为此引入体积较大的模板引擎。

尝试过两种方案均存在缺陷:

  • 返回HTML字符串:维护困难,不符合开发最佳实践
  • 创建独立模板文件:功能可行,但项目规模扩大后会因模板文件过多,难以统一管理

现有代码示例如下:

现有实现代码

ProductController.php

public function searchByName(): void
{
    if (!isset($_GET['search_value'])) {
        $products = $this->productModel->getAll();
        $this->renderFragment('/productCard.php', ['products' => $products]);
    }

    $products = $this->productModel->searchByName($_GET['search_value']);

    if (!isset($products)) $this->renderFragment('/notFoundProduct.php');

    $this->renderFragment('/productCard.php', ['products' => $products]);
}

Controller基类的renderFragment方法

protected function renderFragment(string $path, array $values = []): void
{
    extract($values);
    require_once __DIR__ . '/../../view/fragments' . $path;
    exit();
}

productCard.php模板片段

<?php foreach ($products as $item) : ?>
        <div>
            <img src="<?= BASE_URL.$item['image'] ?>" alt="盆栽植物">
            <p><?= $item['name'] ?></p>
            <p><?= $item['quantity'] ?> 件库存</p>
            <p>$<?= $item['price'] ?></p>
        </div>
<?php endforeach; ?>

shop.php视图文件

<input hx-get="<?= BASE_URL ?>/product-search" hx-trigger="load, keyup change delay:500ms" hx-target="#search-results" name="search_value">

<div id="search-results" class="mt-5 row row-cols-1 row-cols-md-4 row-cols-sm-2 g-4">
   <!-- 此处渲染productCard.php片段 -->
</div>

解决方案:单文件多片段模式

将关联的HTML片段集中到单个模板文件内,通过标记区分不同片段,既实现类似模板引擎的块渲染能力,又避免文件泛滥问题。

步骤1:重构模板文件,增加片段区分逻辑

创建productFragments.php,用条件分支区分不同片段:

<?php
switch ($fragment) {
    case 'productCard':
        foreach ($products as $item) : ?>
            <div>
                <img src="<?= BASE_URL.$item['image'] ?>" alt="盆栽植物">
                <p><?= $item['name'] ?></p>
                <p><?= $item['quantity'] ?> 件库存</p>
                <p>$<?= $item['price'] ?></p>
            </div>
        <?php endforeach;
        break;
    case 'notFoundProduct': ?>
        <div class="alert alert-warning">
            未找到匹配的商品
        </div>
        <?php
        break;
    default:
        // 默认输出空内容或错误提示
        break;
}
?>

步骤2:修改renderFragment方法,支持指定片段

更新Controller基类的渲染方法,允许传入片段名称并传递给模板:

protected function renderFragment(string $templatePath, string $fragmentName, array $values = []): void
{
    $values['fragment'] = $fragmentName;
    extract($values);
    require_once __DIR__ . '/../../view/fragments' . $templatePath;
    exit();
}

步骤3:调整控制器中的调用逻辑

修改ProductController.php的searchByName方法:

public function searchByName(): void
{
    if (!isset($_GET['search_value'])) {
        $products = $this->productModel->getAll();
        $this->renderFragment('/productFragments.php', 'productCard', ['products' => $products]);
    }

    $products = $this->productModel->searchByName($_GET['search_value']);

    if (empty($products)) {
        $this->renderFragment('/productFragments.php', 'notFoundProduct');
    } else {
        $this->renderFragment('/productFragments.php', 'productCard', ['products' => $products]);
    }
}

额外优化:主视图中复用片段

如果需要在主视图shop.php中直接渲染片段,可新增includeFragment方法:

// 可放在Controller基类或全局工具类中
public function includeFragment(string $templatePath, string $fragmentName, array $values = []): void
{
    $values['fragment'] = $fragmentName;
    extract($values);
    require __DIR__ . '/../../view/fragments' . $templatePath;
}

在shop.php中调用:

<input hx-get="<?= BASE_URL ?>/product-search" hx-trigger="load, keyup change delay:500ms" hx-target="#search-results" name="search_value">

<div id="search-results" class="mt-5 row row-cols-1 row-cols-md-4 row-cols-sm-2 g-4">
   <?php $this->includeFragment('/productFragments.php', 'productCard', ['products' => $products]); ?>
</div>

方案优势

  • 规避了HTML字符串拼接的维护痛点
  • 关联片段集中存储,减少文件数量,便于统一管理
  • 基于原生PHP实现,无需引入额外依赖,保持项目轻量
  • 支持片段在控制器和视图中复用,符合DRY开发原则

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 18:37:53