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

CodeIgniter 3 整合 Widget 元件實現頁面區塊復用:MY_Controller 擴展與靜態呼叫雙模式

     編輯:PHP 相關知識

背景動機

專案中多頁面共用「側邊欄導航」、「用戶資訊卡」、「最新公告」等區塊。若每個控制器重複查詢資料並載入視圖,違反 DRY 原則且難以維護。Widget 模式將「資料獲取 + 視圖渲染」封裝為獨立類別,視圖中直接呼叫,實現區塊級復用。

架構設計

application/
├── core/
│   └── MY_Controller.php          # 擴展基礎控制器,提供 widget() 方法
├── libraries/
│   └── Widget.php                 # Widget 基礎類 (可選)
├── widgets/                       # Widget 類別目錄 (自定義)
│   ├── LeftSidebar.php
│   ├── UserCard.php
│   └── Announcement.php
└── views/
    ├── partials/
    │   ├── left_sidebar.php
    │   ├── user_card.php
    │   └── announcement.php
    └── layouts/
        └── main.php

實作步驟

1. 擴展 MY_Controller 提供 widget() 載入器

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class MY_Controller extends CI_Controller {
    protected $widget_namespace = 'App\\Widgets\\'; // 命名空間前綴

    public function __construct() {
        parent::__construct();
    }

    /**
     * 載入並實例化 Widget
     * @param string $name Widget 類別名 (不含命名空間)
     * @param array $params 傳遞給 Widget 建構子的參數
     * @return object Widget 實例
     */
    public function widget(string $name, array $params = []) {
        $class = $this->widget_namespace . ucfirst($name);
        if (!class_exists($class)) {
            // 相容無命名空間寫法
            $class = 'Widget_' . ucfirst($name);
            if (!class_exists($class)) {
                show_error("Widget [$name] not found: $class");
            }
        }
        return new $class($this, ...$params); // 注入 CI 實例
    }

    // 靜態代理方法 (供視圖中直接呼叫 Widget::left())
    public static function run_widget(string $name, array $params = []) {
        $ci =& get_instance();
        return $ci->widget($name, $params)->render();
    }
}

2. 定義 Widget 基礎類 (可選,提供通用方法)

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

abstract class Widget {
    protected $ci;
    protected $data = [];

    public function __construct(CI_Controller $ci) {
        $this->ci = $ci;
    }

    protected function load_view(string $view, array $data = [], bool $return = false) {
        return $this->ci->load->view($view, array_merge($this->data, $data), $return);
    }

    abstract public function render(): string;
}

3. 實作具體 Widget:側邊欄

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

namespace App\Widgets; // 需 PHP 5.6+ 且 composer.json 設定 psr-4 自動載入
// 或類名 Widget_LeftSidebar 放在 libraries/widgets/

class LeftSidebar extends Widget {
    public function render(): string {
        // 1. 獲取資料 (可快取)
        $cache_key = 'widget:left_sidebar:v1';
        $menu = $this->ci->cache->get($cache_key);
        if ($menu === false) {
            $menu = $this->ci->db->select('id, title, url, icon, sort')
                                 ->where('status', 1)
                                 ->order_by('sort', 'asc')
                                 ->get('nav_menu')
                                 ->result_array();
            $this->ci->cache->save($cache_key, $menu, 3600);
        }

        // 2. 獲取用戶資訊 (若登入)
        $user = $this->ci->session->userdata('user');

        // 3. 組裝資料
        $this->data = [
            'menu_items' => $menu,
            'current_user' => $user,
            'active_url' => $this->ci->uri->uri_string(),
        ];

        // 4. 渲染視圖並回傳字串
        return $this->load_view('partials/left_sidebar', [], true);
    }
}

4. 視圖檔 application/views/partials/left_sidebar.php

<aside class="sidebar">
    <div class="user-panel">
        <?php if ($current_user): ?>
            <img src="<?= base_url($current_user['avatar'] ?? 'assets/img/default_avatar.png') ?>" alt="Avatar">
            <span><?= html_escape($current_user['username']) ?></span>
        <?php else: ?>
            <a href="<?= site_url('login') ?>">登入</a> / <a href="<?= site_url('register') ?>">註冊</a>
        <?php endif; ?>
    </div>

    <nav class="menu">
        <ul>
            <?php foreach ($menu_items as $item): ?>
                <li class="<?= ($active_url === $item['url']) ? 'active' : '' ?>">
                    <a href="<?= site_url($item['url']) ?>">
                        <?php if ($item['icon']): ?><i class="<?= $item['icon'] ?>"></i><?php endif; ?>
                        <span><?= html_escape($item['title']) ?></span>
                    </a>
                </li>
            <?php endforeach; ?>
        </ul>
    </nav>
</aside>

5. 佈局檔中呼叫 (兩種模式)

模式 A:控制器預載入,視圖直接輸出 (推薦,效能較好)

<?php
// 控制器
class Home extends MY_Controller {
    public function index() {
        // 預載入 widget 實例到視圖變數
        $this->data['left_sidebar'] = $this->widget('LeftSidebar')->render();
        $this->load->view('layouts/main', $this->data);
    }
}
<?php
// 佈局 views/layouts/main.php
<div class="wrapper">
    <div class="sidebar-col">
        <?= $left_sidebar ?> <!-- 直接輸出預渲染字串 -->
    </div>
    <div class="content-col">
        <?php $this->load->view($content_view); ?>
    </div>
</div>

模式 B:視圖中靜態呼叫 (彈性高,但每次呼叫實例化)

<?php
// 佈局 views/layouts/main.php
<div class="wrapper">
    <div class="sidebar-col">
        <?= MY_Controller::run_widget('LeftSidebar') ?>
    </div>
    <div class="content-col">
        <?php $this->load->view($content_view); ?>
    </div>
</div>

進階:Widget 參數化與快取策略

// 控制器傳參
$this->widget('Announcement', ['limit' => 5, 'category' => 'news'])

// Widget 內部使用參數
class Announcement extends Widget {
    private $limit;
    private $category;
    public function __construct(CI_Controller $ci, array $params = []) {
        parent::__construct($ci);
        $this->limit = $params['limit'] ?? 3;
        $this->category = $params['category'] ?? 'all';
    }
    public function render(): string {
        $cache_key = "widget:announcement:{$this->category}:{$this->limit}";
        // ... 同前
    }
}

效能優化建議

  1. 快取 Widget 輸出 HTML:$this->ci->cache->save($key, $html, 300),避免重複查詢與渲染。
  2. 控制器預載入模式:單次請求中同一 Widget 只實例化一次,視圖直接輸出字串。
  3. 資料查詢用 Query Builder 選取必要欄位:避免 SELECT *。
  4. 避免 Widget 間相互依賴:保持單一職責,資料從 Model/Service 獲取。

與 HMVC (Modular Extensions) 比較

特性Widget 模式HMVC (modules::run)
架構複雜度低 (單一類別)高 (完整 MVC 模組)
資料隔離共享主請求 CI 實例獨立請求上下文
效能極高 (無額外路由分發)較低 (模擬內部請求)
適用場景UI 區塊復用 (側邊欄、導航、卡片)業務模組復用 (完整功能頁面)
學習成本低中

總結

CodeIgniter 3 無內建 Widget 機制,但透過 **MY_Controller 擴展 + 獨立 Widget 類別 + 視圖部分檔** 輕鬆實現區塊級復用。核心優勢:零侵入、效能高、符合單一職責、易於單元測試。建議採用「控制器預載入 + 視圖輸出字串」模式作為預設,複雜參數化場景再使用靜態呼叫。配合檔案/Redis 快取,可支撐高併發下的頁面組裝需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved