# 間接讀取數據
直接讀取數據雖然簡單易懂,但往往在大型的項目中卻并不適用。如果實現一個功能,我們把所有的代碼都寫在一個方法中,顯然當功能復雜時,代碼就會越來越長,越來越難以維護。這顯然不是我們想看到的。
基于此,我們有了數據庫M層,一個專門為我們處理數據表的文件。
## M 數據模型層
沒有M層以前我們取數的過程是這樣的。

有了M層以后,我們取數的過程變成了這樣。

是的,你想的沒錯,在小型的項目,或是我們這個教程中,這樣做的確是畫蛇添足了。但是我們的目標,并不僅僅滿足于做小項目。后面你將會一點點的感受到分層的魅力。
## 調用誰,你就要USE誰
我們再看前面寫過的代碼:

如果刪除或注釋掉第3行。
就會報如下錯誤:

報錯信息中提示這個類 'app\index\controller\DB' 沒有找到。
原因就是我們在用Db類以前,沒有告訴程序這個正確的Db類在哪個位置,怎么告訴呢?利用的就是USE。
那我們說,你怎么就知道要這么use呢?
我們先看一下,Db這個類到底在哪。

其實它在哪并不要緊,PHP中有一個叫命名空間的東西,至于怎么工作的,有興趣的GOOGLE吧。我們只需要知道它叫命名空間就可以了。如下圖所示:

命名空間,可以簡單的理解為:我住哪兒。
我們看到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進行測試結果顯示如下:

下面,我們正式調用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);
}
}
~~~
經測試,我們將得到如下錯誤提示:

~~~
git checkout -f step3.2.4.1
~~~
執行上述命令后,上述示例代碼信息如下:

提示:不能重新聲明`app\index\controller\Teacher`這個類,原因是由于這個類已經存在。
> 每個班級里面只能有一個小明,如果存在兩個小明,那么他們肯定有一個是**大小明**,另一個是**小小明**。
在日常使用電腦時,也是如此。當我們嘗試在同一個文件夾中建立兩個同名文件(包含擴展名)時,就會報錯。
有人說,老師我們并沒有在同一個文件夾中建立`Teacher`呀,按上面的理論,應該不報錯才對。這里,就要說下use這條語句了,當它執行`use app\common\model\Teacher;`時,我們可以簡單理解為:將`application\common\model`文件夾下的`Teacher.php`文件中定義的`Teacher`類導入到當前文件。
此時,由于導入的類名叫做Teacher,然后我們自己的名字也叫做Teacher,就出現了名字發生沖突的錯誤。
所以簡單來說:上述出現錯誤的原因,是由于這兩個類起的名字是相同的。

#### 解決方法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
~~~
執行上述命令后,上述示例代碼信息如下:

#### 解決方法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。由于類名與文件名相同,所以類名當然也要修改了。
修改后如圖所示:

這時候,C層的類名字叫做TeacherController,M層的叫做Teacher,兩個名字不一樣了,當然也就不需要使用別名了。
刷新URL進行測試:
如果你見到如下錯誤:
***控制器不存在:Teacher***
產生的原因有以下兩種:
1. 我們config.php中的配置信息沒有起作用,請檢查拼寫是否有錯誤,文件是否進行正常保存等。
2. 文件名或類名命名不正確。
> 我們在以后的學習中,還會遇到上述錯誤提示,查找問題原因的方向為:1. 命名空間 2. 文件位置 3. 文件名 4. 類名 。
需要重點檢查的項如下圖所示:

上圖中的錯誤,你發現了嗎?
由于此次我們并沒有變動文件的位置,所以命名空間可以確定是沒有問題的。那么,如果有問題就應該出現在文件名和類名上了。
#### 正確的測試結果如下圖所示:

我們看到,第一行提示說在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);
}
}
~~~
測試結果:

使用`var_dump()`提示我們,現在發生的位置位于`TeacherController`的第12行。
同時我們還看到`$Teacher`變量的類型是一個`object`,這個對象的原型是`app\common\model\Teacher`。沒錯,的確是我們引入的那個`app\common\model\Teacher`。
~~~
git checkout -f step3.2.4.3
~~~
執行上述命令后,上述示例代碼信息如下:

## 主角登場
使用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);
}
}
~~~
測試結果:

~~~
git checkout -f step3.2.4.4
~~~
執行上述命令后,上述示例代碼信息如下:

我們看到返回值是個數組,這個數組的第0項是一個對象,第1項也是個對象,這兩個對象都是基于`app\common\model\Teacher`這個類創建的。
我們并沒在`app\common\model\Teacher`中定義任何屬性,那么這些保護類型的私有屬性是怎么來的呢?這是由于它繼承了一個叫做`think\Model`的類。繼承就是這樣:你自己雖然沒有,但是你父類有就和你有是一樣的。在繼承中是這樣規定的,在父類中所有的protected和public的變量或**方法**,都是可以被子類直接使用。
變量我們看到了,那么我們還可以在這個對象上調用哪些方法呢?
> `var_dump()` 函數可以查看對象中的屬性,但卻不顯示對象中存在的方法。
我們現在就查看一下`think\Model`(think開頭的類,位于thinkphp5/thinkphp/library/think文件夾下)中都有什么方法,然后調用一個試試。

我們在Model.php文件中,同時按下 `Ctrl+P`,然后輸入`@getData`,找到獲取對象原始數據getData()方法,如下圖所示。

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());
}
}
~~~
測試結果如下:

為getData()傳參:
~~~
// 調用上述對象的getData()方法
var_dump($teacher->getData('name'));
~~~
再測試,結果顯示如下:

增加另外兩個直接顯示數據的方式:
~~~
// 調用上述對象的getData()方法
var_dump($teacher->getData('name'));
echo $teacher->getData('name');
return $teacher->getData('name');
~~~
測試結果如下:

#### 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');
}
}
~~~
測試結果如下:

也就是說$xxxx中的xxxx是變量名,這個名字你隨便起,只要前后一致就可以。這些是由我們規定好的,它們在我們代碼中第一次出現的位置是表達式的最左側,所以我們想起什么名字,就起什么名字。
比如下圖中這三項,第一次出現的位置是等號的左邊,說明他們是我們定義的,我們當然有自主權了,起什么名字我們說了算。

但第一次出現的位置是等號的右邊的話,那就不一樣了。
這個是其他人規定好的,它怎么規定的,我們就需要怎么使用。
最簡單的,比如:
~~~
$JiaoShiBiao = new Teacher;
~~~
Teacher這個名字,是我們在use的時候規定好的,在我們的代碼中出現在了等號右邊,那么我們就必須按規則執行。
比如:

為了提升代碼的可讀性,我們必須統一代碼書寫的規范。
我們規定大家必須這樣寫:
~~~
<?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');
}
}
~~~
測試結果如下:

~~~
git checkout -f step3.2.4.5
~~~
執行上述命令后,上述示例代碼信息如下:

- 序言
- 第一章 準備知識
- 第一節: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
- 第二節:查看日志
- 第十三章:總結