thinkphp-模型定义、新增、删除、更新、查询
模型基础
定义
定义一个和数据库表向匹配的模型;
namespace app\model;
use think\Model;
class User extends Model
{
}
在控制器中引用:
namespace app\controller;
// 引用model文件夹下的User.php文件
use app\model\User;
class DataModel{
public function index() {
return json(User::select());
}
}
模型会自动对应数据表,并且有一套自己的命名规则,模型类需要去除表前缀(tp_),采用驼峰式命名,并且首字母大写;
tp_user(表名) => User
// 有两个下划线就去掉表前缀tp,然后首字母大写
tp_user_type(表名) => UserType
可以直接使用模型的名称User::*调用查询方法,比如select()等;
如果在phpstorm中没有代码提示,把5.1 的模型注释复制过来;
如果担心设置的模型类名和PHP 关键字冲突,可以开启应用类后缀;比如,我要将原来的model/User.php以及其类改为UserModel.php,
app/UserModel.php 模型
namespace app\model;
use think\Model;
class UserModel extends Model
{
}
控制器 controller/DataModel.php
namespace app\controller;
use app\model\UserModel;
class DataModel{
public function index() {
return json(UserModel::select());
}
}
如果仅仅修改名称,那么会报错,这时候就需要在模型中添加一个私有属性$name,然后设置一下$name 属性为指定user(表名)即可实现;
namespace app\model;
use think\Model;
class User extends Model
{
// 添加后缀需要设置模式名称
protected $name = 'user';
}
修改模型中的主键
默认根据id进行查找,比如:
User::find(19) 查找id=19的行
但是我们可以在模型中指定查询的主键
namespace app\model;
use think\Model;
class User extends Model
{
// 设置主键
protected $pk = 'uid';
}
修改后,find(19)就变成了要查找的uid=19。
从控制器端调用模型操作,如果和控制器类名重复,可以设置别名,在控制器中:
namespace app\controller;
use app\model\User as UserModel;
class DataModel{
public function index() {
return json(UserModel::find(19));
}
}
因为项目大了,很多类名都会重复,所以很多商业代码都使用别名as进行区分。
在模型定义中,可以设置其它的数据表;
namespace app\model;
use think\Model;
class User extends Model
{
// 设置表,默认表是当前名称的表 这里User 对应tp_user表 设置在config/database.php中
// 但是也可以设置指向其他的表 注意这里要写表全名
protected $table = 'tp_one';
}
但是一般不建议这么操作,要求模型与表一一对应。
模型和控制器一样,也有初始化,在这里必须设置static 静态方法;
namespace app\model;
use think\Model;
class User extends Model
{
// 初始化操作
protected static function init()
{
parent::init();
echo '初始化操作';
}
}
初始化操作即为:第一次实例化的时候执行init,只要调用了这个model,那么就会执行这个初始化操作。
模型的新增和删除
使用实例化的方式添加一条数据,首先实例化方式如下,两种均可:
use app\model\User as UserModel;
$user = new UserModel();
$user = new \app\model\User();
设置要新增的数据,然后用save()方法写入到数据库中,save()返回布尔值;
$user = new UserModel();
$user->username = '李白';
$user->password = '123';
$user->gender = '男';
$user->email = 'libai@163.com';
$user->price = 100;
$user->details = '123';
$user->uid = 1011;
// 保存就是新增
$user->save();
也可以通过save()传递数据数组的方式,来新增数据;
$user->save([
'username' => '李白',
'password' => '123',
'gender' => '男',
'email' => 'libai@163.com',
'price' => 100,
'details' => '123',
'uid' => 1011
]);
使用allowField()方法,允许要写入的字段,其它字段就无法写入了;
$user->allowField(['username', 'password'])
->save([
'username' => '李白',
'password' => '123',
'gender' => '男',
'email' => 'libai@163.com',
'price' => 100,
'details' => '123',
'uid' => 1011
]);
这时执行,会报错:General error: 1364 Field 'details' doesn't have a default value,意思就是details是数据表必填字段,没有设置默认值,所以改一下:
$user->allowField(['username', 'password', 'details'])
->save([
'username' => '李白',
'password' => '123',
'gender' => '男',
'email' => 'libai@163.com',
'price' => 100,
'details' => '123',
'uid' => 1011
]);
这时候执行插入后,发现数据表中最新数据:
id | username | password | gender | price | details | uid | status | |
---|---|---|---|---|---|---|---|---|
316 | 李白 | 123 | 男 | null | 0.00 | 123 | null | 0 |
所以像email等不允许写入的字段就会是Null或者是使用创建数据表时候指定的默认值。
DROP TABLE IF EXISTS `tp_user`;
CREATE TABLE `tp_user` (
`id` mediumint(8) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自动编号',
`username` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`password` char(40) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`gender` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '男',
`email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`price` decimal(8, 2) UNSIGNED NOT NULL DEFAULT 0.00,
`details` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`uid` smallint(6) NULL DEFAULT NULL,
`status` tinyint(3) NOT NULL DEFAULT 0 COMMENT '状态',
`list` json NULL,
`delete_time` datetime(0) NULL DEFAULT NULL,
`create_time` datetime(0) NOT NULL DEFAULT '1997-01-01 01:01:01' COMMENT '创建时间',
`update_time` datetime(0) NOT NULL DEFAULT '1997-01-01 01:01:01' COMMENT '修改时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 302 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
模型新增也提供了replace()方法来实现REPLACE into 新增,默认是insert into;
$user = new UserModel();
$user->username = '李白';
$user->password = '123';
$user->gender = '男';
$user->email = 'libai@163.com';
$user->price = 100;
$user->details = '123';
$user->uid = 1011;
$user->replace()->save();
return Db::getLastSql();
SQL:REPLACE INTO `tp_user` SET `username` = '李白' , `password` = '123' , `gender` = '男' , `email` = 'libai@163.com' , `price` = '100' , `details` = '123' , `uid` = 1011 , `create_time` = '2024-04-12 10:37:42.840691' , `update_time` = '2024-04-12 10:37:42.840698'
使用saveAll()方法,可以批量新增数据,返回批量新增的数组
$dataAll = [
[
'username' => '李白1',
'password' => '123',
'gender' => '男',
'email' => 'libai@163.com',
'price' => 100,
'details' => '123',
'uid' => 1011
],
[
'username' => '李白2',
'password' => '123',
'gender' => '男',
'email' => 'libai@163.com',
'price' => 100,
'details' => '123',
'uid' => 1011
]
];
dump($user->saveAll($dataAll));
最终返回的是一个Collection数据集,其中关于新插入行的所有字段数据在:-data:array: 属性中可以查看到。
当新增成功后,使用$user->id,可以获得自增ID(主键需是id);
如果是像上例一样插入的是多行,那么返回的就是null,dump($user->id);
推荐使用::create()静态方法,来创建要新增的数据(只能创建一行,无法创建多行);
$user = UserModel::create(
//参数1 是新增数据数组,必选
[
'username' => '李白3',
'password' => '123',
'gender' => '男',
'email' => 'libai@163.com',
'price' => 100,
'details' => '123',
'uid' => 1011
],
//参数2 是允许写入的字段 allowField,可选,默认为[]
['username', 'password', 'details'],
// 参数3 为是否replace 新增,默认false 为Insert 写入,改为true则为replace新增,
false
);
删除
使用find()方法,通过主键(id)查询到想要删除的数据;
然后再通过delete()方法,将数据删除,返回布尔值;
// 首先要查询到一条数据
$user = UserModel::find(322);
// 返回的是一个布尔值 删除成功返回true
dump($user->delete());
也可以使用静态方法调用destroy()方法,通过主键(id)删除数据;
UserModel::destroy(321);
静态方法destroy()方法,也可以批量删除数据;
dump( UserModel::destroy([319, 320]) );
静态destroy()方法删除成功返回true。
通过数据库类的查询条件删除
UserModel::where('id', '>', 80)->delete();
UserModel::where('username', '=', '李白6')->delete();
使用闭包的方式进行删除;
UserModel::destroy(function($query) {
$query->where('username', '=', '李白3')->delete();
});
更新
使用find()方法获取数据,然后通过save()方法保存修改,返回布尔值
// 首先找到一条
$user = UserModel::find(325);
// 然后执行修改
$user->username = '董存瑞';
$user->email = 'iloveu@163.com';
// 更新和新建都是用save
$user->save();
通过where()方法结合find()方法的查询条件获取的数据,进行修改;
$user = UserModel::where('username', '李黑')->find();
$user->username = '李白';
$user->email = 'libai@163.com';
$user->save();
save()方法只会更新变化的数据,如果提交的修改数据没有变化,则不更新;但如果你想强制更新数据,即使数据一样,那么可以使用force()方法
dump( $user->force()->save() ); // 返回布尔值
Db::raw()执行SQL 函数的方式,同样在这里有效,如果有复杂的操作,就用raw包含一下:
// 首先找到一条
$user = UserModel::find(325);
// 然后执行修改
$user->username = '董存瑞';
$user->email = 'iloveu@163.com';
$user->price = Db::raw('price + 1');
// 更新和新建都是用save
dump( $user->force()->save() );
每次刷新都会给数据库price属性+1
使用allowField()方法,允许要更新的字段,其它字段就无法写入了;
// 首先找到一条
$user = UserModel::find(325);
// 然后执行修改
$user->username = '董存瑞';
$user->email = 'iloveu@163.com';
$user->price = 200;
// 这样price字段会修改成功 因为设置了price字段允许更新
dump( $user->allowField(['username', 'price'])->force()->save() );
如果像下面这样,更新操作会返回true,但是price字段不会更新,因为只允许username字段更新。
$user->price = 200;
dump( $user->allowField(['username'])->force()->save() );
但是tp6目前有个bug,在6.1.4版本下实测:
$user->price = Db::raw('price + 1');
// $user->price = 200;
dump( $user->allowField(['username'])->force()->save() );
使用Db::raw,会忽略allowField中关于允许字段的设置,allowField中没有设置price,那么price依然会每次执行+1操作。
通过saveAll()方法,可以批量修改数据,返回被修改的数据集合;
$list = [
['id' => 316, 'username' => '白+黑', 'email' => 'baihei@163.com'],
['id' => 317, 'username' => '白+黑', 'email' => 'baihei@163.com'],
['id' => 318, 'username' => '白+黑', 'email' => 'baihei@163.com']
];
$user = new UserModel();
$user->saveAll($list);
批量更新saveAll()只能通过主键id 进行更新;
使用静态方法::update()更新,返回的是对象实例;
UserModel::update([
// 必须要有id的放在第一个位置上,作为查询条件
'id' => 316,
'username' => '李白',
'email' => 'bai@163.com'
]);
第二种,将查询条件放在updata([], 查询条件)
UserModel::update([
'username' => '李白',
'email' => 'bai@163.com'
], ['id' => 317]);
第三个参数,限制了修改的字段,比如这里仅仅传入了username,此时更新的时候只会更新Username字段,email字段的内容不会被更改。
UserModel::update([
'username' => '李白白',
'email' => 'bai@163.com'
], ['id' => 318], ['username']);
模型的新增和修改都是save()进行执行的,它采用了自动识别体系来完成。实例化模型后调用save()方法表示新增,查询数据后调用save()表示修改;当然,如果在save()传入更新修改条件后也表示修改(比如传入了id);
// 比如此例,没有传入Id,然后也实例化模型了,那么此时就是新增
$user = new UserModel();
$user->save([
'username' => '宋江',
'password' => 123,
'details' => 321,
'email' => 'jiang@163.com',
'price' => 200
]);
查询
模型数据查询与普通的数据库查询几乎一样。但是也多了很多方便的新功能。
使用find()方法,通过主键(id)查询到想要的数据;
$user = UserModel::find(19);
return json($user);
也可以使用where()方法进行条件筛选查询数据;
$user = UserModel::where('username', '辉夜')->find();
return json($user);
调用find()方法时,如果数据不存在则返回Null,还有findOrEmpty()方法,数据不存在返回空模型(其实就是一个数组),此时,可以后使用isEmpty()方法来判断,是否为空模型。
$user = UserModel::findOrEmpty(419); // 不存在返回空数组[]
if($user->isEmpty()) {
echo '空的';
}
使用select([])方式,查询多条指定id 的字段,不指定就是所有字段(select[]是直接给查出结果了,如果在查询阶段还是使用whereIn);
$user = UserModel::select([19, 20, 21]); // 实际上相当于where in
return json($user);
模型方法也可以使用where 等连缀查询,和数据库查询方式一样
$user = UserModel::where('status', 1) // status=1的行
->limit(3) // 只能显示3条
->order('id', 'desc') // 按照Id倒叙排列
->select();
获取某个字段value()或者某个列column()的值;
// 找到id=19的行(数组)中的username字段的值
$user = UserModel::where('id', '=', 19)->value('username');
$user = UserModel::whereIn('id', [19,20,21])->column('username', 'id');
找到id范围在19、20、21的3条数据并显示出他们的username列的数据,结果中以其id字段的值作为键值
[
19: "蜡笔小新",
20: "路飞",
21: "黑崎一护"
]
同时column的第二个参数可以去掉,那么结果就仅仅是值了。
$user = UserModel::whereIn('id', [19,20,21])->column('username');
/*
[
"蜡笔小新",
"路飞",
"黑崎一护"
]
*/
模型支持动态查询:getBy*,*表示字段名;
id | username | |
---|---|---|
1 | 辉夜 | wubin-work@qq.com |
2 | 辉夜 | huiye@163.com |
UserModel::getByUsername('辉夜');
UserModel::getByEmail('huiye@163.com');
getBy*查找,只会查找出一条!!即从上到下查询到的第一条,比如本例就会查找到id=1的那一条的辉夜!
如果要查找倒叙的第一条,就要使用order-desc了
// 查找出id=2的倒数第一条
$user = UserModel::order('id', 'desc')->getByUsername('李白');
模型支持聚合查询:max、min、sum、count、avg 等
// 查找出price列中的最大值
$user = UserModel::max('price');
使用chunk()方法可以分批处理数据,数据库查询时讲过,防止一次性开销过大
UserModel::chunk(5, function($users) {
foreach($users as $user) {
echo $user->username . ' | ';
}
echo '<br>...<br>';
});
第一个参数5,意思就是每5条执行一次
可以看到结果如下:
蜡笔小新 | 路飞 | 黑崎一护 | 小明 | 孙悟饭 |
...
孙悟天 | 樱桃小丸子 | 孙武 | 李白 | 辉夜 |
...
李黑 | 辉夜 | 辉夜 | 白居易 | 辉夜 |
...
李逵 | 礼拜 | 公孙 | 李白 | 李白 |
...
李白 | 李白 | 李白白 | 董存瑞 | 宋江 |
...
可以利用游标查询功能,可以大幅度减少海量数据的内存开销,它利用了PHP 生成器特性。每次查询只读一行,然后再读取时,自动定位到下一行继续读取;
foreach(UserModel::where('id', '<', 100)->cursor() as $user) {
echo $user->username . ' | ';
}
最终结果:蜡笔小新 | 路飞 | 黑崎一护 | 小明 | 孙悟饭 | 孙悟天 | 樱桃小丸子 | 孙武 | 李白 | 辉夜 |
可以看到它是一行一行的读取并处理的。