前後端分離的核心概念
在傳統的 CI3 開發中,通常由 CI3 的視圖 (View) 直接輸出 HTML,並包含 JavaScript 程式碼。但在前後端分離的模式下:
後端 (CI3):主要負責提供 API 服務(如 RESTful API),處理業務邏輯、資料庫操作、身份驗證等。它只返回資料(通常是 JSON 格式),而不是完整的 HTML 頁面。
前端 (jQuery):負責 使用者介面 (UI) 的呈現和互動。它透過 AJAX 請求從後端取得資料,然後動態地將資料渲染到頁面上。
CI3 後端實作
1. 設定 CI3 專案
確保你已經安裝並設定好 CI3 專案。
2. 建立 API 控制器
建立一個新的控制器來處理 API 請求。例如,我們建立一個 Api.php
控制器:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Api extends CI_Controller {
public function __construct() {
parent::__construct();
// 載入模型或輔助函數
$this->load->model('your_data_model'); // 假設你有一個處理資料的模型
$this->load->helper('url');
}
// 範例:取得所有產品的 API
public function products() {
$products = $this->your_data_model->get_all_products(); // 從模型取得資料
// 設定回應頭為 JSON
header('Content-Type: application/json');
echo json_encode(['status' => 'success', 'data' => $products]);
}
// 範例:新增產品的 API (POST 請求)
public function add_product() {
if ($this->input->method() === 'post') {
$product_name = $this->input->post('name');
$product_price = $this->input->post('price');
if ($product_name && $product_price) {
$data = [
'name' => $product_name,
'price' => $product_price
];
$insert_id = $this->your_data_model->insert_product($data); // 插入資料
header('Content-Type: application/json');
echo json_encode(['status' => 'success', 'message' => '產品新增成功', 'id' => $insert_id]);
} else {
header('Content-Type: application/json');
echo json_encode(['status' => 'error', 'message' => '名稱和價格為必填']);
}
} else {
header('Content-Type: application/json');
echo json_encode(['status' => 'error', 'message' => '只允許 POST 請求']);
}
}
// 範例:處理找不到方法或錯誤的預設回應
public function _remap($method, $params = array()) {
if (method_exists($this, $method)) {
return call_user_func_array(array($this, $method), $params);
} else {
header('Content-Type: application/json');
echo json_encode(['status' => 'error', 'message' => 'API 方法不存在']);
}
}
}
3. 設定路由 (routes.php)
在 application/config/routes.php
中設定 API 的路由,讓 URL 更簡潔。
$route['api/products'] = 'api/products';
$route['api/add_product'] = 'api/add_product';
4. 建立資料模型 (Model)
建立一個模型來處理資料庫操作。例如 Your_data_model.php
:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Your_data_model extends CI_Model {
public function __construct() {
parent::__construct();
$this->load->database();
}
public function get_all_products() {
$query = $this->db->get('products'); // 假設你的產品資料表名為 'products'
return $query->result_array();
}
public function insert_product($data) {
$this->db->insert('products', $data);
return $this->db->insert_id();
}
}
jQuery 前端實作
前端部分會使用 HTML、CSS 和 jQuery 來構建使用者介面,並透過 AJAX 請求與 CI3 後端進行互動。
1. HTML 結構
建立一個 HTML 檔案(例如 index.html
或在 CI3 的 views
資料夾下建立一個簡單的視圖 frontend_view.php
,但不渲染資料):
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>產品列表 - 前後端分離</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
#product-list { margin-top: 20px; border: 1px solid #ccc; padding: 10px; }
.product-item { margin-bottom: 5px; padding: 5px; background-color: #f9f9f9; }
form { margin-top: 20px; padding: 10px; border: 1px solid #eee; }
input[type="text"], input[type="number"] { padding: 8px; margin-right: 10px; }
button { padding: 8px 15px; background-color: #007bff; color: white; border: none; cursor: pointer; }
button:hover { background-color: #0056b3; }
.message { margin-top: 10px; padding: 10px; border-radius: 5px; }
.message.success { background-color: #d4edda; color: #155724; border-color: #c3e6cb; }
.message.error { background-color: #f8d7da; color: #721c24; border-color: #f5c6cb; }
</style>
</head>
<body>
<h1>產品管理系統 (前後端分離)</h1>
<h2>新增產品</h2>
<form id="add-product-form">
<label for="product-name">產品名稱:</label>
<input type="text" id="product-name" name="name" required>
<label for="product-price">產品價格:</label>
<input type="number" id="product-price" name="price" step="0.01" required>
<button type="submit">新增產品</button>
<div id="add-product-message" class="message" style="display: none;"></div>
</form>
<h2>產品列表</h2>
<div id="product-list">
載入中...
</div>
<script>
$(document).ready(function() {
const baseUrl = '<?php echo base_url(); ?>'; // 假設前端頁面是由 CI3 載入
// 函式:載入產品列表
function loadProducts() {
$.ajax({
url: baseUrl + 'api/products', // CI3 後端 API 網址
method: 'GET',
dataType: 'json', // 預期回應為 JSON 格式
success: function(response) {
$('#product-list').empty(); // 清空現有列表
if (response.status === 'success' && response.data.length > 0) {
$.each(response.data, function(index, product) {
$('#product-list').append(
'<div class="product-item">' +
'ID: ' + product.id + ', ' +
'名稱: ' + product.name + ', ' +
'價格: $' + product.price +
'</div>'
);
});
} else if (response.status === 'success' && response.data.length === 0) {
$('#product-list').append('<p>目前沒有產品。</p>');
} else {
$('#product-list').append('<p>載入產品失敗: ' + response.message + '</p>');
console.error('API Error:', response.message);
}
},
error: function(xhr, status, error) {
$('#product-list').empty().append('<p>與後端通訊失敗,請檢查網路或伺服器。</p>');
console.error('AJAX Error:', status, error, xhr.responseText);
}
});
}
// 初始載入產品
loadProducts();
// 表單提交事件:新增產品
$('#add-product-form').submit(function(e) {
e.preventDefault(); // 阻止表單預設提交行為
const productName = $('#product-name').val();
const productPrice = $('#product-price').val();
const messageDiv = $('#add-product-message');
messageDiv.hide().removeClass('success error').text(''); // 清空並隱藏訊息
$.ajax({
url: baseUrl + 'api/add_product', // 新增產品 API 網址
method: 'POST',
dataType: 'json',
data: {
name: productName,
price: productPrice
},
success: function(response) {
if (response.status === 'success') {
messageDiv.addClass('success').text(response.message).fadeIn();
$('#add-product-form')[0].reset(); // 清空表單
loadProducts(); // 重新載入產品列表
} else {
messageDiv.addClass('error').text(response.message).fadeIn();
}
},
error: function(xhr, status, error) {
messageDiv.addClass('error').text('新增產品失敗,請檢查網路或伺服器。').fadeIn();
console.error('AJAX Error:', status, error, xhr.responseText);
}
});
});
});
</script>
</body>
</html>
注意事項:
如果你的前端頁面不是由 CI3 控制器載入,則
const baseUrl = '<?php echo base_url(); ?>';
這行需要替換為你的 CI3 專案的實際根 URL,例如const baseUrl = 'http://localhost/your_ci3_project/';
。你需要確保 CI3 專案能夠正確處理 AJAX 請求。如果遇到 CORS (Cross-Origin Resource Sharing) 問題,你需要在 CI3 後端配置允許跨域請求。
部署與測試
資料庫設定:在 CI3 的
application/config/database.php
中設定你的資料庫連接。建立資料表:在你的資料庫中建立一個
products
表格:SQLCREATE TABLE products ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, price DECIMAL(10, 2) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
運行 CI3 專案:透過 Apache 或 Nginx 配置你的 CI3 專案。
訪問前端頁面:
如果你把 HTML 頁面放在 CI3 的
views
資料夾下,並透過 CI3 控制器載入(例如application/controllers/Welcome.php
中的index()
方法載入frontend_view
),那麼訪問http://localhost/your_ci3_project/
或對應的 URL。如果你把 HTML 頁面獨立放置,則直接打開該 HTML 檔案,但需注意
baseUrl
的設定。
優點與考量
優點:
職責分離:前後端開發人員可以獨立工作,互不影響。
提高開發效率:前端和後端可以並行開發。
更好的擴展性:後端 API 可以為多個前端應用(如 Web、Mobile App)提供服務。
彈性:前端可以使用更現代的框架 (如 React, Vue, Angular) 而不只是 jQuery。
效能優化:前端可以實現更細粒度的頁面更新,減少完整頁面載入。
考量:
CORS 問題:如果前後端部署在不同的域名或埠,可能會遇到跨域問題,需要在後端進行配置。
路由管理:需要為後端 API 設計清晰的路由。
狀態管理:在純前端應用中,需要考慮前端的狀態管理(例如使用瀏覽器儲存或更複雜的狀態管理庫)。
SEO 挑戰:對於爬蟲不支援 JavaScript 的情況,動態載入的內容可能對 SEO 不利。可以考慮 SSR (Server-Side Rendering) 或預渲染。
安全性:需要確保 API 的安全性,例如身份驗證、授權、防止 XSS 和 CSRF 攻擊。
進一步的優化與學習
API 版本控制:為 API 引入版本控制(例如
/api/v1/products
)。身份驗證與授權:可以使用 JWT (JSON Web Tokens) 或其他基於 Token 的驗證機制。
錯誤處理:定義統一的 API 錯誤回應格式。
表單驗證:在前端和後端都進行資料驗證。
安全性:實作輸入過濾、輸出編碼等防止常見 Web 攻擊的措施。
前端框架:考慮使用更現代的前端框架(React, Vue, Angular)來取代 jQuery,它們提供了更強大的組件化、狀態管理和開發工具,更適合大型專案。
透過上述步驟,你就可以在 PHP CI3 專案中成功實現前後端分離,並透過 jQuery 進行前端互動。
沒有留言:
張貼留言