## 數據庫字符集的選擇
MySQL在5.5.3之后增加了這個utf8mb4的編碼,mb4就是most bytes 4的意思,專門用來兼容四字節的unicode。好在utf8mb4是utf8的超集,除了將編碼改為utf8mb4外不需要做其他轉換。當然,為了節省空間,一般情況下使用utf8也就夠了。
本來3字節的utf8已經可以覆蓋這個世界上的所有語言,直到emoji等符號的出現,讓字符集大大擴展,導致utf8開始出現一些四字節的符號。
所以我們建議使用數據庫的編碼為`utf8mb4`,排序方式為:`utf8mb4_unicode_ci`。
## `utf8mb4_unicode_ci`與`utf8mb4_general_ci`如何選擇
字符除了需要存儲,還需要排序或比較大小,涉及到與編碼字符集對應的 排序字符集(collation)。ut8mb4對應的排序字符集常用的有`utf8mb4_unicode_ci`、`utf8mb4_general_ci`。
主要從排序準確性和性能兩方面看:
* 準確性
`utf8mb4_unicode_ci`是基于標準的Unicode來排序和比較,能夠在各種語言之間精確排序。
`utf8mb4_general_ci`沒有實現Unicode排序規則,在遇到某些特殊語言或字符是,排序結果可能不是所期望的。
* 性能
`utf8mb4_general_ci`在比較和排序的時候更快。
`utf8mb4_unicode_ci`在特殊情況下,Unicode排序規則為了能夠處理特殊字符的情況,實現了略微復雜的排序算法。
我個人推薦是`utf8mb4_unicode_ci`,將來 8.0 里也極有可能使用變為默認的規則。相比選擇哪一種collation,使用者應該更關心字符集與排序規則在db里要統一就好。
## 轉換到utf8mb4
如果表定義和連接字符集都是utf8,那么直接在你的表上執行:
~~~
ALTER TABLE tbl_name CONVERT TO CHARACTER SET utf8mb4;
~~~
## 數據庫配置
直接在`database.php`數據庫配置文件中按如下方式定義即可。
~~~
return [
'default' => 'mysql',
'connections' => [
'mysql' => [
// 數據庫類型
'type' => 'mysql',
// 服務器地址
'hostname' => '127.0.0.1',
// 數據庫名
'database' => 'thinkphp',
// 數據庫用戶名
'username' => 'root',
// 數據庫密碼
'password' => '',
// 數據庫連接端口
'hostport' => '',
// 數據庫連接參數
'params' => [],
// 數據庫編碼默認采用utf8
'charset' => 'utf8mb4',
// 數據庫表前綴
'prefix' => 'my_',
],
],
];
~~~
也可在本地再配置一份數據庫,用于開發階段使用。見`thinkphp6`根目錄下面的`.env`文件。
~~~
APP_DEBUG = true
[APP]
DEFAULT_TIMEZONE = Asia/Shanghai
[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1
DATABASE = project
USERNAME = root
PASSWORD = 123456
HOSTPORT = 3306
CHARSET = utf8mb4
DEBUG = true
[LANG]
default_lang = zh-cn
~~~
## 查詢數據
所有的查詢都采用靜態方法,可以使用`Db`類或者模型類完成查詢構造器操作,也就是`think\facade\Db`,通過門面對象`think\facade\Db`進行靜態方法調用。以下列舉常用的方法。
| 查詢方法 | 作用描述 |
| --- | --- |
| table | 指定查詢數據表 |
| field | 指定查詢字段 |
| where | 指定查詢條件 |
| order | 指定結果排序 |
| limit | 指定查詢結果數 |
| find | 查詢一條記錄 |
| select | 查詢數據集 |
| insert | 寫入數據 |
| update | 更新數據 |
| delete | 刪除數據 |
## 數據庫架構設計
使用框架開發應用,一般不需要直接操作數據庫,而是通過框架封裝好的數據庫中間層對數據庫進行操作。這樣的好處主要有兩個:一是簡化數據庫操作,二是做到跨數據庫的一致性。這種設計的中間層通常稱之為數據庫訪問抽象層,簡稱數據訪問層(`DAL`),`ThinkPHP6`的數據訪問層是基于PHP內置的`PDO`對象實現。一般抽象層本身并不直接操作數據庫,而是通過驅動來實現具體的數據庫操作。
`ThinkPHP6`的數據庫設計相比之前版本更加合理,數據訪問層劃分的更細化,把數據訪問對象分成了連接器、查詢器、生成器等多個對象,并通過數據庫訪問入口類統一調用,分工更明確,各司其職。
### 查詢單行
查詢單行數據使用`find`方法:
~~~
// table方法必須指定完整的數據表名
Db::table('think_user')->where('id', 1)->find();
// 如果設置了數據表前綴(prefix)參數的話 也可以使用
Db::name('user')->where('id', 1)->find();
~~~
> 即使滿足條件的數據有多個,`find`查詢也只會返回一條數據。你可以使用`order`排序來決定返回哪一條數據。
最終生成的SQL語句如下:
~~~
SELECT * FROM `think_user` WHERE `id` = 1 LIMIT 1
~~~
> `find`方法查詢結果不存在,返回`null`,否則返回結果數組。
> 如果希望查詢數據不存在的時候返回空數組,可以使用
~~~
// table方法必須指定完整的數據表名
Db::table('think_user')->where('id', 1)->findOrEmpty();
~~~
如果希望在沒有找到數據后拋出異常可以使用
~~~
Db::table('think_user')->where('id', 1)->findOrFail();
~~~
如果沒有查找到數據,則會拋出一個`think\db\exception\DataNotFoundException`異常。
### 查詢多行數據
查詢多行數據使用`select`方法:
~~~
$list = Db::table('think_user')->where('status', 1)->select();
foreach ($list as $user) {
echo $user['name'];
}
~~~
最終生成的SQL語句是:
~~~
SELECT * FROM `think_user` WHERE `status` = 1
~~~
`select`方法查詢結果是一個數據集對象(`think\Collection`),如果需要轉換為純粹的二維數組,可以使用toArray()方法。
~~~
$list = Db::table('think_user')->where('status', 1)->select()->toArray();
foreach ($list as $user) {
echo $user['name'];
}
~~~
如果希望查詢數據不存在的時候返回空數組,可以使用`select`。`select`方法查詢結果不存在,返回返回空數組(`[]`)。
> 如果你的數據表沒有設置表前綴的話,那么`name`和`table`方法是一樣的。
> **注意!如果要判斷數據集是否為空,不能使用`empty`判斷**,必須使用數據集對象的`isEmpty`方法判斷,如下。
~~~
$users = Db::name('user')->select();
if($users->isEmpty()){
echo '數據集為空';
}
~~~
### 查詢單元格的值
~~~
// 返回某個單元格的值
Db::table('think_user')->where('id', 1)->value('name');
~~~
> `value`方法查詢結果不存在,返回`null`。
### 查詢若干列的值
~~~
// 返回一列,類型為數組
Db::table('think_user')->where('status',1)->column('name');
// 指定id字段的值作為索引
Db::table('think_user')->where('status',1)->column('name', 'id');
~~~
如果要返回完整數據,并且添加一個索引值的話,可以使用
~~~
// 指定id字段的值作為索引 返回所有數據
Db::table('think_user')->where('status',1)->column('*','id');
~~~
> `column`方法查詢結果不存在,返回空數組(`[]`)。
## 新增數據
可以使用`insert`方法向數據庫明確新增一條數據
~~~
$data = ['foo' => 'bar', 'bar' => 'foo'];
Db::name('user')->insert($data);
~~~
> `insert`方法添加數據成功返回添加成功的條數,通常情況返回 1。
如果你的數據表里面沒有`foo`或者`bar`字段,那么就會拋出異常。如果不希望拋出異常,可以通過下面的方法關閉嚴格模式,丟棄不存在的字段:
~~~
$data = ['foo' => 'bar', 'bar' => 'foo'];
Db::name('user')->strict(false)->insert($data);
~~~
如果你的數據表采用了自增主鍵,并且添加數據后如果需要返回新增數據的自增主鍵,可以使用`insertGetId`方法新增數據并返回主鍵值:
~~~
$userId = Db::name('user')->insertGetId($data);
~~~
## 添加多條數據
添加多條數據直接使用`insertAll`方法傳入需要添加的數據(通常是二維數組)即可。
~~~
$data = [
['foo' => 'bar', 'bar' => 'foo'],
['foo' => 'bar1', 'bar' => 'foo1'],
['foo' => 'bar2', 'bar' => 'foo2']
];
Db::name('user')->insertAll($data);
~~~
> `insertAll`方法添加數據成功返回添加成功的條數
### 更新數據
使用`update`方法更新數據,數據最好明確包含主鍵數據。
~~~
Db::name('user')->update(['id' => 1, 'name' => 'thinkphp']);
~~~
生成的SQL語句:
~~~
UPDATE `think_user` SET `name`='thinkphp' WHERE `id` = 1
~~~
更新的數據如果不包含主鍵,則必須指定更新條件,例如:
~~~
Db::name('user')
->where('id' ,1)
->update(['name' => 'thinkphp']);
~~~
> `update`方法返回影響數據的條數,沒修改任何數據則返回 0。
如果要更新的數據需要使用`SQL`函數,可以使用下面的方式:
~~~
Db::name('user')
->where('id', 1)
->update([
'name' => Db::raw('UPPER(name)'),
'score' => Db::raw('score-3'),
'read_time' => Db::raw('read_time+1')
]);
~~~
## 刪除數據
~~~
// 根據主鍵刪除
Db::table('think_user')->delete(1);
Db::table('think_user')->delete([1, 2, 3]);
// 條件刪除
Db::table('think_user')->where('id', 1)->delete();
Db::table('think_user')->where('id', '<', 10)->delete();
~~~
最終生成的SQL語句是:
~~~
DELETE FROM `think_user` WHERE `id` = 1
DELETE FROM `think_user` WHERE `id` IN (1,2,3)
DELETE FROM `think_user` WHERE `id` = 1
DELETE FROM `think_user` WHERE `id` < 10
~~~
> `delete`方法返回影響數據的條數,沒有刪除任何數據返回 0。
出于安全考慮,如果不帶任何條件調用`delete`方法會提示錯誤,如果你確實需要刪除所有數據,可以使用:
~~~
// 無條件刪除所有數據
Db::name('user')->delete(true);
~~~
## 使用原生SQL語句
### `query`方法
`query`方法用于執行`SQL`查詢操作,和`select`方法一樣返回查詢結果數據集(數組)。
~~~
Db::query('select * from think_user where status=1');
~~~
### Db::raw 方法
~~~
$conditions = [['hit', '>', 0], ['', 'exp', Db::raw('CHAR_LENGTH(id)=3')]];
$data = Db::name('sys_words')->where($conditions)->limit(2)->select();
位運算的使用方法:
~~~
$map = \[\['', 'exp', Db::raw('role & (1<< 3 )')\]\];
~~~
注意 `CONCAT_WS `與 `CONCAT`對 `NULL`的區別:
~~~
$map\[\] = \['', 'exp', Db::raw('CONCAT\_WS(",",`GCDM`,`XMDM`) LIKE "%' . $param\['code'\] . '%"')\];
~~~
// view 方法比較 特殊
$map = [Db::raw('CHAR_LENGTH(id)=3')]; //必須是數組
$data = Db::view('sys_words', '*')->where('hit', '>', 0)->where($map)->limit(2)->select();
~~~
### `execute`方法
`execute`用于更新和寫入數據的sql操作,如果數據非法或者查詢錯誤則返回`false`,否則返回影響的記錄數。
~~~
Db::execute("update think_user set name='thinkphp' where status=1");
~~~
### `query`方法
~~~
Db::query('select * from users where id=1');
~~~
> 對數據表的CURD操作,除了`select`和存儲過程調用使用`query`方法之外,其它的操作都使用`execute`方法。
### 參數綁定
支持在原生查詢的時候使用參數綁定,包括問號占位符或者命名占位符,例如:
~~~
Db::query("select * from think_user where id=? AND status=?", [8, 1]);
// 命名綁定
Db::execute("update think_user set name=:name where status=:status", ['name' => 'thinkphp', 'status' => 1]);
~~~
> 注意不支持對表名使用參數綁定
### SQL調試
~~~
Db::name('users')->getLastSql();
~~~
在模型操作中 ,為了更好的查明錯誤,經常需要查看下最近使用的SQL語句,可以用`getLastsql`方法來輸出上次執行的sql語句。例如:
~~~
User::get(1);
echo User::getLastSql();
~~~
輸出結果是`SELECT * FROM 'think_user' WHERE 'id' = 1`
> `getLastSql`方法只能獲取最后執行的`SQL`記錄。
也可以使用`fetchSql`方法直接返回當前的查詢SQL而不執行,例如:
~~~
echo User::fetchSql(true)->find(1);
~~~
輸出的結果是一樣的。
- 搭建ThinkPHP6的開發環境
- 配置ThinkPHP6
- 必要的基礎知識(basic)
- MVC開發模式
- 控制器(controller)
- 數據庫(database)
- 模型(model)
- 模型關聯(relation)
- 視圖(view)
- Session
- Cookie
- 緩存(cache)
- 上傳(upload)
- 驗證器(validate)
- 驗證碼(captcha)
- 命令行(command)
- 服務器部署(deploy)
- 數據備份(backup)
- 數據同步(synchronization)
- 訂閱服務(subscribe)
- PHP 易混淆知識點
- 助手函數
- MySQL規范
- Redis 規范
- office插件 phpoffice
- 拼音插件 pinyin
- 日期插件 datetime
- 消息插件 amqp
- 產品部署環境的搭建
- PDF 等雜項處理
- 文件上傳
- 常用擴展
- flc/dysms
- 使用示例 ①
- 使用示例 ②
- qiniu/php-sdk
- 簡介
- 使用示例
- 使用示例 2 ②
- liliuwei/thinkphp-jump
- 擴展介紹
- 下載擴展
- 使用方法
- topthink/think-captcha
- 安裝擴展
- 驗證碼顯示
- 更換驗證碼
- 驗證碼校驗
- 驗證碼配置
- 自定義驗證碼
- phpoffice/phpspreadsheet
- 數據寫入表格
- 讀取表格數據
- topthink/think-queue
- 安裝
- 自定義函數
- 任務類
- 帶有日志的任務類