使用 CodeIgniter 開發動態網站
原文網址: http://www.ibm.com/developerworks/opensource/library/os-codeigniter/
副標: 使用 MVC framework 和 CRUD 來提昇效率
摘要: 學習如何使用CodeIgniter快速高效地編寫一個動態的網站。你將能夠利用CodeIgniter的框架和內建的 shortcut 編寫所需的MVC程式,藉以對資料庫進行新增、讀取、更新、和刪除 (Create, Read, Update, and Delete, 簡稱 CRUD) 的資料存取。
常用縮寫
- CRUD: Create, Read, Update, Delete 新增、讀取、更新、刪除
- HTML: Hypertext Markup Language
- MVC: Model-View-Controller
- SQL: Structured Query Language
- UI: User interface 使用者界面
CodeIgniter 是一個以 PHP 開發的開源的網頁應用程式框架。它可以配合許多資料庫應用程式使用,包括 MySQL、DB2 Express-C、或者其他資料庫。這個框架使用 MVC 設計模式,其主要目的是要將應用程式的資料層 (data layer) 和展示層 (presentation layer) 分割開來。在 Model-View-Controller (模型 - 視圖 - 控制器) (MVC) 模式中, model 管理資料層,負責與資料庫溝通;view 管理展示層,顯示的使用者界面 (UI) 和內容;至於 controller 則是管理 view 和 model 之間的溝通。
本文概述了使用 CodeIgniter 建立一個動態的 Web 應用程式的基礎所必需的步驟。在此假設你已安裝 2.0.1 版或更高版本的 CodeIgniter 以及 4.1 版或更高版本的 MySQL,並且對這兩者也已有基本的瞭解。另外,因為新版的 CodeIgniter 也支援 PHP 的 sqlsrv driver,所以此程式也可和 Microsoft 的 SQL Server 一起正常運作。
網站基礎: Widget 和 CRUD
當然,大部分的動態網站都會和這篇文章中所介紹的例子有所差異 — 在大多數的情況下,是截然不同。然而,所有的動態網站都有兩個共同的關鍵性構件:資料庫和由資料庫 (動態) 存取的資料。 為了概括所討論的主題,我把這個動態存取的資料稱為 widget。所課的 widget 可以是各種不同的事物 — 銷售的書籍、配方、博客的條目、或新聞稿。不管這個 widget 是什麼,它的定義必需是一組具有一致性的資訊。例如,一個配方所需的一組資訊可能包括標題、配料、指示、和 nutritional breakdown。
要從資料庫中讀取一個 widget,你必須先建立它 (然後它可能會被更新或者甚至被刪除)。這就是 CRUD 存在的原因。CRUD 代表管理資料庫中的 widget 所需的四個主要的操作;連同 widget 本身,它們就是任何動態網站的基礎。
設定資料庫
在這篇文章中,你將建立一個 Web 應用程式,用來管理為某個團體的學生和家長的聯絡資訊,例如孩子的運動隊,YMCA的小組、或是學校的班級,這是你將要建立的例子。
首先,替這 widget 建立 data model。這個 widget 所需的資訊如下:
- 學生姓名 Student's name
- 家長姓名 Parents' names
- 住址 Address
- 城巿 City
- 州 State
- 郵遞區號 ZIP or postal code
- 電話 Phone number
- E-mail address
為了儲存這個 widget,建立一個叫 student 的 table,其欄位對應於上面的列表中的資訊。建立 MySQL 的資料庫和table 的 script 如 Listing 1所示。
Listing 1. 建立 MySQL 的資料庫和 table 的 script
CREATE DATABASE classroom;
USE classroom;
CREATE TABLE IF NOT EXISTS `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`s_name` varchar(64) DEFAULT NULL,
`p_name` varchar(64) DEFAULT NULL,
`address` varchar(128) DEFAULT NULL,
`city` varchar(32) DEFAULT NULL,
`state` char(2) DEFAULT NULL,
`zip` char(10) DEFAULT NULL,
`phone` char(20) DEFAULT NULL,
`email` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CodeIgniter 的啟始設定
在建立了資料庫和 table 之後,要對 CodeIgniter 做一些設定,包括 .\application\config\database.php 中的資料庫設定以及 .\application\config\config.php 中的 base URL。針對這篇文章的目的而言,我假設 base URL 是 http://127.0.0.1/codeigniter/。
預設的 controller 和 view
接下來,建立一個預設的 controller 和 view。(當這些都設定好,你就能看到你所寫的任何程式碼的效果了。)對於這個 project,在 .\application\controllers\ 目錄下建立一個叫作 student.php 的 controller,如 Listing 2所示,並且在 .\application\config\routes.php 將其設定為預設的 controller 。
Listing 2. 預設的 controller: Student
<?php
class Student extends CI_Controller {
function __construct()
{
parent::__construct();
}
function index()
{
// display information for the view
$data['title'] = "Classroom: Home Page";
$data['headline'] = "Welcome to the Classroom Management System";
$data['include'] = 'student_index';
$this->load->view('template', $data);
}
}
/* End of file student.php */
/* Location: ./application/controllers/student.php */
請注意,在 index()
function 中建立了一個叫作 data 的 array,其中包含三個具名的 index:title
、headline
、和 include
。這個 array 被傳遞給稱為 template 的 view,其為存放 .\application\views\ 目錄下的 template.php (請參見 Listing 3)。
Listing 3. 預設的 view: template.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title><?php echo $title;?></title>
</head>
<body>
<h1><?php echo $headline;?></h1>
<?php $this->load->view($include);?>
</body>
</html>
這個 view 是此網站所有網頁的 HTML 外殼 (shell) 或視覺外包 (visual wrapper)。它包含了標準的 HTML 設置和接受來自 controller 的三個參數 — title、
headline
、及 include
— 其分別對應於頁面的title、頁面標頭、和 view 的檔名,其中要顯示的內容包含在 view 中。
建立要 include 進來的 view 檔案是初始安裝的最後一個步驟。將它命名為 student_index.php,就是先前在template.php 中宣告的 (如上面的 Listing 2 所示),並將其存放在 .\application\views\ 目錄下。Listing 4 提供了它的程式碼。
Listing 4. Some basic "Hello World" text
<p>Congratulations. Your initial setup is complete!</p>
請注意,其中沒有標準的 HTML 結構標籤。因為 student_index.php 是透過 template.php View 所 include 進來的,因此,它只是 view 的 HTML wrapper 和顯示的一部分。
現在,如果你瀏覽 http://127.0.0.1/codeigniter/,瀏覽器會載入一個頁面,包含頁面標題、歡迎的標題、以及恭禧的訊息。
CodeIgniter 的 shortcut
在一個 project 的開始或建立原型 (prototype) 的階段,我會儘量使用 CodeIgniter 提供的內建 “shortcut”, (例如HTML Table class 以及 Form 和 URL Helper),藉以減少開發和測試的時間。當然,這會犧牲版面或外觀和感覺 (look and feel) 的更精細的控制,但在早期開發的階段,功能應該優先於外觀。
四個 CRUD 操作
現在讓我們來看看 Web 應用程式中的 CRUD 操作。
Creating widgets
完成初始的設定後,你需要撰寫程式來管理這些 widget。由四個 CRUD 操作的第一個開始,即新增 (Create)。此程式碼必須將使用者輸入的資料當成一個 widget 儲存在資料庫中。
就由建立一個對應於 student table 的 HTML form 入手吧,這個 form 的欄位需對應於 student table 的結構。在 .\application\views\ 目錄下建立一個叫作 student_add.php 的 view 檔案,如Listing 5所示。
Listing 5. 產生用於新增操作的 HTML form 的欄位
<?php
echo form_open('student/create');
// an array of the fields in the student table
$field_array = array('s_name','p_name','address','city','state','zip','phone','email');
foreach($field_array as $field)
{
echo '<p>' . $field.': ';
echo form_input(array('name' => $field)) . '</p>';
}
// not setting the value attribute omits the submit from the $_POST array
echo form_submit('', 'Add');
echo form_close();
對於這個檔案有兩個地方要注意。首先是它使用了 form_input()
function,這是 CodeIgniter 的 Form Helper 的一個 function,是一個用來快速來產生大多數 form 中所需的 HTML 的方式。它接受三個參數:欄位名稱,欄位的值,以及任何額外的資料 (例如 JavaScript)。
第二個要注意的地方是,form_submit()
function,它同樣是接受三個參數,但欄位名稱是空字串。這樣可以避免 submit 欄位包含在由form 傳回的 post array 中。這個 model 會使用這個 array 將紀錄新增到資料庫,如果 submit
欄位包含在其中,資料庫的insert 操作就會失敗。
然後,你需要在 Student
的 controller (Listing 6) 加入一個 function 才能夠看到的 HTML 表單。
Listing 6. Student
controller 的 add()
function
function add()
{
$this->load->helper('form');
// display information for the view
$data['title'] = "Classroom: Add Student";
$data['headline'] = "Add a New Student";
$data['include'] = 'student_add';
$this->load->view('template', $data);
}
請注意,在這個 function 中載入 CodeIgniter 的 Form Helper,是要在 view student_add.php 中使用的。
現在,如果你瀏覽 http://127.0.0.1/codeigniter/index.php/student/add,瀏覽器會載入 HTML 表單,其中的欄位對應於 student table 的 record 的資料。這時,如果你將 form 送出,將會產生錯誤,因為你還沒有建立一個 function 來接收 form 的 post。要做到這一點,需要在 Student
controller 中加入一個 create()
function,如 Listing 7 所示。
Listing 7. Student
controller 的 create()
function
function create()
{
$this->load->helper('url');
$this->load->model('MStudent','',TRUE);
$this->MStudent->addStudent($_POST);
redirect('student/add','refresh');
}
這個 function 對應的 URL 是 http://127.0.0.1/codeigniter/index.php/student/create,其首先載入叫作 MStudent
的 model,稍後我們才要建立此 model。然後,執行 addStudent()
function,它接受 student_add.php 所建立的 post
array。最後,透過 redirect()
funciton 將使用者重新導向回 student_add.php 頁面, redirect()
是 URL Helper 的一個功能。
MStudent
model 是負責與資料庫中的 student table 進行互動的。在 .\system\application\models 目錄,建立叫作 mstudent.php 的檔案。(這是不錯的作法,以字首或附加的檔名來表示此檔案是一個 model,因此在檔案名稱前加上 m)。其代碼如 Listing 8所示。
Listing 8. 負責資料層的 MStudent
model
<?php
class MStudent extends CI_Model{
// Create student record in database
function addStudent($data)
{
$this->db->insert('student', $data);
}
}
/* End of file mstudent.php */
/* Location: ./application/models/mstudent.php */
此 model 使用 db->insert()
function 將 post array 新增至 student table 中。這個 function 接受兩個參數:table 的名稱和由欄位的名稱和值對應構成的關聯陣列 (associative array)。此 function 是 CodeIgniter 自動載入的 Active Record
class 的一部分,這是 CodeIgniter 提供的一個 shortcut,用以縮短開發時間。
此外,如上所述,假如 post
array 將 Submit 按鈕包含在內,將其當作一個欄位,將會使 insert 操作失敗,因為在此 table 中沒有叫作 submit
的欄位。
再次瀏覽 http://127.0.0.1/codeigniter/index.php/student/add。這一次,在欄位中輸入一些資料,然後點擊 Submit。頁面將會刷新,並且欄位會被清空。但是,如果你檢查資料庫,將會看到提交的資料已被新增到 student table 中了。
Retrieving (讀取) widgets
第二個 CRUD 操作是讀取 (read):此程式碼只需單純地從資料庫中讀取 widget record。當然,通常也包括顯示這些 record,這也就是為什麼許多人把這操作也稱為 retrieve。
在 MStudent
model 中加入一個用來讀取 student table 的 record 的 function,如Listing 9所示。
Listing 9. 用來讀取 student 中所有 records 的 MStudent
function
// Retrieve all student records
function listStudents()
{
return $this->db->get('student');
}
此程式使用 db->get()
function,這也是 CodeIgniter 的 Active Record
class 的一部分,其會依據傳給此 function 的參數,即 table 名稱 (student
) 產生 SELECT *
的 SQL 指令。
接下來,在 Student
controller 中新增一個叫作 listing()
的 function,在此 funciton 中載入 MStudent
model 以及執行 listStudents()
function,如 Listing 10 所示。其結果存放在 data 的 array 中傳遞給 template view。
Listing 10. 建立用來顯示 student 的 record 的 HTML table
function listing()
{
$this->load->library('table');
$this->load->model('MStudent','',TRUE);
$students_qry = $this->MStudent->listStudents();
// display information for the view
$data['title'] = "Classroom: Student Listing";
$data['headline'] = "Student Listing";
$data['include'] = 'student_listing';
$data['students_qry'] = $students_qry;
$this->load->view('template', $data);
}
最後,還需要一個 view 來顯示這個 HTML table。在 .\application\views\ 目錄下建立一個叫作 student_listing.php 的檔案,如 Listing 11 所示。在這個 view 中使用 table->generate()
function 來產生顯示的表格,這也一個 CodeIgniter 的 shortcut,在 HTML Table
class 中。它會依據查詢結果的object 產生 HTML table,會將欄位名稱列在 header 行,然後每一個 record 依序列在下面。
Listing 11. 顯示 HTML table
<?php
// generate HTML table from query results
$students_table = $this->table->generate($students_qry);
echo $students_table;
要看此操作的結果,請瀏覽 http://127.0.0.1/codeigniter/index.php/student/listing。你會看到從資料庫中檢索出來的學生列表。現在花些時間使用輸入表單來新增更多的學生記錄,或是使用如 Listing 12 中所示的 SQL script 來達到同樣的目的。
Listing 12. 新增多筆 student record 的 SQL script
INSERT INTO classroom.student
(id, s_name, p_name, address, city, state, zip, phone, email)
VALUES
(NULL, 'Peter Green', 'Len & Natalie Green', '480 West Broad Street',
'Eastbrook Canyon', 'PA', '19104', '(215) 900-2341',
'greenery@timewarner.dsl.com'),
(NULL, 'Jonah Ross', 'Robert & Linda Ross', '1293 Law Street',
'Eastbrook Village', 'PA', '19105', '(215) 907-1122', 'ross_boss@gmail.com'),
(NULL, 'Rebecca Dillon', 'Lainie and Howard Dillon', '12 Flamingo Drive',
'Westbrook Village', 'PA', '19103', '(215) 887-4313', 'ld_1975@yahoo.com'),
(NULL, 'Noah Singer', 'Carolyn & Peter Singer', '393 Green Lake Road, 8th Floor',
'Eastbrook Village', 'PA', '19105', '(215) 907-2344', 'candp@gmail.com'),
(NULL, 'Trevor Lee Logan', 'Steven Logan', '400 Green Lake Road, 9th Floor',
'Eastbrook Village', 'PA', '19105-6541', '(828) 299-9885',
'misterSAL@sbcglobal.net'),
(NULL, 'Audrey Christiansen', 'Lovey Christiansen', '1993 East Sunnyside Lane',
'Eastbrook Canyon', 'PA', '19104', '(215) 887-5545',
'lovey@christiansen-clan.com');
在進行下一個 CRUD 操作之前,為了更方便瀏覽此網站,開啟 .\application\views 目錄下的 template.php 檔案,在其中的 <h1>
標籤上方加入一組全域導覽連結 (global navigation links)。此程式碼如 Listing 13 所示。
Listing 13. 全域導覽連結
<div class="navigation">
<?php
// nav bar
echo anchor('student/index', 'Home');
echo (' | ');
echo anchor('student/add', 'Add a New Student');
echo (' | ');
echo anchor('student/listing', 'List All Students');
?>
</div>
此程式碼使用 anchor()
function 這個 shortcut,這是 CodeIgniter 的 URL Helper 的一部分。而且,因為 Student
controller 中的每一個 function 都會呼叫 template view,所以必需在 Student
的 constructor 中載入 URL Helper (請參見 Listing 14)。另外,也要將 create()
function 中的此 function 刪掉,因為不需重複載入此 Helper。
Listing 14. 在 constructor 載入 helper
function __construct()
{
parent::__construct();
// load helpers
$this->load->helper('url');
}
Updating (更新) widgets
你現在將要開發第三個 CRUD 操作:更新。對於這一點,此程式必須:
- 從資料庫讀取一個 widget 的紀錄。
- 顯示此記錄,以供編輯。
- 允許使用者提交修改過的資訊寫回資料庫。
首先修改學生列表,在每一行都加上 Edit 選項。您仍然可以使用 HTML Table
class 來產生 table 所需的大部分 HTML。然而,你現在需要自行以迴圈來處理資料庫傳回的查詢結果的每一個 object,藉以建立 table 的每一個行 (row),並且加上 Edit 選項。view 程式碼 student_listing.php 的修改細節請參見 Listing 15。
Listing 15. 使用 HTML Table
class 來建立 table
// generate HTML table from query results
$tmpl = array (
'table_open' => '<table border="0" cellpadding="3" cellspacing="0">',
'heading_row_start' => '<tr bgcolor="#66cc44">',
'row_start' => '<tr bgcolor="#dddddd">'
);
$this->table->set_template($tmpl);
$this->table->set_empty(" ");
$this->table->set_heading('', 'Child Name', 'Parent Name', 'Address',
'City', 'State', 'Zip', 'Phone', 'Email');
$table_row = array();
foreach ($students_qry->result() as $student)
{
$table_row = NULL;
$table_row[] = anchor('student/edit/' . $student->id, 'edit');
$table_row[] = $student->s_name;
$table_row[] = $student->p_name;
$table_row[] = $student->address;
$table_row[] = $student->city;
$table_row[] = $student->state;
$table_row[] = $student->zip;
$table_row[] = $student->phone;
$table_row[] = mailto($student->email);
$this->table->add_row($table_row);
}
$students_table = $this->table->generate();
修改後的程式碼具有的好處是能對 HTML 做更精細的控制:
- 標頭行 (header row) 可以使用適合的欄位名稱以及改變底色。
- 不會顯示
ID
欄位. - 交替的改變相鄰的行 (row) 底色。
接下來,在 Student
controller 中加入一個叫作 edit()
的 function,這是 Edit 選項所指向的 function。程式碼如 Listing 16 所示。
Listing 16. Student
controller 的 edit()
function
function edit()
{
$this->load->helper('form');
$id = $this->uri->segment(3);
$this->load->model('MStudent','',TRUE);
$data['row'] = $this->MStudent->getStudent($id)->result();
// display information for the view
$data['title'] = "Classroom: Edit Student";
$data['headline'] = "Edit Student Information";
$data['include'] = 'student_edit';
$this->load->view('template', $data);
}
這個 Edit 選項會將 student 的 record ID 當作為 URL 的第三個 segment (在 http://127.0.0.1/codeigniter/index.php/ 之後)。uri->segment(3)
function (CodeIgniter 的 URL Helper 的一部分),會從 URL 解析此 ID,並將其傳遞到 MStudent
model 的某個 function,藉以檢索該筆學生的記錄,程式碼如 Listing 17 所示。
Listing 17. 由資料庫取出一筆 student 的 record
// Retrieve one student record
function getStudent($id)
{
return $this->db->get_where('student', array('id'=> $id));
}
然後建立一個叫作 student_edit.php 的 HTML 表單 (form),以供顯示和編輯這筆 student 的紀錄,程式碼如 Listing 18 所示。
Listing 18. 建立用來編較單筆 student 的 HTML form 的 view
<?php
echo form_open('student/update');
echo form_hidden('id', $row[0]->id);
// an array of the fields in the student table
$field_array = array('s_name','p_name','address','city','state','zip','phone','email');
foreach($field_array as $field_name)
{
echo '<p>' . $field_name.': ';
echo form_input($field_name, $row[0]->$field_name) . '</p>';
}
// not setting the value attribute omits the submit from the $_POST array
echo form_submit('', 'Update');
echo form_close();
請注意,這個程式檔幾乎和 student_add.php 一樣。有許多程式人員喜歡使用單一的 add/edit 程式檔的架構來取代兩個分開的程式檔。基本上,這只是不同的程式風格,兩種方式各有利弊。
接下來,在 Student
controller 加入 update()
function,藉以接收 form 的 post 資料,程式碼如 Listing 19 所示。
Listing 19. Student
controller 的 update()
function
function update()
{
$this->load->model('MStudent','',TRUE);
$this->MStudent->updateStudent($_POST['id'], $_POST);
redirect('student/listing','refresh');
}
最後,修改 MStudent
model,加入一個 function 以供更新資料庫中的 student 的紀錄。此功能如 Listing 20 所示。
Listing 20. 更新單筆 student 的 record
// Update one student record
function updateStudent($id, $data)
{
$this->db->where('id', $id);
// Microsoft SQL Server can't update identity column 'id'
unset($data['id']);
$this->db->update('student', $data);
}
現在已完成更新的操作。你可以瀏覽學生的列表,編輯任何一個學生的紀錄,提交表單,然後在刷新的學生列表中看到更新後的資訊。其中,Microsoft SQL Server 無法更新識別資料行 'id',所以要將 array 中的 'id' 值刪掉。
Deleting (刪除) widgets
最後一個 CRUD 操作是刪除。在這個操作中,要讓使用者能夠從列表中選擇一筆記錄,然後刪除該記錄。你也會想在真的刪除紀錄之前確認使用者的意思 (當碰到使用者點擊了錯誤的連接的情況時)。
首先,以 Listing 21 的程式碼取代 Student
controller 的 listing()
function 中建立 Edit 選項的程式碼。
Listing 21. 在學生列表中加入 Delete
選項
$table_row[] = '<nobr>' .
anchor('student/edit/' . $student->id, 'edit') . ' | ' .
anchor('student/delete/' . $student->id, 'delete',
"onClick=\" return confirm('Are you sure you want to '
+ 'delete the record for $student->s_name?')\"") .
'</nobr>';
接下來,在 Student
controller 中加入 delete()
function ,這是 Delete 選項所指向的 function。代碼如 Listing 22 所示。
Listing 22. Student
controller 的 delete()
function
function delete()
{
$id = $this->uri->segment(3);
$this->load->model('MStudent','',TRUE);
$this->MStudent->deleteStudent($id);
redirect('student/listing','refresh');
}
最後,修改 MStudent
model,在其中加入一個 function 以供刪除一筆紀錄。此 function 如 Listing 23 所示。
Listing 23. 刪除單筆 student 的 record
// Delete one student record
function deleteStudent($id)
{
$this->db->where('id', $id);
$this->db->delete('student');
}
現在測試一下。瀏覽學生的列表並且試著刪除一筆學生的記錄。如果在 JavaScript 的提示中點擊 OK,學生名單將會刷新,並顯示該筆紀錄已被刪除。
結論
恭喜!你已經使用 CodeIgniter 完成了一個動態的 Web 網站的基礎。因為使用了 MVC 模式,這網站將展示層(presentation layer) 和資料層 (data layer) 很清楚的分割開來。並且藉由使用 CodeIgniter 所提供的 coding shortcuts,使得開發時間得以加快,也使得程式碼變少。事實上,你有沒有注意到你完成的這個應用程式只使用了一個 controller,一個 model,和五個 view 嗎?這真的是相當高效率的程式碼。