ThinkPHP 提供了一个 Model 类,供其他的 Model 进行继承。Model 类中是 MVC 中的模型类,它是调用 持久层 的上层类。感觉这么描述问题很多,但是有什么办法呢?但是,这个 Model 有时无法满足我们的一些需求,因此我们需要自定义一个 Model 类出来,不过自定义的 Model 同样要继承 TP 提供的 Model 类,而把我们自定义的 Model 类作为我们项目中的 Model 基类。我怎么感觉我在说绕口令,等等...我有点晕。
//TP 恶补ing...
最近比较绕有兴趣地学习PHP,看完了一些基础语法、学会了使用SMARY模版,继而看了ThinkPHP框架,
---恢复内容开始---
一、定义数据表模型
此框架的强大超出我的意外,它的ROR真是强,还有内置的CRUD,比JAVA的框架更智能。
前言
TP的手册相当多,其实不必再出这样的贴子,论技术,我也是菜鸟一个,同时也在学习当中。
看到论坛上多了不少新朋友,不少在抱怨手册看不懂,那我就姑且抛砖引玉,尝试与新朋友们更简单地、手把手地进入TP的应用中去。讲解过程中有错的地方,大家帮忙指正。
无聊的需求
1.模型映射
方便记忆性的学习转载了一些以下文章
这个系列,初步定下的目标为,从零开始,以TP示例中心中的Form为例进行讲解,以实践为主,理论为辅,
将TP的最基本内容逛一遍,至少让我们一起学会如何进行最简单的对数据进行查、增、改、删操作并输出到模板。
在使用 Java 的开源项目 JeeSite 时,养成了一个不好的习惯,习惯给每张表都增加 create_by、create_date、update_by、update_date、remarks 和 del_flag 这么几个字段。如果每张表都有这几个字段,那么对每张表进行 insert 时都会对以上的字段进行设置,对每张表进行 update 时都会对其中部分字段进行更新,对每条记录进行 delete 时都其实是对 del_flag 字段进行 置位。重复操作很多,一些操作方法被修改。那么,这个时候就要自定义一个自己的 Model 来作为项目的 基类 了,这个 Model 就负责干上面我说的那些事情了。
要测试数据库是否正常连接,最直接的办法就是在当前控制器中实例化数据表,然后使用 dump 函数输出,查看数据库的链接状态。代码:
CURD知识之一 C 创建(create)
由于我们说的是循序渐进,所以我用步骤式来说明,只要一步一步跟着做,相信聪明的你在使用过程中就会明白TP的应用了。
public function testdb(){ $obj=M("User"); dump($obj); }
名词解析:
1 CURD:代表数据库四个基本操作:创建:create,更新:update,读取:read,删除:delete
注意:以下的步骤,仅仅是TP灵活的布署方式其中一种,实际开发中可以根据自己的情况去另行设定。
至于为什么那样做,我们会在最后再作总结,我觉得先实操然后再进行说明比较容易明白。以下不再重复解释。
自定义一个 Model
此时浏览器输出:
2 模型对象:即数据对象,你项目里每一个Model类文件都会对应着一个数据表(或者视图),模型与数据表存在一种映射关系。TP约定了命名要遵循一定的规范,如果不符合,则需要根据情况进行额外的相应设置。例如Model类的tableName属性
============================================
自定义一个 Model,Model 中至少重新 TP 提供的 Model 中的 add、save、delete 和 select 方法。
在第三节时,曾略为提到Model类文件命名,现在再回顾一下这部分的内容:
我们之前在数据库中,已建立了一个think_form数据库表,并且在配置文件config.php中,我们定义了数据表的前缀是think_,
模型类(Model)文件的命名规则是:
一 快速开始一个项目
定义代码如下:
object(Model)#5 (20) { ["_extModel:private"] => NULL ["db:protected"] => object(DbMysql)#7 (18) { ["dbType:protected"] => string(5) "MYSQL" ["autoFree:protected"] => bool(false) ["model:protected"] => string(7) "_think_" ["pconnect:protected"] => bool(false) ["queryStr:protected"] => string(28) "SHOW COLUMNS FROM `tpk_user`" ["modelSql:protected"] => array(1) { ["user"] => string(28) "SHOW COLUMNS FROM `tpk_user`" } ["lastInsID:protected"] => NULL ["numRows:protected"] => int(2) ["numCols:protected"] => int(0) ["transTimes:protected"] => int(0) ["error:protected"] => string(0) "" ["linkID:protected"] => array(1) { [0] => resource(27) of type (mysql link) } ["_linkID:protected"] => resource(27) of type (mysql link) ["queryID:protected"] => resource(28) of type (mysql result) ["connected:protected"] => bool(true) ["comparison:protected"] => array(10) { ["eq"] => string(1) "=" ["neq"] => string(2) "<>" ["gt"] => string(1) ">" ["egt"] => string(2) ">=" ["lt"] => string(1) "<" ["elt"] => string(2) "<=" ["notlike"] => string(8) "NOT LIKE" ["like"] => string(4) "LIKE" ["in"] => string(2) "IN" ["notin"] => string(6) "NOT IN" } ["selectSql:protected"] => string(96) "SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%COMMENT%" ["bind:protected"] => array(0) { } } ["pk:protected"] => string(2) "id" ["tablePrefix:protected"] => string(4) "tpk_" ["name:protected"] => string(4) "user" ["dbName:protected"] => string(0) "" ["connection:protected"] => string(0) "" ["tableName:protected"] => string(0) "" ["trueTableName:protected"] => string(8) "tpk_user" ["error:protected"] => string(0) "" ["fields:protected"] => array(5) { [0] => string(2) "id" [1] => string(8) "username" ["_autoinc"] => bool(true) ["_pk"] => string(2) "id" ["_type"] => array(2) { ["id"] => string(7) "int(11)" ["username"] => string(11) "varchar(20)" } } ["data:protected"] => array(0) { } ["options:protected"] => array(0) { } ["_validate:protected"] => array(0) { } ["_auto:protected"] => array(0) { } ["_map:protected"] => array(0) { } ["_scope:protected"] => array(0) { } ["autoCheckFields:protected"] => bool(true) ["patchValidate:protected"] => bool(false) ["methods:protected"] => array(13) { [0] => string(5) "table" [1] => string(5) "order" [2] => string(5) "alias" [3] => string(6) "having" [4] => string(5) "group" [5] => string(4) "lock" [6] => string(8) "distinct" [7] => string(4) "auto" [8] => string(6) "filter" [9] => string(8) "validate" [10] => string(6) "result" [11] => string(4) "bind" [12] => string(5) "token" } }
不包括前缀的数据库表表名并且首字母大写 Model.class.php
名词解释:
1 <?php
2 namespace AdminModel;
3 use ThinkModel;
4
5 /**
6 * 项目中其他 Model 类的基类
7 * 该类继承自 TP 提供的基类 Model
8 */
9 class BaseModel extends Model {
10
11 /**
12 * 继承基类 Model 的 add 方法
13 * 自动插入 id create_by create_date update_by update_date del_flag
14 */
15 public function add($data='',$options=array(),$replace=false) {
16
17 $data["id"] = $this->getUuid();
18 $data["create_by"] = "";
19 $data["create_date"] = date("Y-m-d H:i:s");
20 $data["update_by"] = "";
21 $data["update_date"] = date("Y-m-d H:i:s");
22 $data["del_flag"] = '0';
23
24 return parent::add($data, $options, $replace);
25 }
26
27 /**
28 * 获得 未删除 的所有数据
29 * 记录是否被删除通过 del_flag 字段来进行确定
30 */
31 public function select($options=array()) {
32 $map["del_flag"] = 0;
33 $this->where($map);
34
35 return parent::select($options);
36 }
37
38 /**
39 * 获取 全部 的所有数据
40 */
41 public function selectAll($options=array()) {
42 return parent::select($options);
43 }
44
45 /**
46 * 更新数据
47 * 更新数据时,要更新 update_by update_date 两个字段
48 */
49 public function save($data='',$options=array()) {
50 $data["update_by"] = "";
51 $data["update_date"] = date("Y-m-d H:i:s");
52
53 return parent::save($data, $options);
54 }
55
56 /**
57 * 删除也是更新
58 * 设置 删除 标志位即可
59 */
60 public function delete($data='',$options=array()) {
61 $data["del_flag"] = 1;
62
63 return parent::save($data, $options);
64 }
所以之前我们为think_form数据表在Myapp/Lib/Model目录下建立一个文件FormModel.class.php
项目:你要开发的系统,称之为项目。
这样,我们的 BaseModel 就定义好了,以后项目中的 Model 就不再继承 TP 的 Model 类了,而是继承我们自定义的 BaseModel 了。
如果没有提示错误即为成功。
模型类的特殊命名还可以智能识别驼峰式的表命名,假设我们有个是类似think_new_table这样的表
可以命名为NewTableModel.class.php。默认配置便可以智能识别自动对应think_new_table表,因此不必修改配置。
入口文件:你可以理解为这个项目的唯一一道门,以后所有的操作都会通过这道门去执行处理。
不必理会什么意思,你甚至可以先把它看成是index.php就是入口文件
M("User") 就是模型映射,M 函数等于 new Model() ,Model 类是模型的基类,也是数据库操作的基类, "User" 是该类的一个成员属性,表示模型名称,模型名称与数据库中的数据表进行映射。注意:User "U" 要大写,数据库中此时应该存在一张 user 表,系统会根据配置文件中的设置给 user 表添加前缀,例如 tpk_user。如果不需要为表添加前缀,将模型名称首字母改为小写,例如 M("user")。
开发过程中,只要简单地定义好与数据表对应的模型类(xxxModel.class.php),就可以进行对数据操作了。
如何让这个模型支持自动验证,自动填充,自动过滤这些知识,在下面涉及到时会作相应讲解。
TP: ThinkPHP框架的简称
继承我们定义的基类
一般来说,在表中添加数据都是通过表单来建立,为了更直观地学习CURD中的C,我们接下来建立个表单来进行添加数据的操作。
之前我们已在Myapp/Lib/Model/下建立好FormModel.class.php类----模型(M)
同时也在Myapp/Lib/Action/下添加了一个Index应用模块和写下了一个index操作----控制器(C)
并且在Tpl目录下建立了与之对应的模板目录Index及index.html模板文件 -------视图(V)
MVC模式的开发已体现了出来。
tdweb曾简明扼要地概括了TP中这三者的关系,
1 下载TP1.5正式版
继承的 BaseModel 的方法和继承 Model 的方法是一样的,只是使用 use 引入命名空间到当前作用域时的需要修改为我们的命名空间,而不是再去使用 TP 提供的命名空间。代码如下:
2.自定义模型
C的作用,就是从M中取得数据到V,
2 拟好你的项目名称,我们这里以 Myapp 为项目名称
1 <?php
2 namespace AdminModel;
3 use AdminModelBaseModel;
4
5 class TrunkModel extends BaseModel {
D 函数用于快速实例化自定义模型,可以进行复杂的数据库操作,比如数据检验、数据缓存、数据加工等。自定义模型存放在 Lib/Model 目录下,例如为数据表 tpk_article 数据表建立模型映射,则需要创建 ArticleModel.class.php,然后使用 D 函数进行实例化,ArticleModel 模型将与 tpk_article 表进行映射。
使用TP以表单提交数据到库,流程和你所了解的表单提交没有什么区别,只是TP简化了数据操作的处理过程。
我们在实际操作中体会一下。
先来看看我们所定义的数据表form的字段:
3 在www根目录下,将TP框架所有文件全部复制过去,文件夹名称是ThinkPHP
use 引入的命名空间是 AdminModelBaseModel ,因为我们定义的 BaseModel 的命名空间是 AdminModel,因为这里的 TrunkModel 和 BaseModel 在一个命名空间下,省去 use 也是可以的。
例:使用 select() 输出 tpk_article 表的数据 ( select() 方法用于列出所有符合条件的数据 ) :
4 与ThinkPHP同级新建一个文件夹,起名为 Myapp,也就是项目名称
class IndexAction extends Action { public function article(){ $obj=D("Article"); $rows=$obj->select(); dump($rows); } }
复制代码
5 在www根目录下,创建一个PHP文件,起名index.php,这就是入口文件
这样,在我们用 D 方法实例化 TrunkModel 的对象后,使用 add 方法插入数据时,就会先调用 BaseModel 中的 add 方法了,这样我们每张表都有的 create_by 等一系列字段就都会自动得到了。
浏览器输出:
其中的create_time字段是为了记录数据插入的时间,我们可以利用TP的数据自动填充来处理。
入口文件index.php代码:
在Model类定义 $_auto 属性,可以完成数据自动处理功能,用来处理默认值和其他 onclick="tagshow(event)">系统写入字段。
注意1:该自动填充可能会覆盖表单提交项目。其目的是为了防止表单非法提交字段。
注意2:要使用Model类的create方法创建数据对象的时候才会自动进行表单数据处理。
?>
array(6) { [0] => array(7) { ["id"] => string(1) "1" ["title"] => string(4) "test" ["content"] => string(12) "test_content" ["category"] => string(13) "test_category" ["area"] => string(6) "北京" ["add_user"] => string(5) "admin" ["add_time"] => string(19) "2014-11-20 23:03:44" } [1] => array(7) { ["id"] => string(1) "2" ["title"] => string(12) "吼吼吼吼" ["content"] => string(18) "任溶溶柔然人" ["category"] => string(14) "test_category2" ["area"] => string(6) "河北" ["add_user"] => string(5) "admin" ["add_time"] => string(19) "2014-11-22 15:16:12" } [2] => array(7) { ["id"] => string(1) "4" ["title"] => string(7) "test2_m" ["content"] => string(4) "haha" ["category"] => string(0) "" ["area"] => string(6) "福建" ["add_user"] => NULL ["add_time"] => string(19) "2014-11-22 11:44:26" } [3] => array(7) { ["id"] => string(1) "5" ["title"] => string(2) "22" ["content"] => NULL ["category"] => string(0) "" ["area"] => string(6) "福建" ["add_user"] => NULL ["add_time"] => string(19) "2014-11-22 12:40:58" } [4] => array(7) { ["id"] => string(1) "6" ["title"] => string(1) "1" ["content"] => string(1) "2" ["category"] => string(0) "" ["area"] => string(6) "福建" ["add_user"] => NULL ["add_time"] => NULL } [5] => array(7) { ["id"] => string(1) "7" ["title"] => string(6) "lalala" ["content"] => string(6) "hohoho" ["category"] => string(0) "" ["area"] => string(6) "北京" ["add_user"] => NULL ["add_time"] => NULL } }
1 打开Myapp/Lib/Model/FormModel.class.php文件,更改代码为
复制代码
就这么简单几行,然后打开浏览器,输入
一个TP项目就这样构建出来了。你会看到
代码知识要点:
Model类的$_auto属性由多个填充因子组成的数组,填充因子定义格式:
例2:让 tpk_article 表的内容根据客户所在的地区显示当地的新闻:
array(填充字段,填充内容,填充条件,附加规则)
复制代码
ArticleModel.class.php:
填充字段:就是需要进行处理的表单字段,这个字段并不一定要是数据库表中的字段,对于表单内的辅助检测字段比如重复密码和验证码也可以处理。
填充条件:ADD | UPDATE | ALL(注意,此是1.5版本说明,1.6已改为1 2
3,后续教程有说明)
当为ADD时,会在新增数据时自动填充,这是默认的处理方式
当为UPDATE时,在更新数据的时候会自动填充
当为ALL时,所有情况下都会进行自动填充
这行字。并自动为你创建好项目的目录。接下来,我们这个项目添砖加瓦。
<?php class ArticleModel extends Model{ public function article(){ $rows=$this->where("area='{$this->checkUserArea()}'")->select(); return $rows; } protected function checkUserArea(){ return "北京"; }
附加规则:附加规则是针对填充内容而言,表示该内容填充的方式,包括function,callback,field,string
对于field使用其它字段进行填充和string直接标示字符串作为值进行填充很好理解。例如上面
=============================================
控制器代码:
二简单认识项目里的各个文件夹
IndexAction.class.php:
复制代码
名词解释:
<?php class IndexAction extends Action { $obj=D("Article"); $rows=$obj->article(); $this->assign("list",$rows); $this->display();
就是将状态status字段的值直接以1填充.
Action:模块控制器类,你暂时可以理解为一个Action文件代表着一个应用模块,Action文件中的每一个方法(function)代表着一个操作,操作分为有输出到模板的操作和只具执行不需要输出的操作。对于有输出的操作,则代表了你的项目中一个展示给用户应用的web页面。(注意:这不是正统的术语解释)
同时视图代码:
下面主要说说function与callback这两个附加规则。
Model:数据表相对应的Model类,可以理解为一个Model文件对应着你项目数据库的其中一个表。命名关系请参看这里:
TPL/Index/article.html:
在添砖加瓦之前,我们有必要认识一下各个文件夹的用途。
<!DOCTYPE html> <html> <body> <volist name="list" id="vo"> <li><{$vo.title}> - <{$vo.area}> - <{$vo.content}></li> </volist> </body> </html>
上面create_time代表了要处理的字段,填充内容是time,附加规则是function使用函数,填充条件是ADD新增时处理,那么整行代码表示对create_time字段在新增的时候使time函数作为该字段的值进行自动填充。
再看一个function作为附加规则的例子
打开Myapp文件夹,里面TP已让你很省心地构建了最基本的目录。其中:
附:数据表 tpk_article 的表结构为:
Cache文件夹:项目自动生成的模版缓存会出现在这里
当使用function作为附加规则时,第二个填充内容就代表了这个因子是一个函数名称,该函数的参数就是代表填充字段的值,比如password的值是123456,则上面的代码会先将这个值使用函数md5('123456'),这样处理后再插入到数据表中去。
对于function(函数)和callback(回调方法)理论是一样的,只不过一个是表示填充内容所写的是函数名,另一个是表示填充内容是类中的方法名。函数可以是PHP5内置函数或你自己所写的函数。而callback是当前模型类所能调用的一个方法。 有关数据填充方面的详细资料,请参看官方使用手册
《ThinkPHP数据操作指南》
Common文件夹:你的项目中要用到的自己写的函数,可以在这个文件夹下创建一个名为common.php文件,
在这个文件中书写函数,这些函数可以用在你项目的各个类,同时也可以在模板变量中使用,TP框架会自动加载。
总结:自定义模型映射,一个模型对应一个数据表,所有增删改查都在模型类中完成。M 实例化的参数是数据库的表名,D 实例化的是自己在 model 文件夹下建立的模型文件。
2 打开Myapp/Tpl/default/Index/index.html文件,我们修改代码将form写进去
Conf文件夹:项目的运行配置文件config.php将放在这个文件夹里,(还有路由配置,调试配置等,请先忽略)
Data文件夹:TP会把项目的数据库表字段生成到这里,另外。。。那些先不必理会。
3.create 方法
在上面代码中,我们简单地建了一个form表单,并将标题统一使用一个模板变量{$title}。
action提交到的处理地址是__URL__/add,其中__URL__是一个常量定义,表示当前模块地址,TP模板引擎会自动将这句解释为/index.php/Index/add,常见的模板使用的常量有
Lang文件夹:项目的语言设置目录,先不必理会。
TP 对数据的插入和更新都做了高度封装:提供了 create() 方法用于创建数据对象。
__ROOT__ 网站根目录地址
__APP__ 当前项目(入口文件)地址
__URL__ 当前模块地址
__ACTION__ 当前操作地址
__SELF__ 当前 URL 地址
Lib文件夹:应用类库目录,在这文件夹内还有两个文件夹:Action和Model,Action目录放置命名为xxxAction.class.php的控制器文件,Model目录放置对应数据库表的命名为xxxModel.class.php的类文件。
概念:数据对象 —— 数据字段与数据表之间的关系,数据会被映射为类成员,再与数据表映射,最后实现数据的插入或更新。
3 设置模板变量 {$title} ,增加add操作方法
Logs文件夹:项目中自动产生的日志文件会存放在这里。暂不必理会。
create() 方法是连贯操作、CURD 操作的集合 ( 包括数据创建、数据检验、表单验证、自动完成等 )。
打开Myapp/Lib/Action/IndexAction.class.php文件,修改代码如下
Temp文件夹:数据缓存目录,存放项目中自动生成的项目运行缓存文件等,以及使用文件方式时的缓存文件等
cerate() 的数据源由 POST 表单提供,比如表单中有 username 表单元素,则该元素会被自动映射为数据表中的 username 字段。数据对象创建成功以后,对象被存放于内存中。
Tpl文件夹:模板文件目录,内有一个default文件夹,也就是默认的风格。
项目生成的目录结构就先简单介绍到这里,下一步我们要简单地对项目进行最基本的配置。
例:添加数据
代码知识要点:
模板变量赋值前面的章节已介绍过。这里就不啰嗦了。
三创建数据表,并对项目进行配置,以便连接到数据库。
控制器:IndexAction,动作:add_article,代码:
我们来看看add方法的代码。
$Form = D("Form");
这里使用了TP的特色单字母函数D,是操作数据库时最常用的TP函数,表示实例化Form对象,
即$Form = new FormModel();
D函数的具体代码可以查看ThinkPHP目录内的common/function.php中的片段,
它会自动引入Model类,并判断之前如果实例化过这个Model,就不再实例化,若然该Model不存在,就会抛出异常错误,另外,D可以跨项目访问Model,暂且忽略。
1
我们这里使用的是Mysql数据库,利用PhpMyadmin新建一个数据库,名称为myapp。
使用示例中心中的sql如下:
IndexAction.class.php:
$Form->create()
使用Model类的Create方法创建一个Form对象,失败会返回false。
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
<?php class IndexAction extends Action { Public function add_article(){ $this->display(); } }
$Form->add(); //add方法会将表单数据进行写入
$this->redirect(); //执行跳转
就这么简单几行代码,便完成了对数据插入的处理。
复制代码
视图: Tpl/Index/add_article.html:
2
在Conf文件夹内,建立一个config.php文件。这个文件,就是Myapp项目的配置文件。系统会自动加载
config.php文件中的配置是以数组返回方式进行定义,会覆盖TP框架中的common目录下convention.php的默认配置。没有设置的配置,就以默认为准。配置设置可以利用C函数动态改变,暂且不必理会。
而我们连接到数据库的设置也是在这个文件中书写。
<!DOCTYPE html> <html> <body> <form method="post" action="__URL__/add"> <input type="text" name="title" placeholder="标题"><br><br> <textarea name="content" id="content" placeholder="内容"></textarea><br><br> <input type="submit" name="submit" value="提交"> </form> </body> </html>
config.php代码如下:
__URL__/add 表示当前控制器的 add 动作。
'DB_PORT'=>'3306',
'DB_PREFIX'=>'think_', // 数据表表名的前缀 请参看
);
?>
add 动作代码:
复制代码
<?php class IndexAction extends Action { public function add(){ //表单处理 $articleObj = M('Article'); $articleObj->create(); $articleObj->add_time = date("Y-m-d H:i:s",time()); if($articleObj->add()){ $this->success("数据添加成功"); }else{ $this->error("数据添加失败"); } }
暂时就是这样,后面会再根据需要进一步添加和讲解。
例子结束。
3
在Lib/Model目录下,创建一个文件,命名为FormModel.class.php。命名规则刚才你已看了两次。
这里就不再多说,我们可以看到,数据表名是think_form,由于我们配置了数据表表名的前缀为think_,
所以文件的命名直接使用 "不含前缀的数据表表名 Model.class.php"就行了。
在这个文件中书写代码:
4.模型属性( Model )
复制代码
_map 属性:字段映射
就这样定义一个类名就行了。该类继承了Model类。至于自动验证,自动过滤,自动填充这些,暂且不理会。
为了避免前台表单元素与数据库字段名相同而使数据库字段暴露,可以修改前台表单元素的 name,同时在 Model 中使用 _map 属性。代码:
4 提前先爽一下吧。再次利用PhpMyAdmin,在该表中插入一些数据
sql如下:
视图:Tpl/Index/add_article.html:
<!DOCTYPE html> <html> <body> <form method="post" action="__URL__/add"> <input type="text" name="subject" placeholder="标题"><br><br> <textarea name="textEdit" id="content" placeholder="内容"></textarea><br><br> <input type="submit" name="submit" value="提交"> </form> </body> </html>
复制代码
模型:ArticleModel.class.php:
然后我们打开Myapp/Lib/Action/IndexAction.class.php文件,将里面的内容删掉。改成下面这样:
<?php class ArticleModel extends Model{ //使用_map属性(字段映射)将表单元素映射为相应表字段 protected $_map = array( "subject"=>"title", "textEdit"=>"content" ); }
控制器:IndexAction.class.php:
复制代码
<?php class IndexAction extends Action { $articleObj = D("Article"); $articleObj->create(); $articleObj->add_time = date("Y-m-d H:i:s",time()); if($articleObj->add()){ $this->success("数据添加成功"); }else{ $this->error("数据添加失败"); } }
打开浏览器,输入
例子结束。
今天先到这里,下一步我们继续循序渐进。让我们一起把这个Form最后变成一个有会员功能的留言板。
二、基础模型
1.连贯操作
[教程] 一步一步循序渐进学习TP二(新手篇)
连贯方法除了 select() 方法外,其他的连贯方法不区分前后顺序。
例:
由于时间关系,此系列共同学习教程更新的速度会比较慢些,请多见谅,上一章节的内容请看这里。
$rows=$obj->where("add_user='dee'")->order("add_time desc")->limit(10)->select();
PHP开发,无非是对数据库使用了逻辑控制的增删改查和使用模板输出数据内容。
通常数据的插入都是通过表单来进行添加。表单提交涉及到页面显示,
所以这一节我们暂时放下对数据库的操作讲解,先来简单学习一下TP的模板引擎的变量输出。
2.CURD
第四节简单了解TP的模板输出
上一章节我们提及到,
① 创建数据 add()
TP中的每一个xxxAction.class.php文件代表着一个应用模块,此Action中的每一个方法(function)代表着一个操作,操作分为有输出到模板的操作和只具执行不需要输出的操作。
<?php class IndexAction extends Action { public function post(){ $articleObj = D("Article"); $data['title'] = $_POST['subject']; $data['content'] = $_POST['textEdit']; $data['add_time'] = date("Y-m-d H:i:s",time()); if($articleObj->add($data)){ $this->success("数据添加成功"); }else{ $this->error("数据添加失败"); } } }
打开Myapp/Lib/Action/IndexAction.class.php文件,我们看看里面的基础代码
另一个例子:
<?php class IndexAction extends Action { public function post(){ $articleObj = D("Article"); $data['title'] = $_POST['subject']; $data['content'] = $_POST['textEdit']; $data['add_time'] = date("Y-m-d H:i:s",time()); if($articleObj->data($data)->add()){ $this->success("数据添加成功"); }else{ $this->error("数据添加失败"); } } }
复制代码
理论知识:
② 更新数据 save()
1 在TP开发中,要增加一个应用模块,就在Action文件夹里建立一个类,类的文件命名格式是模块名称 Action.class.php。例如我们这里的应用模块是Index,所以定义文件名为IndexAction.class.php
<?php class IndexAction extends Action { public function post2(){ $articleObj = M("Article"); $data['id'] = 2; $data['title'] = $_POST['subject']; $data['content'] = $_POST['textEdit']; $data['area'] = '河北'; $data['add_time'] = date("Y-m-d H:i:s",time()); if($articleObj->save($data)){ $this->success("数据修改成功"); }else{ $this->error("数据修改失败"); } } }
2 应用模块类的定义要继承框架的Action类。要为这个应用模块添加一个操作,则定义一个以此操作为命名的function.例如上面的index操作。
通常一个应用模块中,会有若干操作(function)需要有与用户交互的页面,这就需要用到模板输出,
TP本身已内置了一套具有TP特色的,很强大易扩展但应用非常方便兼简单的模板引擎。
在应有模块中,如果某个操作是需要页面显示的,只要对应在Myapp/Tpl/default/里建立一个文件夹,文件夹以应用模块的名称来命名,然后在这个文件夹下,建立一个以这个function名称来命名的html文件,就可以在这个方法中使用$this->display()方法来直接调用该模板。(当然也可以调用其它模块下的其它模板或显式指定模板文件位置和名称,由于是循序渐进式的学习,就让我们先忽略吧)
③ 读取数据 select 和 getFiled
了解这些理论后,我们先简单实操一下这些知识。
getField 例子:
1 在Myapp/Tpl/default/下建立一个文件夹,根据应用模块的名称,我们将这个文件夹命名为Index
<?php class IndexAction extends Action { public function post3(){ $articleObj = M("Article"); if($articleObj->where("id=3")->setField("content","{$_POST['subject']}")){ $this->success("数据修改成功"); }else{ $this->error("数据修改失败"); } } }
2 在Myapp/Tpl/default/Index/下建立一个html文件,根据操作名称,我们命名该文件为index.html
3 打开Myapp/Lib/Action/IndexAction.class.php文件,修改代码为
④ 删除数据 delete
<?php class IndexAction extends Action { public function delete(){ $articleObj = M("Article"); if($articleObj->where("id=".$_GET['id'])->delete()){ $this->success("数据删除成功"); }else{ $this->error("数据删除失败"); } } }
复制代码
代码知识要点(摘自手册:ThinkPHP模板指南,此后的知识要点均来自TP官方手册,不再申明)
3.查询语言
在Action类里面使用 assign方法对模板变量赋值,无论何种变量类型都统一使用assign赋值。
例子:
<?php class IndexAction extends Action { public function archives(){ $obj = M("Archives"); // $data['writer'] = array("eq","网络营销中心"); // $data['title'] = array("like","精诚%"); $data['id'] = array("lt","100"); $rows=$obj->where($data)->select(); $count=$obj->where($data)->count(); $tb=C("DB_PREFIX"); $this->assign("list",$rows); $this->assign("count",$count); $this->assign("tb",$tb); $this->display("Article"); } public function archives2(){ $obj = M("Archives"); $data['id'] = array(array("lt","1034"),array("GT","1029"),"and"); $rows=$obj->where($data)->select(); $this->assign("list",$rows); $this->display("Article"); } }
复制代码
// 下面的写法是等效的
可以在控制器中使用 echo $obj->getLastSql(); 查看转换后的 SQL 语句。
复制代码
参考资料:《PHP MVC 开发实战》
// 模板变量赋值后就需要调用模板文件来输出相关的变量,模板调用通过display方法来实现
复制代码
4 打开Myapp/Tpl/default/Index/index.html文件,代码为
复制代码
代码知识要点:
模板变量使用{$变量名称}这种标签进行输出。
不同的模板变量类型,使用不同的标签,标签可以自行另外定义,暂且不理会。
5
打开浏览器输入地址:
附加补充知识:
1 如果要同时输出多个模板变量,可以使用下面的方式:
复制代码
这样,就可以在模板文件中同时输出name、email和phone三个变量。
2 我们使用上面的变量定义,将整个数组定义为一个模板变量来输出
复制代码
在html中,要输出$array['name']的值,代码是
复制代码
3 将这个数组循环输出
3.1 IndexAction.class.php中代码更改如下
}
?>
复制代码
3.2 将Myapp/Tpl/default/Index/index.html代码更改如下:
</iterate>
</body>
</html>
复制代码
代码知识要点:
name='array'是指要循环的模板变量是array,id='vo'是指这个数据在模板输出时所使用的名称
对于TP的模板引擎输出先简单了解到这里。
[教程] 一步一步循序渐进学习TP三(新手篇)
第一节 快速开始一个项目
第二节 简单认识项目里的各个文件夹
第三节 配置项目,连接数据库。
第四节 简单了解TP的模板输出
第五节初步了解URL如何访问模块操作
通过前面的学习,我们已大概地明白了应用模块中的操作与模板如何对应。
而这一节将简单讲解如何通过URL来访问操作。由于我们要循序渐进,所以先只介绍下面章节要用到的知识,要想深入学习还请参看官方手册《URL设计和SEO支持》
TP支持四种URL访问方式,默认是智能模式,所以我们就这种模式进行简要说明。
智能模式下的URL基本结构是这样的
你可以理解为
http:// 服务器地址 / 入口文件位置 / 应用模块名称 /具体操作名称/ GET变量参数
解释一下上面文字中所译的URL结构,
为什么appname项目名称我将它译成了入口文件位置:通常情况下,我们的入口文件index.php都是放在根目录中,这时项目名称就会被入口文件index.php所替代。在前几节里,我们已知道,一个Action文件就是一个应用模块,而应用模块中的每个方法(function)就是一个具体操作。因此,假如要访问我们实例中的Index模块下的index操作。地址应该是
如果入口文件不是在服务器的根目录,而是在myapp目录下,那么入口文件位置就是
Myapp/index.php,此时上面的URL访问就相应要换成
这样说理应很清楚了,不过别让这么多index给混乱咯,对应URL的结构去明白每一个index的函义,同时注意大小写
简单实操:
1 打开Myapp/Lib/Action/IndexAction.class.php文件,在index这个function下再添加一个操作test
} // 类定义end
?>
复制代码
2 尝试对应URL结构,来访问这个test操作,如果能成功看到echo的文字。则这节我们又学会了多一点知识。
附加知识点:
去掉URL里面的index.php
要去掉URL里的入口文件index.php,使URL类似这样的形式
可以按官方手册所提供的方法进行如下操作。
1 确认httpd.conf配置文件中加载了mod_rewrite.so模块
2 AllowOverride None 将None改为 All
3 打开Myapp/Conf/config.php文件,在配置的数组中添加一行:
'URL_MODEL'=>2,
在入口文件所在的同级目录下,新建一个.htaccess文件,内容是
复制代码
如果你的服务器环境支持rewrite,使用
此后我们的学习中用到的URL,都是假定你使用了rewrite的情况。
第六节 CURD知识之一 C 创建(create)
.htaccess.rar (248 Bytes)
第一节 快速开始一个项目
第二节 简单认识项目里的各个文件夹
第三节 配置项目,连接数据库。
第四节 简单了解TP的模板输出
第五节 初步了解URL如何访问模块操作
注意:看到不少留言说操作不成功或出错,请别忘记,此系列教程出来时,TP1.6还没出来,所以系列6之前所介绍的示例和操作都还是标准版本1.5的,如果你使用1.6版,请对应两者差异性。
例如submit不能有name属性,是因为原来1.5有字段缓存进行过滤处理。1.6没有了字段缓存。
例如数据自动验证和自动填充都有了一定的区别。
第六节 CURD知识之一 C 创建(create)
从这一节开始,我们就要涉及到数据库操作,TP对数据库的操作非常简便,以下是一些基础知识讲解。
名词解析:
1 CURD:代表数据库四个基本操作:创建:create,更新:update,读取:read,删除:delete
2 模型对象:即数据对象,你项目里每一个Model类文件都会对应着一个数据表(或者视图),模型与数据表存在一种映射关系。TP约定了命名要遵循一定的规范,如果不符合,则需要根据情况进行额外的相应设置。例如Model类的tableName属性
在第三节时,曾略为提到Model类文件命名,现在再回顾一下这部分的内容:
我们之前在数据库中,已建立了一个think_form数据库表,并且在配置文件config.php中,我们定义了数据表的前缀是think_,
模型类(Model)文件的命名规则是:
不包括前缀的数据库表表名并且首字母大写 Model.class.php
所以之前我们为think_form数据表在Myapp/Lib/Model目录下建立一个文件FormModel.class.php
模型类的特殊命名还可以智能识别驼峰式的表命名,假设我们有个是类似think_new_table这样的表
可以命名为NewTableModel.class.php。默认配置便可以智能识别自动对应think_new_table表,因此不必修改配置。
开发过程中,只要简单地定义好与数据表对应的模型类(xxxModel.class.php),就可以进行对数据操作了。
如何让这个模型支持自动验证,自动填充,自动过滤这些知识,在下面涉及到时会作相应讲解。
一般来说,在表中添加数据都是通过表单来建立,为了更直观地学习CURD中的C,我们接下来建立个表单来进行添加数据的操作。
之前我们已在Myapp/Lib/Model/下建立好FormModel.class.php类----模型(M)
同时也在Myapp/Lib/Action/下添加了一个Index应用模块和写下了一个index操作----控制器(C)
并且在Tpl目录下建立了与之对应的模板目录Index及index.html模板文件 -------视图(V)
MVC模式的开发已体现了出来。
tdweb曾简明扼要地概括了TP中这三者的关系,
C的作用,就是从M中取得数据到V,
使用TP以表单提交数据到库,流程和你所了解的表单提交没有什么区别,只是TP简化了数据操作的处理过程。
我们在实际操作中体会一下。
先来看看我们所定义的数据表form的字段:
复制代码
其中的create_time字段是为了记录数据插入的时间,我们可以利用TP的数据自动填充来处理。
在Model类定义 $_auto
属性,可以完成数据自动处理功能,用来处理默认值和其他系统写入字段。
注意1:该自动填充可能会覆盖表单提交项目。其目的是为了防止表单非法提交字段。
注意2:要使用Model类的create方法创建数据对象的时候才会自动进行表单数据处理。
1 打开Myapp/Lib/Model/FormModel.class.php文件,更改代码为
复制代码
代码知识要点:
Model类的$_auto属性由多个填充因子组成的数组,填充因子定义格式:
array(填充字段,填充内容,填充条件,附加规则)
填充字段:就是需要进行处理的表单字段,这个字段并不一定要是数据库表中的字段,对于表单内的辅助检测字段比如重复密码和验证码也可以处理。
填充条件:ADD | UPDATE | ALL(注意,此是1.5版本说明,1.6已改为1 2
3,后续教程有说明)
当为ADD时,会在新增数据时自动填充,这是默认的处理方式
当为UPDATE时,在更新数据的时候会自动填充
当为ALL时,所有情况下都会进行自动填充
附加规则:附加规则是针对填充内容而言,表示该内容填充的方式,包括function,callback,field,string
对于field使用其它字段进行填充和string直接标示字符串作为值进行填充很好理解。例如上面
复制代码
就是将状态status字段的值直接以1填充.
下面主要说说function与callback这两个附加规则。
复制代码
上面create_time代表了要处理的字段,填充内容是time,附加规则是function使用函数,填充条件是ADD新增时处理,那么整行代码表示对create_time字段在新增的时候使time函数作为该字段的值进行自动填充。
再看一个function作为附加规则的例子
复制代码
当使用function作为附加规则时,第二个填充内容就代表了这个因子是一个函数名称,该函数的参数就是代表填充字段的值,比如password的值是123456,则上面的代码会先将这个值使用函数md5('123456'),这样处理后再插入到数据表中去。
对于function(函数)和callback(回调方法)理论是一样的,只不过一个是表示填充内容所写的是函数名,另一个是表示填充内容是类中的方法名。函数可以是PHP5内置函数或你自己所写的函数。而callback是当前模型类所能调用的一个方法。 有关数据填充方面的详细资料,请参看官方使用手册
《ThinkPHP数据操作指南》
2 打开Myapp/Tpl/default/Index/index.html文件,我们修改代码将form写进去
</form>
</body>
</html>
复制代码
在上面代码中,我们简单地建了一个form表单,并将标题统一使用一个模板变量{$title}。
action提交到的处理地址是__URL__/add,其中__URL__是一个常量定义,表示当前模块地址,TP模板引擎会自动将这句解释为/index.php/Index/add,常见的模板使用的常量有
__ROOT__ 网站根目录地址
__APP__ 当前项目(入口文件)地址
__URL__ 当前模块地址
__ACTION__ 当前操作地址
__SELF__ 当前 URL 地址
3 设置模板变量 {$title} ,增加add操作方法
打开Myapp/Lib/Action/IndexAction.class.php文件,修改代码如下
}//类定义 end
?>
复制代码
代码知识要点:
模板变量赋值前面的章节已介绍过。这里就不啰嗦了。
我们来看看add方法的代码。
$Form = D("Form");
这里使用了TP的特色单字母函数D,是操作数据库时最常用的TP函数,表示实例化Form对象,
即$Form = new FormModel();
D函数的具体代码可以查看ThinkPHP目录内的common/function.php中的片段,
它会自动引入Model类,并判断之前如果实例化过这个Model,就不再实例化,若然该Model不存在,就会抛出异常错误,另外,D可以跨项目访问Model,暂且忽略。
$Form->create()
使用Model类的Create方法创建一个Form对象,失败会返回false。
$Form->add(); //add方法会将表单数据进行写入
$this->redirect(); //执行跳转
就这么简单几行代码,便完成了对数据插入的处理。
现在我们可以尝试一下在表单中输入数据进行提交试试了。
请先随便插入几条数据,以便下一节我们继续学习CURD中的R
很抱歉!不得不出这一篇过渡文章。
为了让这系列教程不致于日后会误导,从这节开始,让我们转型到1.6版的使用方式上。
但是对于1.6版的使用,我也是在摸索当中学习,错误和不足之处肯定会比较多,
故恳请流年大哥和tdweb,东至,yhustc等大大可以及时指正和补充,在此先谢谢了。
说明1 从这节开始,TP或dev这两个简称指的都是ThinkPHP 1.6版,
可以到svn下载。
下载后删除原来的ThinkPHP框架文件,将dev同样命名为ThinkPHP,并仍是放置于网站的根目录下。
另外,为了方便需要时可以对实例截图而不引起读者你误解,基于我的网站目录结构,现将Myapp的index.php位置更改到Myapp文件夹下(不再是原来的根目录).
步1:将根目录中的原index.php剪切并粘贴到Myapp文件夹下。更改代码为:
?>
复制代码
代码补充注释:代码中项目名称其实是可以不必定义的,系统会自动根据当前的目录名定义名称。
步2: 将.htaccess文件复制一份粘贴到Myapp目录下。
步3: 将Cache目录,Temp目录,Data目录清空。
说明2 第五节中对于URL访问的默认模式
在dev版默认配置中,dispatch即URL调度功能是关闭的,所以在默认模式下,要访问Index模块的index操作,地址应是:index.php?m=index&a=index,m指的是模块变量(Model),a指的是操作变量(Action)。
如果要使用类似第五节中的index.php/moduleName/actionName方式,则要在config.php文件里设置DISPATCH_ON为true;
并且,也只有开启了此功能后,URL_MODEL的设置才会有效。
(在上面的章节我们提及过,可以设置URL_MODEL为2来使用rewrite功能去除显示index.php)
所以,现在我们的config.php代码是
);
?>
复制代码
代码补充注释:此节之后的内容会涉及到模板输出及数据操作,所以在这里明确设置了模板变量为数组,并开启TP的调试模式。
注意3:第六节 CURD知识之一 C(创建)中的自动填充
dev版的自动填充功能已移到AdvModel类中。要使用此功能,我们需要在Myapp/Lib/Model类文件中引入该类.
那么再来看看我们所定义的数据表form的字段:
复制代码
引用tdweb手记:
如果需要新建数据时自动填入时间戳,只需要设置数据表字段名"create_time",如果需要修改时自动保存时间戳,那么设置数据表字段名为"update_time"。
如果你的数据表字段名不是默认的,那么需要指定,例如
protected $autoCreateTimestamps = 'ctime';
protected $autoUpdateTimestamps = 'utime';
故此,让我们打开Myapp/Lib/Model/FormModel.class.php文件,将代码更改为如下:
array('status','1'),//自动填充的定义规则请参看第六节内容 填充规则有所不同,见附注
);
}
?>
复制代码
代码补充注释:
注意:dev版的填充条件改为1 新增 2 更新 3
包含1和2,默认为新增。所以定义更方便了.而使用ADD | UPDATE |
ALL会无效的(TO流年大哥:TP代码还有一个特色就是函数或方法的命名很符合语言使用习惯,原来的add,update,all很直观,易记,可惜没了)
array('field','填充内容','填充条件','附加规则',[额外参数])
alias_import($alias,$classfile='')快速定义和别名导入
这是新增的函数,我尝试注解一下,不正确请指正。
该函数可以动态设置文件列表导入和直接导入文件,常会用到的文件别名有
'Page' 分页类 Page.class.php',
'ViewModel' 视图模型类 ViewModel.class.php',
'AdvModel' 高级模型类 AdvModel.class.php',
'RelationModel' 关联模型类 RelationModel.class.php',
当要引入已定义别名的文件,我们只要直接在参数中传递别名就行了。
别名可以自行在ThinkPHP/Common/alias.php中扩充。
除了使用已定义的别名外,也可以使用动态指定。
复制代码
或一次引入多个文件
复制代码
注:指定文件或别名不存在时会返回false
类似的函数还有
import($class,$baseUrl = '',$ext='.class.php')
流年大哥语:由于性能问题,这个方式以后不再支持
这是ThinkPHP内建的类库和文件导入的一个函数,具有缓存和检测机制,相同的文件不会重复导入,冲突时会提示错误。使用这方法时,系统会自动识别导入类库文件的位置:
Think 代表TP框架基类库 即ThinkPHP/Lib/Think目录
ORG 代表第三方公共类库 即ThinkPHP/Lib/ORG目录
@ 代表当前项目类库,例如Import("@.Action.xxxAction")
----即Myapp/Lib/Action/xxxAction.class.php
@指的是当前项目名称,这和使用Import("Myapp.Action.xxxAction")是一样的。
TP的约定是Think、ORG等导入的是以系统(TP)基类库为相对起始目录,否则就认为是项目(Myapp)应用类库为起始目录。
注意1 使用时要注意文件名的大小写
注意2
Import会自动将.转换为/,如果文件名中含有.,则要将.改为#才能正常导入。
注意3 导入的类文件后缀默认是.class.php
2 第三方框架类库导入:vendor($class,$baseUrl = '',$ext='.php'), 起始目录统一是ThinkPHP/Vendor
默认后缀是.php
注:vendor也使用了Import函数。因此...
说明3,原来的模板文件index.html保持不变,因为我们已启用了DISPATCH_ON开关。
仅将Myapp/Lib/Action/IndexAction.class.php代码更改如下
}//类定义 end
?>
复制代码
代码补充解释:
select($options=array());
为了方便查看数据,index操作中使用到了一个基本查询方法
tdweb语:select()方法代替了之前比较流行的findAll(),不传任何参数则是获取全部数据。
我们仍是循序渐进,具体关于查询的方法会在用到的时候陆续一起学习。
dump($var, $echo=true,$label=null,
$strict=true)函数。浏览器友好的变量输出。
当只有一个$var参数时,会直接打印这个$var。
dump($Form);会输出类似
}
复制代码
也可以设置参数
dump($要输出的变量, 是否直接打印0或1,变量说明 ,
是否以正规数组形式0,1)
例如:
复制代码
输出类似
)
复制代码
$vo = $Form->create();
创建数据对象 但不保存到数据库
模型类的create方法,如果没有设值,则默认是通过表单提交的数据$_POST进行创建。
同时Create方法也支持从其它方式例如数据对象或者数组等来创建,
对这方法与tdweb的说法有点不一样,望指正。
由于简洁版并不自动生成数据表data缓存,所以如果你用create()而不给他传递任何值,仅仅依靠$_POST得到值,那么不管你的表单POST什么,程序都会认为是数据库的字段拼入QL,所以,如果你POST过来的数据而数据表里没有这个字段,则会出现添加失败。
但我看到代码里似乎已自动过滤不符合的数据,并测试过添加数据表里没有的字段没有出现添加失败。
redirect('/Myapp/',2,'数据添加成功!'); 跳转函数。
函数原型:redirect($url,$time=0,$msg='')
看参数就已经很清楚了。
过渡篇就先到这里,建议大家多点动手实际操作和看看源代码。
下一节我们再继续,请各位务必多多指教。
在上一节实操中,我们已成功插入了多条测试的数据,但是很明显,我们发现其中的邮箱地址一项不符合格式也可以提交,这当然不是我们所希望见到的,这时我们可以利用TP的数据自动验证功能去控制用户输入。
而之前已提交的数据,也可以通过编辑数据将原来错误格式的邮箱地址重新更改为正确的email,这些处理让我们都放在下一节的数据编辑中一起学习。
在此之前,先来看看TP最基本的数据查询。
第 8 节数据查询(CURD之R)
TP的查询很人性化地分成单数据返回或多数据返回,视乎自己项目过程中的实际需要,按照这系列教程的学习习惯,我们仍是在实际操作中去了解,以代码来学习,一起动手吧。
1 先将Myapp/Lib/Action/indexAction.class.php代码稍作修改,以方便更直观地查看查询结果。
复制代码
2 在第四节时,我们了解过模板变量的循环输出,现在使用这知识点将对应的模板文件Myapp/Tpl/default/Index/index.html修改如下
.submit{height:2em;padding:0 5px;cursor:pointer;}
</style>
</head>
<body>
<form action="__URL__/add" method="post" name="formname" id="formname" class="box">
<p>
<p><input name="submit" type="submit" value=" 提交 " class="submit" /></p>
</form>
{//Form表数据循环输出}
<iterate name="data" id="vo">
<div class="box">
<h3>NO.{$vo.id}:{$vo.title}</h3>
<p>-----------由 {$vo.email} 发表于: <em>{$vo.create_time|date='Y-m-d',###}</em></p>
<p>内容:</p>
<p>{$vo.content}</p>
</div>
</iterate>
</body>
</html>
复制代码
在浏览器中输入
这时我们只要每输入一条数据信息,就会按照id倒序的方式显示在模板上。
---恢复内容结束---
本文由pc28.am发布于计算机编程,转载请注明出处:ThinkPHP学习笔记1,自定义基类