ThinkPHP01:数据库和模型
一、开启调试模式
- 根目录会自带一个
.example.env文件。把.example删掉即可开启调试模式。 - 在
.env中设置APP_DEBUG = true - 调试模式下,在页面右下角会出现Trace调试小图标。

二、配置文件
- 配置文件有两种形式:
.env用于本地开发测试;config用于部署上线。 - 本地测试时,
.env优先于config。部署上线时,.env会被自动忽略。 - 获取配置:
public function config() { echo "env配置:" . env("database.hostname"); echo "<br>"; echo "config配置:" . config("database.connections.mysql.hostname"); }
三、URL解析
- ThinkPHP 默认单应用模式。
class Test {
// 控制器默认操作, http://localhost:8000/test
public function index() {
return "index";
}
// http://localhost:8000/test/hello
public function hello() {
return "hello";
}
// http://localhost:8000/test/hello1?value=world
public function hello1($value = "") {
return "hello, ". $value;
}
}
- 修改默认的控制器文件目录,在config下的route.php中配置。
四、数据库
- 查询整个表,当有前缀时使用 name(“member”)
use think\facade\Db;
class DataTest {
public function index() {
$dbs = Db::table('member')->select();
return json($dbs);
}
}
- 单数据查询,find() 没有数据返回 null,findOrFail() 没有数据抛出异常, findOrEmpty() 没有数据返回空数组。
public function index2() {
// 查询id为1
$dbs = Db::table('member')->where('id',1)->find();
// 查看sql语句
return Db::getLastSql();
return json($dbs);
}
- 数据集查询,select() 返回所有数据,selectOrFail() 没有数据抛出异常,toArray()将select() 后的数据集转化为刷组。
- 其他查询,value() 返回某个字段,column() 返回某个字段的多个值,如果数据量过大使用chunk() 分批处理数据,或游标查询cursor()。
- 同一个对象实例第二次查询会保留第一次查询的值,使用 removeOption() 可以清理掉上一次查询保留的值。
- 新增数据,使用 insert() 返回影响行数,如果添加不存在的字段数据会抛出异常,如果强行新增不存在的字段使用 strick(false) 忽略异常,insertGetId() 获取插入的ID,insertAll() 插入多条数据,replace() 可以实现replace into,save() 可以判断是否新增或修改。
public function index3() {
$data = [
"name" => "k",
"age" => 12,
"email" => "@@@"
];
return $this->memberTable->insert($data);
}
- 修改数据,使用update(),修改时执行函数操作使用 exp(),自增自减某个字段使用 inc()/dec(),使用 raw() 可以实现前面的所有操作。
- 删除数据,使用delete(),默认根据主键删除。
- 查询表达式,有 where()、whereLike()、whereBetween()、whereIn()、whereNull()。
- 时间查询,有 where()、whereBetween()、whereBetweenTime()、whereTime()、whereYear()、whereMonth()、whereDay()、whereBetweenTimeField()。
- 聚合查询,count()、max()、min()、avg()、sum()。子查询,fetchSql()、buildSql()、where闭包。原生查询,query()、execute()。
- 链式查询,where()、whereRaw()、field()、fieldRaw()、withoutField()、alias()、limit()、page()、order()、orderRaw()、group()、having()。
- 快捷查询,whereColumn()、whereXXX() XXX代表字段名、getByXXX()、getFieldByXXX()、when()、withAttr()。
- 事务处理,transaction()、rollback()。
五、模型
1. 定义模型
- 定义语法,对应数据库中的member表,大驼峰命名。
use think\Model; class Member extends Model { } - 如果要在模型中指定模型名。
use think\Model; class MemberModel extends Model { protected $name = 'member'; } - 默认主键是 id,主动设置主键
use think\Model; class MemberModel extends Model { protected $pk = 'uid'; } - 类名默认对应表名,主动设置表
use think\Model; class MemberModel extends Model { protected $table= 'member'; } - 模型初始化,自动调用。
use think\Model; class MemberModel extends Model { protected static function init(){ parent::init(); } }
2. 使用模型
控制器中引入模型
use \app\model\Member as MemberModel;
① 查询记录
- Db门面的查询功能,模型都支持。
- 所有记录
use \app\model\Member as MemberModel; class Member { public function index() { $member = MemberModel::select(); $member = MemberModel::find(1); return $member; } } - 单条记录
public function index() { $member = MemberModel::find(1); return $member; }
② 新增记录
普通新增
public function insert() { // 新增方式1 $member = new MemberModel(); $member->name = "李白"; $member->age = 18; $member->email = "@"; $member->save(); // 新增方式2 $member = new MemberModel(); $member->save([ 'name' => "李黑", 'age' => 19, 'email' => "@" ]); }验证新增,设置允许写入的字段
$member->allowField(['name', 'age'])->save([ 'name' => "李黑", 'age' => 19, 'email' => "@" ]);replace新增
$member->replace()->save();新增成功后,获取自增的ID
$member->id;批量新增
$member = new MemberModel(); $dataAll = [ [ 'name' => "黑李", 'age' => 22, 'email' => "@" ], [ 'name' => "白李", 'age' => 24, 'email' => "@" ], ]; dump($member->saveAll($dataAll));使用静态方法
create(新增数组, 允许写入的字段, 是否replace),创建要新增的数据。$member = MemberModel::create([ 'name' => "白李", 'age' => 24, 'email' => "@" ], ['name', 'age'], true); echo $member->id;
③ 删除记录
删除单个,默认根据主键删除。
public function delete() { // 成员方法删除 $member = MemberModel::find(1); $member->delete(); // 静态方法删除 MemberModel::destroy(2); }批量删除
MemberModel::destroy([3, 4, 5]);通过闭包删除
MemberModel::destroy(function ($query){ $query->where("age", "<", 10); });
④ 更新记录
普通更新
public function update() { $member = MemberModel::find(8); $member->name = "李四"; $member->age = 120; $member->save(); }强制更新
$member->force()->save();Db::raw()执行SQL函数$member->age = Db::raw("age + 1");allowField()限制更新的字段(限制不了raw)。saveAll()批量更新,只能根据主键更新,返回被修改的数据集。静态方法
update(数据数组, 限定记录, 限定字段)MemberModel::update([ 'name' => "占山", 'age' => 15 ], ['id' => 10], ['name']);
3. 字段设置
模型的数据字段对应表字段,默认严格区分大小写。
class Member extends Model { // 是否严格区分大小写 protected $strict = false; // 设置字段 protected $schema = [ "id" => 'int', 'name' => "string", "age" => "int", "email" => "string" ]; }$schema属性只对模型有效。如果需要Db类也有效,config/database.php 开启字段缓存。
'fields_cache' => true,在模型中处理数据
class Member extends Model { public function getMemberName($id) { $data = $this->find($id); return $data->getAttr('name'); } }
4. 模型获取器
一个获取器对应模型中的一个特殊方法,该方法是public,方法名格式固定
getXxxAttr()。获取器的作用是对模型实例的数据做出自动处理。
class Member extends Model { // 获取器,获取数据时自动执行 public function getAgeAttr($value) { if ($value < 18) { return "未成年"; }else{ return "已成年"; } } }如果定义了获取器,并且需要获取原始值,使用
getData()。动态获取器
withAttr()public function index6() { $var = MemberModel::select()->withAttr("email", function ($value) { return strtoupper($value); }); return json($var); }
5. 模型修改器
修改器的作用是在新增、修改数据时对数据进行格式化、过滤等处理,方法名格式固定
setXxxAttr()。修改器只对模型操作有效,对Db类无效。
class Member extends Model { public function setEmailAttr($value) { return strtoupper($value); } }
6. 模型查询范围
在模型中封装一个查询或写入的方法,方便控制器调用。方法名格式
scopeXxx。class Member extends Model { public function scopeAdult($query) { $query->where("age", ">=", 18) ->field("id, name, age") ->limit(3); } }调用只需要后缀,查询范围只支持find和select。
use \app\model\Member as MemberModel; class Member { public function index7() { $select = MemberModel::adult()->select(); return json($select); } }全局查询范围,任何查询都需要加上这个范围。
class Member extends Model { // 定义全局查询范围 protected $globalScope = ['status']; public function scopeStatus($query) { $query->where("status", 1); } }
7. 模型数据集
- 数据集继承于collection类。
- 隐藏某个字段
hidden(),显示某个字段visible(),添加获取器字段append(),对字段进行函数处理withAttr()。
8. 模型的自动时间戳
自动时间戳会自动写入create_time和update_time两个字段。默认值是int。
全局开启自动时间戳,在config/database.php中设置
'auto_timestamp' => true,只想设置一个模型开启,在模型中设置
protected $autoWriteTimestamp = true;自定义新增和修改的时间戳
protected $createTime = "create_at"; protected $updateTime = "update_at";不需要update_time
protected $updateTime = false;
9. 模型的只读字段
只读字段只支持模型,不支持数据库方式。
只读字段在修改时无法被修改。
protected $readonly = ['name','email'];动态设置只读字段。
$member->readonly(['name','email'])->save();
10. 模型类型和转换
类型转换会调用属性里的获取器等操作。
设置类型转换可以在获取数据前将类型转换成需要的类型。
protected $type = [ 'name' => 'string', 'price' => 'float', "create_time" => "datetime:Y-m-d" ];当字段不再使用时,可以设置为废弃字段。
protected $disuse = ['status', 'uid'];
11. 模型软删除
软删除不会物理删除记录,相当于全局查询范围。
在模型中设置软删除的功能。
use think\model\concern\SoftDelete; class Member extends Model { use SoftDelete; protected $deleteTime = "delete_time"; }取消屏蔽软删除
UserModel::withTrashed()->select();显示被软删除的记录
UserModel::onlyTrashed()->select();还原软删除
restore()$user = UserModel::onlyTrashed()->find(300); $user->restore();物理删除
UserModel::onlyTrashed()->find(298)->force()->delete();
六、关联模型
1. 关联模型定义
关联模型是将表与表之间进行关联和具象化,更高效的操作数据。
主表关联附表(正向关联),
hasOne(附表模型, 外键, 主键)一对一关联,外键默认为主表名_id。class profile extends Model { } class Member extends Model { public function profile() { return $this->hasOne(Profile::class); } }附表关联主表(反向关联),
belongsTo()class Profile extends Model { public function member() { return $this->belongsTo(Member::class, "user_id"); } }调用模型
use app\model\Member as MemberModel; class Grade { public function index() { $member = MemberModel::find(1); return $member->profile->hobby; } }关联方式
函数 说明 hasOne 一对一 belongsTo 一对一 hasMany 一对多 hasOneThrough 远程一对一 hasManyThrough 远程一对多 belongsToMany 多对多 morphMany 多态一对多 morphTo 多态 正反向关联
说明 正向关联 反向关联 一对一 hasOne belongsTo 一对多 hasMany belongsTo 多对多 belongsToMany belongsToMany 远程一对多 hasManyThrough \ 多态一对一 morphOne morphTo 多态一对多 morphMany morphTo
2. 一对一关联查询
一对一关联修改
$member = MemberModel::find(1); $member ->profile->save(['hobby' => '吃饭']);一对一关联新增
$member = MemberModel::find(1); $member ->profile()->save(['hobby' => '吃饭']);正向关联反向操作
$member = MemberModel::hasWhere("prefile", ["id" => 2])->find();
3. 一对多关联查询
has()方法查询关联附表的主表内容。MemberModel::has("profile", ">=", 2)->select();together()可以在删除主表内容时,同时删除附表关联的内容。$member = MemberModel::with('profile')->find(2); $member->together(['profile'])->delete();新增和一对一关联新增相同。
4. 关联预载入
关联预载入可以减少查询次数提高性能,但不支持多次调用。
$list = MemberModel::with(['profile'])->select([19, 20, 21]); foreach ($list as $member) { dump($member->profile); }如果主表关联多个附表。
$list = MemberModel::with(['profile', 'book'])->select([19, 20, 21]); foreach ($list as $member) { dump($member->profile.$member->book); }延迟预载入
$list = MemberModel::select([19, 20, 21]); $list->load(['profile']); foreach ($list as $member) { dump($member->profile); }
5. 关联统计和输出
统计主表在附表中有多少条记录,包括 withMax、withMin、withCount、withAvg等。
public function index3() { $list = MemberModel::withCount(['profile'])->select([1,2,3]); foreach ($list as $member) { echo $member->profile_count . '<br>'; } }关联统计输出采用
关联方法_统计方法。hidden和visible控制隐藏和显示的字段,append添加额外字段。$list = MemberModel::withCount(['profile'])->select([1,2,3]); $list->hidden(['password', 'gender', 'profile.status']);
6. 多对多关联查询
多对多中间表模型类 Pivot。
在模型类中设置多对多关联,
belongsToMany(关联模型, 中间表, 外键, 关联键)public function roles(){ return $this->belongsToMany(Role::class, Access::class); }使用
// 获取用户 $member = MemberModel::find(2); // 获取用户权限 $roles = $member->roles;新增,需要通过用户表新增到中间表关联。
$member = MemberModel::find(2); $member->roles->save(Role::find(5)); $member->roles->saveAll([1, 2, 3]); $member->roles->attach(5, ["detail" => "测试"]);删除中间表数据
$member = MemberModel::find(2); $member->detach(5);
七、操作JSON
1. 数据库JSON
在数据库中写入JSON,使用数组方式。用
json()指定JSON字段。public function insert(){ $data = [ "name" => "张三", "list" => ["nikeName" => "zs", "gender" => "女"] ]; return Db::name("user")->json(["list"])->insert($data); }查询数据,需要转换JSON,也需要设置
json()Db::name('user')->json(["list"])->find(15);修改JSON字段
$data["list"] = ["nikeName" => "zs", "gender" => "女"]; Db::name("user")->json(['list']->where('id', 1)->update($data); $data["list->gender"] = "男"; Db::name("user")->json(["lsit"])->where('id', 1)->update($data);
2. 模型JSON
在模型中设置JSON字段
protected $json = ["list"];模型中通过JSON的数据查询
UserModel::where('list->name', "小红")->find();更新数据
$user = UserModel::find(1); $user->list->name = "小黑"; $user->save();
八、事件
- 在执行增删改查的时候,可以触发一些事情来执行额外的操作。
1. 数据库事件
- 数据库事件只支持 find、select、update、delete、insert。
- 数据库实践方法为
Db::event('事件名', '执行函数')
| 事件名 | 说明 |
|---|---|
| before_select | select查询前回调 |
| before_find | find查询前回调 |
| after_insert | insert操作成功后回调 |
| after_update | update操作成功后回调 |
| after_delete | dalete操作成功后回调 |
在控制器端,事件一般写在初始化方法里(继承于 BaseController)。
use app\BaseController; use think\facade\Db; class Member extends BaseController { public function initialize() { Db::event("before_select", function ($query) { echo "执行了批量查询"; }); Db::event("after_update", function ($query) { echo "修改被执行"; }); } }
2. 模型事件
- 模型支持的事件类型更丰富。
class Member extends Model {
protected static function onAfterRead($query){
echo "一条数据被查询";
}
protected static function onBeforeUpdate(Model $model) {
parent::onBeforeUpdate($model); // TODO: Change the autogenerated stub
}
}