# 间接读取数据
直接读取数据虽然简单易懂,但往往在大型的项目中却并不适用。如果实现一个功能,我们把所有的代码都写在一个方法中,显然当功能复杂时,代码就会越来越长,越来越难以维护。这显然不是我们想看到的。
基于此,我们有了数据库M层,一个专门为我们处理数据表的文件。
## M 数据模型层
没有M层以前我们取数的过程是这样的。
![](https://box.kancloud.cn/2016-06-13_575e5bec70780.png)
有了M层以后,我们取数的过程变成了这样。
![](https://box.kancloud.cn/2016-06-13_575e5bec83b8d.png)
是的,你想的没错,在小型的项目,或是我们这个教程中,这样做的确是画蛇添足了。但是我们的目标,并不仅仅满足于做小项目。后面你将会一点点的感受到分层的魅力。
## 调用谁,你就要USE谁
我们再看前面写过的代码:
![](https://box.kancloud.cn/2016-06-13_575e5bec95ec4.png)
如果删除或注释掉第3行。
就会报如下错误:
![](https://box.kancloud.cn/2016-06-13_575e5becb7ceb.png)
报错信息中提示这个类 'app\index\controller\DB' 没有找到。
原因就是我们在用Db类以前,没有告诉程序这个正确的Db类在哪个位置,怎么告诉呢?利用的就是USE。
那我们说,你怎么就知道要这么use呢?
我们先看一下,Db这个类到底在哪。
![](https://box.kancloud.cn/cfa27f6809b7dd160a110dddfc174837_254x650.png)
其实它在哪并不要紧,PHP中有一个叫命名空间的东西,至于怎么工作的,有兴趣的GOOGLE吧。我们只需要知道它叫命名空间就可以了。如下图所示:
![](https://box.kancloud.cn/2016-06-13_575e5bed00748.png)
命名空间,可以简单的理解为:我住哪儿。
我们看到Db这个类,它住在了`think`中,它的名字叫`Db`,所以别人想找它帮忙的时候,只需要`use think\Db;`就可以了。
当然了,我们自己写的类,由于也有命名空间,别人也可以通过我们的类住在哪儿,叫什么名字,来use我们的文件。
其实想搞清楚命名空间这个神奇的东西并不容易,所以搞不太清楚没有关系,我们现在只需要知道 **调用谁,就USE谁** ,就可以了。慢慢的,我们会一点点的明白什么是命名空间。
### 调用M层,我们就要USE M层
#### 建立M层文件
上面我们是使用了Db这个类,这次我们放弃Db类。在实际的开发过程中,大多数的数据表都是固定的(有些项目需要动态生成数据表),所以我们也很少会去用这个Db类。基于此,我们对Db类的了解,到此为止。
这次我们引用think内置的一个类think\Model,这个类中封装好了所有的数据库操作方法。我们将采用继承的方法,如果现在不太清楚什么是继承,可以直接往下看,但我们有必要去学一学C++了。
代码如下:
~~~
<?php
// 简单的原理重复记: namespace说明了该文件位于application\common\model 文件夹中
namespace app\common\model;
use think\Model; // 导入think\Model类
/**
* Teacher 教师表
*/
// 我的类名叫做Teacher,对应的文件名为Teacher.php,该类继承了Model类,Model我们在文件头中,提前使用use进行了导入。
class Teacher extends Model
{
}
~~~
我们再来巩固一下namespace,这个叫做命名空间的东西。
app\common\model; 是指这个文件位于application文件夹下的common文件夹下的model文件夹中。
所以当我们看到以app打头的命名空间时,也就相当于告诉了我们这个文件位于哪个位置。
类名必然与文件名相同。比如Teacher类,必须对应Teacher.php文件。
所以根据以上的代码,我们应该能够快速的定位到该代码应该处于: `application\common\model\Teacher.php`文件中。
查看**application**目录树
~~~
├── command.php
├── common
│ └── model
│ └── Teacher.php
├── common.php
├── config.php
├── database.php
├── index
│ └── controller
│ ├── Index.php
│ └── Teacher.php
├── route.php
└── tags.php
~~~
最后我们是通过 extends 来继承的,因此,不仅仅我们在调用这个类之前需要进行use,我们在继承一个类之前也需要use。
#### 调用Teacher.php 模型文件
我们先回到C层,即controller文件夹下,对原Teahcer.php进行重新编辑,去除对Db类的调用。
> 我们需要记往一点,只有controller文件夹下的文件,才可以通过URL访问的到。其它的文件,比如我们刚刚新建的这个M层的Teacher.php,是直接访问不到的。
删除冗余的代码后如下:
~~~
<?php
namespace app\index\controller; // 该文件的位于application\index\controller文件夹
/**
* 教师管理
*/
class Teacher
{
public function index()
{
echo 'hello teacher';
}
}
~~~
输入URL进行测试结果显示如下:
![](https://box.kancloud.cn/2016-06-13_575e5bed4c317.png)
下面,我们正式调用Teacher模型。
1、先use (导入)
2、再调用
示例代码如下:
~~~
<?php
namespace app\index\controller; // 该文件的位于application\index\controller文件夹
use app\common\model\Teacher; // 教师模型
/**
* 教师管理
*/
class Teacher
{
public function index()
{
$Teacher = new Teacher;
dump($Teacher);
}
}
~~~
经测试,我们将得到如下错误提示:
![](https://box.kancloud.cn/2016-06-13_575e5bed5fa87.png)
~~~
git checkout -f step3.2.4.1
~~~
执行上述命令后,上述示例代码信息如下:
![](https://box.kancloud.cn/cd2587c989c11dcde5a2c738d5391514_653x307.png)
提示:不能重新声明`app\index\controller\Teacher`这个类,原因是由于这个类已经存在。
> 每个班级里面只能有一个小明,如果存在两个小明,那么他们肯定有一个是**大小明**,另一个是**小小明**。
在日常使用电脑时,也是如此。当我们尝试在同一个文件夹中建立两个同名文件(包含扩展名)时,就会报错。
有人说,老师我们并没有在同一个文件夹中建立`Teacher`呀,按上面的理论,应该不报错才对。这里,就要说下use这条语句了,当它执行`use app\common\model\Teacher;`时,我们可以简单理解为:将`application\common\model`文件夹下的`Teacher.php`文件中定义的`Teacher`类导入到当前文件。
此时,由于导入的类名叫做Teacher,然后我们自己的名字也叫做Teacher,就出现了名字发生冲突的错误。
所以简单来说:上述出现错误的原因,是由于这两个类起的名字是相同的。
![](https://box.kancloud.cn/4eb08b4e6a8ba95f3543d6491935999f_436x352.png)
#### 解决方法1
为导入的那个Teacher 改个名字
~~~
<?php
namespace app\index\controller; // 该文件的位于application\index\controller文件夹
// 导入app\common\model\Teacher模型,并给它重新起个名字:SmallTeacher
use app\common\model\Teacher as SmallTeacher; // 教师模型 带有别名
/**
* 教师管理
*/
class Teacher
{
public function index()
{
$SmallTeacher = new SmallTeacher;
dump($SmallTeacher);
}
}
~~~
~~~
git checkout -f step3.2.4.2
~~~
执行上述命令后,上述示例代码信息如下:
![](https://box.kancloud.cn/2016-06-29_577343c45a825.png)
#### 解决方法2,更改thinkphp的命名规则,不给他们发生冲突的机会。
1、修改config.php
在config.php 找到 controller_suffix 项,并配置为 true:
~~~
// 控制器类后缀
'controller_suffix' => true,
~~~
config.php 位置application根目录下。
> 细心的你可能早就发现了一个问题:为什么这个文件就没有命名空间呢?
是的,它没有,但是我们也没有看到里面有Class的字样呀,既然它不是一个Class(类),没有命名空间就是正常的。
2、修改原来C层的文件名
Teacher.php -> TeacherController.php
当然了,同为控制器的Index.php,也要修改成IndexController.php。由于类名与文件名相同,所以类名当然也要修改了。
修改后如图所示:
![](https://box.kancloud.cn/d0dd7825d4501386228b71a5176a97cf_1086x329.png)
这时候,C层的类名字叫做TeacherController,M层的叫做Teacher,两个名字不一样了,当然也就不需要使用别名了。
刷新URL进行测试:
如果你见到如下错误:
***控制器不存在:Teacher***
产生的原因有以下两种:
1. 我们config.php中的配置信息没有起作用,请检查拼写是否有错误,文件是否进行正常保存等。
2. 文件名或类名命名不正确。
> 我们在以后的学习中,还会遇到上述错误提示,查找问题原因的方向为:1. 命名空间 2. 文件位置 3. 文件名 4. 类名 。
需要重点检查的项如下图所示:
![](https://box.kancloud.cn/e71d9f52244643929c70b269f7ab49dd_1090x329.png)
上图中的错误,你发现了吗?
由于此次我们并没有变动文件的位置,所以命名空间可以确定是没有问题的。那么,如果有问题就应该出现在文件名和类名上了。
#### 正确的测试结果如下图所示:
![](https://box.kancloud.cn/4fb88c6eef32448ab2e4acd73f147f38_666x395.png)
我们看到,第一行提示说在debug文件的169行。显然,我们更希望看到的是在TeacherController文件的第12行,这是由于我们使用了thinkphp内置的dump()方法造成的。在以后的教程中,如果我们在其它的历史项目中看到这个函数,知道它等于var_dump()就可以了。
> 在以后的项目或是教程中,我们将弃用dump()函数,一律改为var_dump()。
最后,我们去除原来的别名。
代码如下:
~~~
<?php
namespace app\index\controller; // 该文件的位于application\index\controller文件夹
use app\common\model\Teacher; // 教师模型
/**
* 教师管理
*/
class TeacherController
{
public function index()
{
$Teacher = new Teacher;
var_dump($Teacher);
}
}
~~~
测试结果:
![](https://box.kancloud.cn/b96fdad49533afeedcf9e4d018e31695_684x389.png)
使用`var_dump()`提示我们,现在发生的位置位于`TeacherController`的第12行。
同时我们还看到`$Teacher`变量的类型是一个`object`,这个对象的原型是`app\common\model\Teacher`。没错,的确是我们引入的那个`app\common\model\Teacher`。
~~~
git checkout -f step3.2.4.3
~~~
执行上述命令后,上述示例代码信息如下:
![](https://box.kancloud.cn/ec2dbb06785e65c966cefacd47df4d6b_1079x297.png)
## 主角登场
使用select()来获取数据库中的信息。
~~~
<?php
namespace app\index\controller; // 该文件位于application\index\controller文件夹
use app\common\model\Teacher; // 教师模型
/**
* 教师管理
*/
class TeacherController
{
public function index()
{
$Teacher = new Teacher;
$teachers = $Teacher->select();
var_dump($teachers);
}
}
~~~
测试结果:
![](https://box.kancloud.cn/9ef0204be7b536a78a71d0d245e2af9a_553x144.png)
~~~
git checkout -f step3.2.4.4
~~~
执行上述命令后,上述示例代码信息如下:
![](https://box.kancloud.cn/eba80b6b4b6d342918bb8e0a4480c731_1112x329.png)
我们看到返回值是个数组,这个数组的第0项是一个对象,第1项也是个对象,这两个对象都是基于`app\common\model\Teacher`这个类创建的。
我们并没在`app\common\model\Teacher`中定义任何属性,那么这些保护类型的私有属性是怎么来的呢?这是由于它继承了一个叫做`think\Model`的类。继承就是这样:你自己虽然没有,但是你父类有就和你有是一样的。在继承中是这样规定的,在父类中所有的protected和public的变量或**方法**,都是可以被子类直接使用。
变量我们看到了,那么我们还可以在这个对象上调用哪些方法呢?
> `var_dump()` 函数可以查看对象中的属性,但却不显示对象中存在的方法。
我们现在就查看一下`think\Model`(think开头的类,位于thinkphp5/thinkphp/library/think文件夹下)中都有什么方法,然后调用一个试试。
![](https://box.kancloud.cn/c8f967270b9c0029dc49612fc1f4882c_1203x669.png)
我们在Model.php文件中,同时按下 `Ctrl+P`,然后输入`@getData`,找到获取对象原始数据getData()方法,如下图所示。
![](https://box.kancloud.cn/af8c5bc6a0a33b42910601c4fb135599_633x469.png)
getData()方法代码如下:
~~~
/**
* 获取对象原始数据 如果不存在指定字段返回false
* @access public
* @param string $name 字段名 留空获取全部
* @return mixed
* @throws InvalidArgumentException
*/
public function getData($name = null)
{
if (is_null($name)) {
return $this->data;
} elseif (array_key_exists($name, $this->data)) {
return $this->data[$name];
} else {
throw new InvalidArgumentException('property not exists:' . $this->class . '->' . $name);
}
}
~~~
在此,不得不说:**越是技术牛气的人,代码越是规范!**
想成为技术大牛吗?从规范代码做起!
修改C层代码:
~~~
<?php
namespace app\index\controller;
use app\common\model\Teacher; // 教师模型
/**
* 教师管理
*/
class TeacherController
{
public function index()
{
$Teacher = new Teacher;
$teachers = $Teacher->select();
// 获取第0个数据
$teacher = $teachers[0];
// 调用上述对象的getData()方法
var_dump($teacher->getData());
}
}
~~~
测试结果如下:
![](https://box.kancloud.cn/9a4a511f4e62844fcece2323e72c7ae5_573x240.png)
为getData()传参:
~~~
// 调用上述对象的getData()方法
var_dump($teacher->getData('name'));
~~~
再测试,结果显示如下:
![](https://box.kancloud.cn/3206fcd698b5987da23ee63380151b38_758x75.png)
增加另外两个直接显示数据的方式:
~~~
// 调用上述对象的getData()方法
var_dump($teacher->getData('name'));
echo $teacher->getData('name');
return $teacher->getData('name');
~~~
测试结果如下:
![](https://box.kancloud.cn/4304e1df387836bd7247a12c7575ff96_755x107.png)
#### thinkphp 模型与数据表的自动关联
细心的你,发现我们并没有指定要去查哪个表,系统自动的就将Teacher表中的数据返回给了我们。
> 这是由于当我们新建的 app\common\model\Teacher,继承了think\Model, 在think\Model中,有个功能是自动关联数据表。也就是说,我们起的类名叫做Teacher,它就自动关联yunzhi_teacher这个表。为什么前面会自动加上yunzhi_ ? 再去找一下database.php吧。
是的,就这么神奇,数据表中的数据就这么过来了。
用Db类的select()方法和我们此处的 select() 方法,获取到的虽然都是一个数组,但一个是数组中的子项还是数组,而另一个是数组中的子项却是对象了。由于我们并不打算使用Db类,所以在此不对这两者的区别做过多的阐述。我们只需要知道,我们是面向对象的编程思路,即:**一切皆对象** 就可以了。
### 重构代码
重新去写自己的代码就叫做重构,所以我们这里也叫重构,但我们重构的目的是越重构越清晰,而在这里重构的目的是希望大家对代码有了更加深入的理解。
重构后代码如下:
~~~
<?php
namespace app\index\controller;
use app\common\model\Teacher; // 教师模型
/**
* 教师管理
*/
class TeacherController
{
public function index()
{
$JiaoShiBiao = new Teacher;
$SuoYouJiaoShi = $JiaoShiBiao->select();
// 获取第0个数据
$jiaoShiZhangSan = $SuoYouJiaoShi[0];
// 调用上述对象的getData()方法
echo '教师姓名' . $jiaoShiZhangSan->getData('name') . '<br />';
return '重复一遍:教师姓名' . $jiaoShiZhangSan->getData('name');
}
}
~~~
测试结果如下:
![](https://box.kancloud.cn/fe4be06cf9c9a7eac2ca5143c038e891_346x101.png)
也就是说$xxxx中的xxxx是变量名,这个名字你随便起,只要前后一致就可以。这些是由我们规定好的,它们在我们代码中第一次出现的位置是表达式的最左侧,所以我们想起什么名字,就起什么名字。
比如下图中这三项,第一次出现的位置是等号的左边,说明他们是我们定义的,我们当然有自主权了,起什么名字我们说了算。
![](https://box.kancloud.cn/2016-06-13_575e5bef7f8d0.png)
但第一次出现的位置是等号的右边的话,那就不一样了。
这个是其他人规定好的,它怎么规定的,我们就需要怎么使用。
最简单的,比如:
~~~
$JiaoShiBiao = new Teacher;
~~~
Teacher这个名字,是我们在use的时候规定好的,在我们的代码中出现在了等号右边,那么我们就必须按规则执行。
比如:
![](https://box.kancloud.cn/2016-06-13_575e5befa4317.png)
为了提升代码的可读性,我们必须统一代码书写的规范。
我们规定大家必须这样写:
~~~
<?php
namespace app\index\controller; // 该文件的位于application\index\controller文件夹
use app\common\model\Teacher; // 教师模型
/**
* 教师管理
*/
class TeacherController
{
public function index()
{
// $Teacher 首写字母大写,说明它是一个对象,更确切一些说明这是基于Teacher这个模型被我们手工实例化得到的,如果存在teacher数据表,它将对应teacher数据表。
$Teacher = new Teacher;
// $teachers 以s结尾,表示它是一个数组,数据中的每一项都是一个对象,这个对象基于Teahcer这个模型。
$teachers = $Teacher->select();
// 获取第0个数据
$teacher = $teachers[0];
// 调用上述对象的getData()方法
var_dump($teacher->getData('name'));
echo $teacher->getData('name');
return $teacher->getData('name');
}
}
~~~
测试结果如下:
![](https://box.kancloud.cn/bd0214b8fc84f6bb1a8252116a534808_755x107.png)
~~~
git checkout -f step3.2.4.5
~~~
执行上述命令后,上述示例代码信息如下:
![](https://box.kancloud.cn/0de925eba2a32d4fe7edba5035d6eca2_1240x556.png)
- 序言
- 第一章 准备知识
- 第一节:XAMPP
- 第二节:NAVICAT
- 第三节:GIT
- 第二章 HelloWorld
- 第一节:下载THINPHP5.0
- 第二节:启动xampp
- 第三节:hello thinkphp
- 第四节:Hello world
- 第五节:MCA
- 第六节:单引号与双引号
- 第三章 教师管理
- 第一节:新建数据库
- 第二节:CURD之R 读数据
- 3.2.1 时序图
- 3.2.2 连接数据库
- 3.2.3 直接读取数据
- 3.2.4 间接读取数据
- 3.2.5 使用V层显示数据
- 3.2.6 引入bootstrap
- 3.2.7 加入bootstrap样式
- 第三节:CURD之 C 增加数据
- 3.3.1 插入数据
- 3.3.2 表单传值
- 3.3.3 存储表单数据
- 3.3.4 自动时间戳
- 3.3.5 增加验证
- 第四节:CURD之 D 删除数据
- 3.4.1 什么是关键字
- 3.4.2 删除数据
- 3.4.3 定制删除链接
- 3.4.4 完善删除
- 第五节:CURD之 U 更新数据
- 3.5.1 读取数据
- 3.5.2 展示数据
- 3.5.3 修改数据
- 3.5.4 传递数据
- 3.5.5 接收数据
- 3.5.6 更新数据 方法一
- 3.5.7 更新数据 方法二
- 第六节:衔接各个action & 重构代码
- 3.6.1 index action
- 3.6.2 add action
- 3.6.3 insert action
- 3.6.4 delete action
- 3.6.5 edit action
- 3.6.6 update action
- 第七节:分页
- 3.7.1 调用内置分页
- 3.7.2 select与paginate(选学)
- 3.7.3 分页原理(选学)
- 3.7.4 调用page(选学)
- 第八节:模糊查询
- 3.8.1 增加查询
- 3.8.2 完善查询
- 第四章 登录与注销
- 第一节:cookie 与 session
- 第二节:静态方法
- 第三节:规划URL跳转
- 第四节:登录
- 4.4.1 登录流程
- 4.4.2 index action
- 4.4.3 login action
- 4.4.4 引入M层
- 4.4.5 异常Exception
- 第五节:注销
- 第六节:验证
- 4.6.1 action增加验证
- 4.6.2 使用构造函数验证
- 4.6.3 使用继承验证
- 第五章 E-R图与数据字典
- 第一节:第一张E-R图
- 第二节:实体间的关系
- 第三节:开发规范
- 第六章 班级管理
- 第一节:建立数据表
- 第二节:数据列表index
- 第三节:增加数据add
- 第四节:编辑数据edit
- 第五节:删除数据delete
- 第七章 学生管理
- 第一节:建立数据表
- 第二节:数据列表
- 7.2.1 显示性别
- 7.2.2 显示创建时间
- 7.2.3 显示班级名称
- 7.2.4 显示辅导员姓名
- 第三节:编辑数据
- 第四节:魔法函数
- 第五节:源码分析
- 第八章 课程管理
- 第一节:建立数据表
- 第二节:新增课程
- 第三节:编辑课程
- 8.3.1 edit
- 8.3.2 update
- 第九章 代码重构
- 第一节:add 与 edit
- 第二节:insert 与 update
- 第三节:模板继承
- 9.3.1 index.html
- 9.3.2 edit.html
- 9.3.3 小结
- 第十章 UML图
- 第一节:E-R图回顾
- 第二节:UML图
- 第十一章 菜单与路由
- 第一节:添加菜单
- 第二节:虚拟主机
- 第三节:定制路由
- 第十二章 开发调试
- 第一节:开启trace
- 第二节:查看日志
- 第十三章:总结