利用 MVC 框架和 CRUD 提高效率 级别: 中级 Kevin Howard Goldberg, CTO, imagistic 2010 年 2 月 10 日 |
CodeIgniter 是一种以 PHP 编写的开源 Web 应用程序框架。它可适用于很多种数据库应用程序,包括 MySQL、DB2® Express-C 等。此框架使用的是 MVC 设计模式,其最主要的目的是分离一个软件应用程序的数据层和表示层。在 Model-View-Controller (MVC) 模式中,model 管理数据层,与数据库通信;view 管理表示层,显示 UI 和内容;而 controller 则负责管理视图和模型之间的通信。
|
本文概述了使用 CodeIgniter 创建一个基础动态 Web 站点或应用程序所需的步骤。本文假设您已经安装了 CodeIgniter V1.7.2 或更高版本以及 MySQL V4.1 或更高版本,并且对二者有基本的了解。如果您是 CodeIgniter 的新手或是需要快速复习一下,请参见 参考资料。
当然,大多数动态 Web 站点都不会等同于本文中所使用的这个例子 — 在很多情况下,甚至会有显著的差别。不过,所有动态 Web 站点都具有两个关键的共同点:数据库以及从该数据库(动态地)检索到的东西。为了方便本文的讨论,我将这种可动态检索到的东西称为是一个小部件。一个小部件可以是几乎任何内容 — 一本在售的书、一个菜谱、一个博客条目或一篇新闻稿。不管这个小部件是什么,其定义都需要一组连贯的信息。例如,一个菜谱所需的一组信息可能应该包括一个标题、配料、操作指导和营养分析。
要从数据库中检索小部件,首先必须要创建它(然后它才可以被更新甚至是删除),这就是所谓的 CRUD。CRUD 代表的是管理数据库内的小部件所需的最主要的四个操作;若再加上小部件本身,实际上就构成了动态 Web 站点的基础。
|
|
在本文中,我们将创建一个 Web 应用程序来管理一组学生及其家长的联系信息,比如孩子所在的体育队、YMCA 小组或学校班级,这些都是我们将要创建的。
首先,创建这个小部件的数据模型。这个小部件所需的一组信息如下:
为了存储这个小部件,创建一个表,名为 student,内含与如上所列这组信息相对应的字段。用来创建这个 MySQL 数据库和表的脚本如清单 1 所示。
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 ; |
|
|
创建了数据库和表后,在 ./system/application/config/database.php 内设置 CodeIgniter 数据库变量,在 ./system/application/config/config.php 内设置基础 URL。对于本文,我假设这个基础 URL 是 http://127.0.0.1/codeigniter/。
接下来,创建一个默认的控制器和视图。(它们就绪后,您将能够看到所编写代码的效果)。对于本项目,在 ./system/application/controllers/ 文件夹内,创建一个名为 student.php 的控制器,如清单 2 所示,并在 ./system/application/config/routes.php 内将其设置为默认控制器。
<?php class Student extends Controller { function Student() { parent::Controller(); } 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: ./system/application/controllers/student.php */ |
注意,index()
函数创建了一个名为 data 的数组,该数组包含三个已命名的索引:title
、 headline
和 include
。此数组被传递给一个名为 template 的视图,该视图被存储为 ./system/application/views/ 文件夹内的 template.php(参见清单 3)。
<!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> |
此视图将是该 Web 站点内的所有页面的 HTML shell 或可视包装器。它包含一个标准的 HTML 设置并接受来自控制器的三个值 — title
、 headline
和 include
— 分别对应于页面标题、页面大标题以及一个用于内容显示的视图文件。
这个包含的视图文件是初始设置的最后一个步骤。将其命名为 student_index.php,正如在 template.php 内所声明的那样(如上面的 清单 2 所示),并将其存储在 ./system/application/views/ 文件夹内。清单 4 提供了它的源代码。
<p>Congratulations. Your initial setup is complete!</p> |
请注意,这里没有标准的 HTML 结构标签。这是因为 student_index.php 是通过 template.php 视图包括进来的,因而,是该视图的 HTML 包装器和显示的一部分。
现在,如果导航到 http://127.0.0.1/codeigniter/,浏览器会载入一个页面,此页面具有一个页面标题、一个表示欢迎的大标题和一条祝贺消息。
|
|
让我们从 CRUD 操作的角度来分析这个 Web 应用程序。
现在,初始设置已经完成了,我们需要编写用来管理小部件的代码。以这四种 CRUD 操作中的第一个操作开始:创建操作。该代码必须将用户输入保存为此数据库内的一个小部件。
首先,创建一个 HTML 表单,表单的字段对应于这个 student 表的结构。在 ./system/application/views/ 文件夹内创建一个名为 student_add.php 的视图文件,如清单 5 所示。
<?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()
函数。该函数是 CodeIgniter Form Helper 的一部分,也是生成大多数表单所需的 HTML 的一种十分快捷的方式。它接受三个参数:一个字段名、一个字段值以及任何额外的数据(比如 JavaScript)。
|
第二点需要注意的是 form_submit()
函数,该函数也接受同样的三个参数,并具有一个针对字段名的空白字符串。这就可以防止提交字段成为表单发布数组的一部分。在向数据库添加记录时,此模型会使用该数组;若包括了 submit
字段,那么数据库插入就会失败。
然后,需要向 Student
控制器(清单 6)添加一个函数,以便能够看到这个 HTML 表单。
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); } |
请注意,此函数加载 CodeIgniter Form Helper 以供 student_add.php 视图所用。
现在,如果导航到 http://127.0.0.1/codeigniter/index.php/student/add,浏览器就会加载这个 HTML 表单,其中的各字段代表着 student 表内的一条记录。如果提交此表单,就会得到一个错误,因为我们尚未创建一个函数来接收此表单的发布。为此,向 Student
控制器添加一个 create()
函数,如清单 7 所示。
清单 7. Student
控制器的 create()
函数
function create() { $this->load->helper('url'); $this->load->model('MStudent','',TRUE); $this->MStudent->addStudent($_POST); redirect('student/add','refresh'); } |
此函数对应于 URL http://127.0.0.1/codeigniter/index.php/student/create,它首先加载一个称为 MStudent
的模型(此模型在下一个步骤创建)。然后,它会执行 addStudent()
函数,传递进在 student_add.php 页面上创建的 post
数组。最后,它会使用 redirect()
函数(URL Helper 文件的一部分)将用户重定向回 student_add.php 页面。
MStudent
模型负责与数据库内的 student 表进行交互。在 ./system/application/models 文件夹,创建一个文件,名为 mstudent.php。(一种好的做法是在文件名之前或之后添加一个能表明该文件是一个模型的前缀或后缀,比如 m)。其代码如清单 8 所示。
<?php class MStudent extends Model{ // Create student record in database function addStudent($data) { $this->db->insert('student', $data); } } /* End of file mstudent.php */ /* Location: ./system/application/models/mstudent.php */ |
此模型使用 db->insert()
函数来向 student 表插入 post 数组数据。此函数接受两个参数:表的名称以及字段名和值的关联数组。此函数是 CodeIgniter 的 Active Record
类的一部分,会自动加载,并且是 CodeIgniter 提供的 shortcut 之一,可减少开发时间。
并且,正如之前提到的,如果 post
数组将 Submit 按钮作为一个字段包括进来,那么插入就会失效,因为在此表内没有一个名为 submit
的字段。
再次导航到 http://127.0.0.1/codeigniter/index.php/student/add。这次,将某些数据放入这些字段并单击 Submit。页面将会刷新,并且字段也会被置空。但是如果查看一下该数据库,就会看到所提交的数据已经被添加到这个 student 表。
第二个 CRUD 操作是读取,代码的作用是从数据库中读取小部件的记录。当然,经常还需要显示这些记录,这也是为什么人们更愿意将这种操作称为检索 的原因。
首先,用一个函数更新 MStudent
模型以便检索 student 表的记录,如清单 9 所示。
// Retrieve all student records function listStudents() { return $this->db->get('student'); } |
上述代码使用了 db->get()
函数(该函数也是 CodeIgniter 的 Active Record
类的一部分)来生成一个 SELECT *
,它针对在函数的参数(student
)内命名的表。
接下来,向 Student
控制器添加一个函数,名为 listing()
,用来加载 MStudent
模型并执行 listStudents()
函数,如清单 10 所示。
function listing() { $this->load->library('table'); $this->load->model('MStudent','',TRUE); $students_qry = $this->MStudent->listStudents(); // generate HTML table from query results $students_table = $this->table->generate($students_qry); // display information for the view $data['title'] = "Classroom: Student Listing"; $data['headline'] = "Student Listing"; $data['include'] = 'student_listing'; $data['data_table'] = $students_table; $this->load->view('template', $data); } |
此函数在 HTML Table
类内使用了 table->generate()
函数(一个 CodeIgniter shortcut)。它生成了一个 HTML 表,并且以来自查询结果对象的字段名作为表的标题行,之后,每条记录依次列出。其结果以数据数组的形式传递给模板视图。
最后,需要一个视图来显示这个 HTML 表。在 ./system/application/views/ 文件夹内创建一个名为 student_listing.php 的文件,如清单 11 所示。
<?php echo $data_table; ?> |
要查看此操作的结果,访问 http://127.0.0.1/codeigniter/index.php/student/listing。您将会看到从此数据库中检索到的学生列表。现在通过使用输入表单添加更多的学生记录,或运行如清单 12 所示的 SQL 脚本实现相同的目的。
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', '[email protected]'), (NULL, 'Jonah Ross', 'Robert & Linda Ross', '1293 Law Street', 'Eastbrook Village', 'PA', '19105', '(215) 907-1122', '[email protected]'), (NULL, 'Rebecca Dillon', 'Lainie and Howard Dillon', '12 Flamingo Drive', 'Westbrook Village', 'PA', '19103', '(215) 887-4313', '[email protected]'), (NULL, 'Noah Singer', 'Carolyn & Peter Singer', '393 Green Lake Road, 8th Floor', 'Eastbrook Village', 'PA', '19105', '(215) 907-2344', '[email protected]'), (NULL, 'Trevor Lee Logan', 'Steven Logan', '400 Green Lake Road, 9th Floor', 'Eastbrook Village', 'PA', '19105-6541', '(828) 299-9885', '[email protected]'), (NULL, 'Audrey Christiansen', 'Lovey Christiansen', '1993 East Sunnyside Lane', 'Eastbrook Canyon', 'PA', '19104', '(215) 887-5545', '[email protected]'); |
在转入下一个 CRUD 操作之前,为了让导航站点更为简便,可以在 ./system/application/views 文件夹内的 template.php 文件中的 <h1>
标签之上添加一组全局导航链接。代码如清单 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()
函数 shortcut,它也是 CodeIgniter 的 URL Helper 文件的一部分。并且,由于 Student
控制器内的每个函数都会接触到模板视图,所以需要加载 URL Helper,作为 Student
构造函数的一部分(参见清单 14)。此外还应该从 create()
函数删除它以避免两次加载 Helper 文件。
function __construct() { parent::Controller(); // load helpers $this->load->helper('url'); } |
现在可以进行第三个 CRUD 操作:更新。对此,代码必须:
首先,更新学生列表以便在每一行都包括一个 Edit 选项。仍可以使用 HTML Table
类来生成此表所需的大多数 HTML。不过,现在需要显式地对数据库查询结果对象进行循环以便创建这些表的行并添加 Edit 选项。对 Student
控制器内的 listing()
函数的更新详见清单 15。
// 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 控制:
ID
字段。 接下来,向 Student
控制器添加一个函数,称为 edit()
,这也是 Edit 选项所指向的位置。代码如清单 16 所示。
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 选项将学生记录 ID 作为 URL 内的第三个片段(http://127.0.0.1/codeigniter/index.php/ 之后)传递。uri->segment(3)
函数(CodeIgniter 的 URL Helper 文件的一部分)从这个 URL 解析此 ID 并将其传递给 MStudent
模型内的一个函数来检索此学生记录,如清单 17 所示。
// Retrieve one student record function getStudent($id) { return $this->db->get_where('student', array('id'=> $id)); } |
然后,创建一个名为 student_edit.php 的 HTML 表单来显示和编辑此学生记录,如清单 18 所示。
<?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。很多开发人员都喜欢构造一个同时包含添加/编辑的文件结构,而不是创建两个不同的文件。不过归根到底,采取哪种方式都和样式有关,并且两种方式各有利弊。
接下来,为了接收这个表单发布,向 Student
控制器添加函数 update()
;代码如清单 19 所示。
清单 19. Student
控制器的 update()
函数
function update() { $this->load->model('MStudent','',TRUE); $this->MStudent->updateStudent($_POST['id'], $_POST); redirect('student/listing','refresh'); } |
最后,用一个函数更新 MStudent
模型来在数据库内更新此学生记录。此函数如清单 20 所示。
// Update one student record function updateStudent($id, $data) { $this->db->where('id', $id); $this->db->update('student', $data); } |
更新操作完成。现在,可以导航到学生列表、编辑学生记录、提交表单并查看刷新后的学生列表内的更新信息。
最后一个 CRUD 操作是删除。在这个操作中,用户需要能够从一个列表中选择一个记录,然后再删除该记录。此外,在实际删除此记录之前,需要确认用户的真实目的(用户有可能单击了错误的链接)。
首先用清单 21 中的代码代替用来创建 Student
控制器的 listing()
函数内的 Edit 选项的那些代码。
$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
控制器添加一个 delete()
函数,这是 Delete 选项所指向的位置。代码如清单 22 所示。
清单 22. Student
控制器的 delete()
函数
function delete() { $id = $this->uri->segment(3); $this->load->model('MStudent','',TRUE); $this->MStudent->deleteStudent($id); redirect('student/listing','refresh'); } |
最后,用一个函数更新 MStudent
模型来删除此记录。该函数如清单 23 所示。
// Delete one student record function deleteStudent($id) { $this->db->where('id', $id); $this->db->delete('student'); } |
现在,我们不妨试一下。导航到此学生列表并尝试删除一个学生记录。如果在 JavaScript 提示内单击 OK,这个学生列表将会被刷新并会显示此记录已被删除。
|
|
祝贺您!您已经完成了用 CodeIgniter 开发动态 Web 站点的基础工作。该站点由于使用了 MVC 模式,其表示层和数据层已经完全分开。并且通过包括 CodeIgniter 提供的编码 shortcut,开发时间被大大缩短,所需编写的代码行也减少到了最少。实际上,您是否意识到这个应用程序的完成只使用了一个控制器、一个模型和五个视图?这无疑是一种十分高效的编码。
|
|
os-codeigniter-classroom.zip | 2160KB | HTTP |
os-codeigniter-MVC-files-only.zip | 5KB | HTTP |
关于下载方法的信息 |
学习
获得产品和技术
讨论
Kevin Howard Goldberg 在高科技产业具有 20 多年的从业经验,现居住在美国加州 Westlake Village,是一名技术总监、作者和顾问。他目前是 imagistic 公司的 CTO,这是一家获过奖的数字营销和技术公司,这家公司是他在 1997 年与别人共同成立的。作为一个 Web 开发和技术专家,他在 Santa Monica College Computer Science Advisory Board 服务并曾在 Film Roman、Lionsgate 和 Philips Interactive Media 担任过高职。他是 XML: Visual QuickStart Guide (2nd Edition)(PeachPit Press,2008 年)一书的作者。 |
来源:http://www.ibm.com/developerworks/cn/opensource/os-codeigniter/index.html