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

CodeIgniter 整合 Widget 機制實現頁面佈局複用完整教學

     編輯:PHP 綜合知識

前言

CodeIgniter (CI) 核心輕量,無內建 Widget/元件系統,但透過 `MY_Controller` 擴展與靜態呼叫模式,可輕鬆實現「頁面區塊複用」(如導航欄、側邊欄、頁尾)。本文示範完整整合步驟與最佳實踐。

一、核心架構設計

層級檔案/類別職責
基礎控制器`core/MY_Controller.php`提供 `widget()` 方法載入 Widget 類別
Widget 基礎類`libraries/Widget.php` (或各自類別)封裝區塊邏輯:資料查詢 + 視圖渲染
具體 Widget`widgets/LeftMenu.php` 等特定區塊實作
視圖模板`views/layout/main.php`頁面佈局,呼叫 Widget 靜態方法

二、實作步驟

1. 建立基礎控制器 `application/core/MY_Controller.php`

<?php
class MY_Controller extends CI_Controller {
  protected $widgets = [];

  public function __construct() {
    parent::__construct();
    // 載入常用資源
    $this->load->library('session');
    $this->load->helper(['url', 'html']);
  }

  /**
   * 載入並實例化 Widget
   * @param string $name Widget 類別名稱 (對應 widgets/ 目錄下檔名)
   * @return object Widget 實例
   */
  protected function widget($name) {
    if (empty($name)) return null;
    
    // 單例模式:同一請求只實例化一次
    if (!isset($this->widgets[$name])) {
      $file = APPPATH . "widgets/{$name}.php";
      if (!file_exists($file)) {
        show_error("Widget 不存在: {$name}", 500);
      }
      require_once $file;
      $className = 'Widget_' . $name;
      if (!class_exists($className)) {
        show_error("Widget 類別未定義: {$className}", 500);
      }
      $this->widgets[$name] = new $className($this);
    }
    return $this->widgets[$name];
  }

  // 共用方法:取得登入用戶資訊
  protected function getCurrentUser() {
    return $this->session->userdata('user');
  }
}

2. 建立 Widget 基礎類 `application/libraries/Widget_base.php` (可選)

<?php
abstract class Widget_Base {
  protected $CI;
  
  public function __construct($ci) {
    $this->CI = $ci;  // 持有 CI 超級物件引用
  }
  
  // 便利方法:載入視圖
  protected function view($view, $data = [], $return = false) {
    return $this->CI->load->view($view, $data, $return);
  }
  
  // 便利方法:取得模型
  protected function model($name) {
    $this->CI->load->model($name);
    return $this->CI->$name;
  }
}

3. 實作具體 Widget:`application/widgets/LeftMenu.php`

<?php
class Widget_LeftMenu extends Widget_Base {
  
  public function render($params = []) {
    // 1. 取得資料 (可從快取、模型、服務層)
    $user = $this->CI->getCurrentUser();
    $menuItems = $this->getMenuItems($user);
    
    // 2. 渲染視圖
    return $this->view('widgets/left_menu', [
      'items' => $menuItems,
      'active' => $params['active'] ?? '',
      'user' => $user,
    ], true);  // true = 回傳字串而非直接輸出
  }
  
  private function getMenuItems($user) {
    $items = [
      ['label' => '儀表板', 'url' => 'dashboard', 'icon' => 'home', 'roles' => ['*']],
      ['label' => '文章管理', 'url' => 'articles', 'icon' => 'file', 'roles' => ['admin', 'editor']],
      ['label' => '用戶管理', 'url' => 'users', 'icon' => 'users', 'roles' => ['admin']],
      ['label' => '個人設定', 'url' => 'profile', 'icon' => 'user', 'roles' => ['*']],
    ];
    
    // 依角色過濾
    $role = $user['role'] ?? 'guest';
    return array_filter($items, fn($item) => in_array('*', $item['roles']) || in_array($role, $item['roles']));
  }
}

4. Widget 視圖:`application/views/widgets/left_menu.php`

<nav class="sidebar">
  <ul class="nav flex-column">
    <{foreach $items as $item}>
      <li class="nav-item">
        <a class="nav-link <{if $active == $item.url}>active<{/if}>" 
           href="<{site_url($item.url)}>">
          <i class="fas fa-<{$item.icon}>"></i> <{$item.label}>
        </a>
      </li>
    <{/foreach}>
  </ul>
  
  <{if $user}>
    <div class="sidebar-footer">
      <span><{$user.name}> (<{$user.role}>)</span>
    </div>
  <{/if}>
</nav>

5. 主佈局視圖:`application/views/layout/main.php`

<!DOCTYPE html>
<html lang="zh-TW">
<head>
  <meta charset="UTF-8">
  <title><{$page_title}|默認標題}></title>
  <link href="<{base_url('assets/css/app.css')}>" rel="stylesheet">
</head>
<body class="d-flex">
  <!-- 側邊欄 Widget -->
  <div class="sidebar-wrapper">
    <?= $this->widget('LeftMenu')->render(['active' => $current_page ?? '']) ?>
  </div>
  
  <!-- 主內容區 -->
  <main class="flex-grow-1 p-4">
    <?= $content ?? '' ?>
  </main>
  
  <!-- 頁尾 Widget -->
  <footer class="border-top p-3 text-center text-muted">
    <?= $this->widget('Footer')->render() ?>
  </footer>
</body>
</html>

6. 控制器繼承 MY_Controller 使用佈局

<?php
class Dashboard extends MY_Controller {
  public function index() {
    $this->load->model('Stats_model');
    
    $data = [
      'page_title' => '儀表板',
      'current_page' => 'dashboard',
      'stats' => $this->Stats_model->getSummary(),
    ];
    
    // 使用佈局渲染
    $this->load->view('layout/main', [
      'content' => $this->load->view('dashboard/index', $data, true),
    ]);
  }
}

三、進階:Widget 參數化與快取

class Widget_ArticleList extends Widget_Base {
  public function render($params = []) {
    $cacheKey = 'widget_article_list_' . md5(serialize($params));
    
    // 嘗試從快取讀取
    if ($html = $this->CI->cache->get($cacheKey)) {
      return $html;
    }
    
    // 參數預設值
    $limit = $params['limit'] ?? 10;
    $category = $params['category'] ?? null;
    $template = $params['template'] ?? 'widgets/article_list_default';
    
    // 查詢資料
    $articles = $this->model('Article_model')->getLatest($limit, $category);
    
    // 渲染
    $html = $this->view($template, ['articles' => $articles], true);
    
    // 寫入快取 (5 分鐘)
    $this->CI->cache->save($cacheKey, $html, 300);
    
    return $html;
  }
}

四、與 HMVC 模組化架構比較

特性Widget 模式 (本文)HMVC (Modular Extensions)
複雜度低,輕量級高,完整模組隔離
適用場景頁面區塊複用 (導航、側邊欄、廣告位)功能模組獨立開發 (部落格、商城、論壇)
資料存取透過 CI 實例存取模型/資料庫模組內含完整 MVC
效能單一請求上下文,輕量子請求開銷較大
學習曲線平緩陡峭

五、最佳實踐總結

  1. Widget 只負責「區塊級」複用:導航、側邊欄、頁尾、廣告位、推薦列表
  2. 資料邏輯封裝在 Widget 內部:控制器不需知道 Widget 如何取資料
  3. 視圖回傳字串:`view(..., true)` 讓佈局控制輸出時機
  4. 單例模式避免重複實例化:同一請求中同一 Widget 只建立一次
  5. 善用快取:高頻區塊 (如熱門文章、分類導航) 必須快取
  6. 命名規範:`Widget_{Name}` 類別、`widgets/{name}.php` 檔案、`views/widgets/{name}.php` 視圖

總結

CI 整合 Widget 機制的核心在於:MY_Controller 提供載入入口、Widget 類別封裝「資料+視圖」、佈局視圖靜態呼叫渲染。這套輕量架構零依賴、易理解、效能好,非常適合中小型專案快速構建可維護的頁面佈局系統。

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