首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

Smarty 3 開發自訂插件 {load_data} 實現模板端直接載入資料庫資料

     編輯:PHP 相關知識

傳統 MVC 模式下,控制器需預先查詢資料庫並 assign 給模板,區塊多時控制器代碼冗長且難維護。本文示範開發 Smarty 3 函數插件 {load_data},讓模板直接宣告資料來源(表名、條件、限制),自動查詢並指派變數,配合 {foreach} 即時輸出。

一、插件檔案結構

建立 plugins/function.load_data.php 放置於 Smarty plugins 目錄(預設 libs/plugins/ 或專案自訂目錄)。

二、完整插件代碼

<?php
/**
 * Smarty {load_data} 函數插件
 * 用法:{load_data assign="變數名" table="表名" where="條件" limit="筆數" class="模型類別"}
 */
function smarty_function_load_data($params, &$smarty) {
    // 1. 解析參數
    $class = (!isset($params['class']) || empty($params['class'])) ? 'cls_crud' : trim($params['class']);
    if (!isset($params['table']) || empty($params['table'])) {
        trigger_error('{load_data}: `table` attribute is required', E_USER_ERROR);
        return;
    }

    // 2. 實例化模型(假設採用工廠模式)
    $db = $class::factory(array('table' => $params['table']));

    // 3. 執行查詢
    $where = isset($params['where']) ? $params['where'] : '';
    $limit = isset($params['limit']) ? $params['limit'] : '';
    $data = $db->get_block_list(array($where), $limit);

    // 4. 指派給模板變數
    if (!empty($params['assign'])) {
        $smarty->assign($params['assign'], $data);
    }
    // 若未指定 assign,可選擇直接輸出或忽略
}
?>

三、模板使用範例

{* 載入商品列表,指派給 $products *}
{load_data assign="products" table="products" where="`status`=1" limit=20 class="ProductModel"}

{foreach from=$products item=product}
    <div class="product-card">
        <h3>{$product.name}</h3>
        <span class="price">{$product.price|number_format}</span>
    </div>
{/foreach}

{* 載入最新消息,直接在區塊中迴圈 *}
{load_data assign="news" table="news" where="`pub_date`<='{date("Y-m-d")}'" limit=5}
{foreach from=$news item=n}
    <a href="/news/{$n.id}">{$n.title}</a><br>
{/foreach}

四、進階功能擴充

1. 支援排序、欄位選取、關聯查詢

$order = isset($params['order']) ? $params['order'] : '';
$fields = isset($params['fields']) ? $params['fields'] : '*';
$join = isset($params['join']) ? $params['join'] : '';

$data = $db->get_block_list(array($where), $limit, $order, $fields, $join);

2. 統一資料格式化/過濾(插件層集中處理)

// 例如:敏感詞過濾、日期格式化、金額分轉元
foreach ($data as &$row) {
    $row['content'] = sensitive_filter($row['content']);
    $row['created_at'] = date('Y-m-d H:i', strtotime($row['created_at']));
    $row['price'] = $row['price'] / 100;
}

3. 快取支援

$cache_id = md5(serialize($params));
if ($smarty->caching && $cached = $smarty->getCachedTemplate($cache_id)) {
    $data = $cached;
} else {
    $data = $db->get_block_list(...);
    if ($smarty->caching) $smarty->saveCachedTemplate($cache_id, $data);
}

五、模型層配合介面(cls_crud 參考)

<?php
class cls_crud {
    protected $table;

    public static function factory($config) {
        $instance = new static();
        $instance->table = $config['table'];
        return $instance;
    }

    // 回傳二維陣列
    public function get_block_list($where = array(), $limit = '', $order = '', $fields = '*', $join = '') {
        $sql = "SELECT {$fields} FROM `{$this->table}`";
        if ($join) $sql .= " {$join}";
        if ($where) $sql .= " WHERE " . implode(' AND ', $where);
        if ($order) $sql .= " ORDER BY {$order}";
        if ($limit) $sql .= " LIMIT {$limit}";

        // 使用 PDO 或資料庫抽象層執行
        return $this->db->query($sql)->fetchAll(PDO::FETCH_ASSOC);
    }
}
?>

六、優缺點權衡與替代方案

優點缺點/風險
控制器極度精簡,區塊獨立維護模板包含商業邏輯(SQL 條件),違反嚴格 MVC
區塊複用性高,不同頁面直接引用SQL 注入風險:where 參數直接拼接,需嚴格過濾或參數化
前端人員可自行調整查詢條件難以進行單元測試、效能分析
減少控制器與模型的樣板代碼資料庫連線數可能失控(每個 {load_data} 獨立查詢)

七、建議替代架構:View Model / Presenter

大型專案建議:

  1. 控制器建立 ViewModel 物件,聚合該頁面所需所有資料。
  2. ViewModel 封裝查詢邏輯、快取、權限檢查。
  3. 模板僅負責呈現,assign 整個 ViewModel 或陣列。
  4. 若需區塊複用,使用 {include} 子模板 + {function} 區塊函數。

八、安全檢查清單(上線前必做)

  • [ ] where 參數禁止直接拼接 SQL,改用參數綁定或白名單欄位/運算子。
  • [ ] table、class 參數嚴格白名單驗證,防止任意表讀取/類別實例化。
  • [ ] 加入查詢結果筆數上限(如 max(limit, 1000)),防止撈全表導致 OOM。
  • [ ] 開啟 Smarty security_policy,限制可用修飾器與 PHP 函數。
  • [ ] 記錄慢查詢日誌,定期審視模板產生的 SQL。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved