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

CodeIgniter 控制器繼承架構設計:MY_Controller、AdminBase、HomeBase 多層次實踐

     編輯:PHP 綜合知識

前言:為什麼需要分層基礎控制器?

隨著專案增長,控制器間會出現大量共同邏輯:權限檢查、語系初始化、模板載入、資料準備、日誌記錄... 若每個控制器重複撰寫,維護成本極高。CodeIgniter 透過 MY_Controller 機制支援多層次繼承,本文示範標準三層架構設計。

三層繼承架構

CI_Controller (核心)
    │
    ▼
MY_Controller (應用通用基礎)
    │
    ├──────────────┬──────────────┐
    ▼              ▼              ▼
AdminBase      FrontBase       ApiBase
(後台基類)      (前台基類)       (API基類)
    │              │              │
    ▼              ▼              ▼
Admin_*       Front_*         Api_*
控制器         控制器          控制器

逐層實作詳解

第 1 層:MY_Controller - 應用通用基礎

application/core/MY_Controller.php

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

class MY_Controller extends CI_Controller {
    protected $data = [];           // 視圖共用資料
    protected $current_user = null; // 當前登入用戶
    
    public function __construct() {
        parent::__construct();
        
        // 1. 載入通用輔助函數/類庫
        $this->load->helper(['url', 'form', 'html', 'date', 'text']);
        $this->load->library(['session', 'pagination']);
        
        // 2. 多語系初始化
        $lang = $this->session->userdata('lang') ?: config_item('language');
        $this->config->set_item('language', $lang);
        $this->lang->load('common', $lang);
        
        // 3. 共用視圖資料
        $this->data['site_name'] = config_item('site_name');
        $this->data['current_url'] = current_url();
        $this->data['csrf_token'] = $this->security->get_csrf_hash();
        
        // 4. 記錄請求日誌 (開發環境)
        if (ENVIRONMENT === 'development') {
            log_message('debug', 'Request: ' . $this->input->server('REQUEST_METHOD') . ' ' . current_url());
        }
        
        // 5. 註冊關閉函數:清理資源、記錄回應時間
        register_shutdown_function([$this, '_shutdown']);
    }
    
    /** 統一視圖渲染入口 */
    protected function _render($view, $data = [], $layout = 'main') {
        $data = array_merge($this->data, $data);
        $data['content_view'] = $view;
        $data['content_data'] = $data;
        $this->load->view("layouts/{$layout}", $data);
    }
    
    /** 輸出 JSON 回應 */
    protected function _json($data, $status = 200) {
        $this->output
            ->set_status_header($status)
            ->set_content_type('application/json', 'utf-8')
            ->set_output(json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
    }
    
    /** 關閉處理 */
    public function _shutdown() {
        // 可在此記錄慢查詢、回應時間、釋放鎖等
    }
}

第 2 層:AdminBase - 後台管理基類

application/core/AdminBase.php

class AdminBase extends MY_Controller {
    protected $admin_user = null;
    protected $permissions = [];
    
    public function __construct() {
        parent::__construct();
        
        // 1. 強制 HTTPS (生產環境)
        if (ENVIRONMENT === 'production' && !$this->input->is_https_request()) {
            $this->_json(['error' => 'HTTPS required'], 403);
            exit;
        }
        
        // 2. 管理員登入驗證
        $this->_auth_check();
        
        // 3. 權限載入
        $this->_load_permissions();
        
        // 4. 後台專用視圖資料
        $this->data['admin_user'] = $this->admin_user;
        $this->data['menu_active'] = $this->router->fetch_class(); // 側邊欄高亮
        $this->data['breadcrumbs'] = $this->_build_breadcrumbs();
    }
    
    /** 登入驗證核心 */
    private function _auth_check() {
        $admin_id = $this->session->userdata('admin_id');
        if (!$admin_id) {
            // AJAX 請求回傳 401,普通請求導向登入頁
            if ($this->input->is_ajax_request()) {
                $this->_json(['error' => 'Unauthorized', 'redirect' => site_url('admin/login')], 401);
            } else {
                $this->session->set_userdata('login_redirect', current_url());
                redirect('admin/login');
            }
            exit;
        }
        
        $this->admin_user = $this->Admin_model->get_by_id($admin_id);
        if (!$this->admin_user || $this->admin_user->status !== 1) {
            $this->session->sess_destroy();
            redirect('admin/login');
        }
        
        // 更新最後活動時間
        $this->Admin_model->update_last_activity($admin_id);
    }
    
    /** 載入權限清單 */
    private function _load_permissions() {
        $this->permissions = $this->Admin_model->get_permissions($this->admin_user->role_id);
        $this->data['permissions'] = $this->permissions;
    }
    
    /** 權限檢查助手 */
    protected function _check_perm($perm_key) {
        if (!in_array($perm_key, $this->permissions)) {
            if ($this->input->is_ajax_request()) {
                $this->_json(['error' => 'Permission denied'], 403);
            } else {
                show_error('權限不足:' . $perm_key, 403);
            }
            exit;
        }
    }
    
    /** 建立麵包屑導航 */
    private function _build_breadcrumbs() {
        $class = $this->router->fetch_class();
        $method = $this->router->fetch_method();
        $crumbs = [['title' => '首頁', 'url' => site_url('admin/dashboard')]];
        
        // 可根據路由映射標題
        $titles = [
            'users' => '會員管理',
            'orders' => '訂單管理',
            'products' => '商品管理',
        ];
        if (isset($titles[$class])) {
            $crumbs[] = ['title' => $titles[$class], 'url' => site_url("admin/{$class}")];
        }
        return $crumbs;
    }
    
    /** 後台統一佈局 */
    protected function _render($view, $data = []) {
        parent::_render($view, $data, 'admin'); // 使用 layouts/admin.php
    }
}

第 2 層:FrontBase - 前台網站基類

application/core/FrontBase.php

class FrontBase extends MY_Controller {
    public function __construct() {
        parent::__construct();
        
        // 1. 前台專用載入
        $this->load->model(['Product_model', 'Category_model', 'Cart_model']);
        $this->load->library('cart');
        
        // 2. 會員登入狀態 (非強制)
        $this->_load_member();
        
        // 3. 購物車/收藏數量
        $this->data['cart_count'] = $this->Cart_model->get_count($this->member_id ?? 0);
        $this->data['wishlist_count'] = $this->Wishlist_model->get_count($this->member_id ?? 0);
        
        // 4. SEO 基礎資料
        $this->data['seo'] = [
            'title' => config_item('site_name'),
            'description' => config_item('site_description'),
            'keywords' => config_item('site_keywords'),
            'og_image' => base_url('assets/images/og-default.jpg'),
        ];
    }
    
    private function _load_member() {
        $member_id = $this->session->userdata('member_id');
        if ($member_id) {
            $this->member = $this->Member_model->get_by_id($member_id);
            $this->data['member'] = $this->member;
        }
    }
    
    /** 前台統一佈局 */
    protected function _render($view, $data = []) {
        parent::_render($view, $data, 'front'); // 使用 layouts/front.php
    }
}

第 2 層:ApiBase - API 介面基類

application/core/ApiBase.php

class ApiBase extends MY_Controller {
    protected $api_user = null;
    
    public function __construct() {
        parent::__construct();
        
        // 1. 關閉 CSRF、視圖、佈局
        $this->config->set_item('csrf_protection', FALSE);
        
        // 2. 統一 JSON 輸出
        $this->output->set_content_type('application/json', 'utf-8');
        
        // 3. API 版本控制 (從 Header 或 URL)
        $this->api_version = $this->input->get_request_header('X-API-Version') ?: 'v1';
        
        // 4. 簽名驗證 / Token 驗證
        $this->_auth_check();
        
        // 5. 速率限制
        $this->_rate_limit();
    }
    
    private function _auth_check() {
        $token = $this->input->get_request_header('Authorization');
        if ($token && strpos($token, 'Bearer ') === 0) {
            $token = substr($token, 7);
            $this->api_user = $this->Api_token_model->validate($token);
        }
        
        // 公開 API 可不強制驗證,由具體控制器決定
    }
    
    private function _rate_limit() {
        $key = 'api_rate:' . ($this->api_user->id ?? $this->input->ip_address());
        $count = $this->cache->increment($key);
        if ($count === 1) {
            $this->cache->save($key, 1, 60); // 1 分鐘視窗
        }
        if ($count > 100) { // 每分鐘 100 次
            $this->_json(['error' => 'Rate limit exceeded'], 429);
            exit;
        }
    }
    
    /** 成功回應 */
    protected function _success($data = [], $message = 'success') {
        $this->_json(['code' => 0, 'message' => $message, 'data' => $data]);
    }
    
    /** 失敗回應 */
    protected function _error($message, $code = 400, $data = []) {
        $this->_json(['code' => $code, 'message' => $message, 'data' => $data], $code);
    }
}

第 3 層:具體控制器繼承範例

// 後台:application/controllers/admin/Users.php
class Users extends AdminBase {
    public function __construct() {
        parent::__construct();
        $this->_check_perm('users.view'); // 權限檢查
        $this->load->model('User_model');
    }
    
    public function index() {
        $this->_check_perm('users.list');
        $page = (int)$this->input->get('page') ?: 1;
        $per_page = 20;
        $users = $this->User_model->get_paginated($page, $per_page);
        $this->data['users'] = $users;
        $this->_render('admin/users/index');
    }
    
    public function edit($id) {
        $this->_check_perm('users.edit');
        // ...
    }
}

// 前台:application/controllers/Product.php
class Product extends FrontBase {
    public function detail($id) {
        $product = $this->Product_model->get_detail($id);
        if (!$product) show_404();
        
        $this->data['product'] = $product;
        $this->data['seo']['title'] = $product->name . ' - ' . config_item('site_name');
        $this->data['seo']['og_image'] = $product->image_url;
        
        $this->_render('product/detail');
    }
}

// API:application/controllers/api/v1/Orders.php
class Orders extends ApiBase {
    public function index() {
        if (!$this->api_user) return $this->_error('Unauthorized', 401);
        
        $orders = $this->Order_model->get_by_user($this->api_user->id);
        $this->_success($orders);
    }
}

目錄結構建議

application/
├── core/
│   ├── MY_Controller.php      # 第1層:通用基礎
│   ├── AdminBase.php          # 第2層:後台基類
│   ├── FrontBase.php          # 第2層:前台基類
│   ├── ApiBase.php            # 第2層:API基類
│   └── MY_Model.php           # 模型基類
├── controllers/
│   ├── admin/                 # 後台控制器 (繼承 AdminBase)
│   │   ├── Dashboard.php
│   │   ├── Users.php
│   │   └── Login.php          # 登入頁不需繼承 AdminBase
│   ├── front/                 # 前台控制器 (繼承 FrontBase)
│   │   ├── Home.php
│   │   ├── Product.php
│   │   └── Cart.php
│   └── api/                   # API 控制器 (繼承 ApiBase)
│       └── v1/
│           ├── Orders.php
│           └── Products.php
└── views/
    ├── layouts/
    │   ├── main.php           # 通用佈局
    │   ├── admin.php          # 後台佈局
│   │   └── front.php          # 前台佈局
    ├── admin/
    ├── front/
    └── errors/

關鍵設計原則總結

原則實踐
單一職責分層MY_Controller 只做通用初始化,業務邏輯下沉到具體基類
組合優於繼承複雜共用邏輯 (如活動檢查、優惠計算) 做成 Service/Library 注入,而非繼承
建構子鏈完整每層 parent::__construct() 必須呼叫,否則父類初始化失效
登入頁/公開頁不繼承基類Login、Register、Password Reset 直接繼承 CI_Controller 或 MY_Controller,避免無限重導
視圖資料標準化統一使用 $this->data 陣列,_render() 合併資料,避免變數散落

總結

CodeIgniter 控制器繼承架構的核心在於:**MY_Controller 奠定通用基礎,業務基類 (AdminBase/FrontBase/ApiBase) 封裝領域邏輯,具體控制器專注業務流程**。三層架構清晰分離關注點,新增功能只需決定繼承哪個基類,自動獲得對應的權限、佈局、驗證、速率限制等能力,大幅降低重複代碼與維護成本。

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