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
}
}