1. 增

  • 模型添加一条数据可以使用 save(),也可以使用 create() ,只不过后者是静态方式
  • create() 不可以插入多条数据
  • 不推荐,使用 saveAll() 添加多条数据,性能很差,还不如使用 Db::insert()

1.1 save() 添加一条数据

  • 5.1.6 + 版本开始返回数据是否添加成功的布尔值

可以这么使用(几乎也不会有人这么去使用):

  1. <?php
  2. $user = new User;
  3. $user->name = 'thinkphp';
  4. $user->email = 'thinkphp@qq.com';
  5. $user->save();

常见的使用方式:

  1. <?php
  2. // 新增一个用户 save
  3. public function addOneUser($user)
  4. {
  5. $model = new userModel();
  6. return ($model->save($user)) ? '增加成功' : '增加失败';
  7. }

如果需要过滤字段:

  1. <?php
  2. $user = new User;
  3. // 过滤post数组中的非数据表字段数据
  4. $user->allowField(true)->save($_POST);

如果你通过外部提交赋值给模型,并且希望指定某些字段写入,可以使用:

  1. <?php
  2. $user = new User;
  3. // post数组中只有name和email字段会写入
  4. $user->allowField(['name','email'])->save($_POST);

最佳的建议是模型数据赋值之前就进行数据过滤,例如:

  1. <?php
  2. $user = new User;
  3. // 过滤post数组中的非数据表字段数据
  4. $data = Request::only(['name','email']);
  5. $user->save($data);

1.2 replace 写入

5.1.14+版本开始,save方法可以支持replace写入。

  1. <?php
  2. $user = new User;
  3. $user->name = 'thinkphp';
  4. $user->email = 'thinkphp@qq.com';
  5. $user->replace()->save();

1.3 获取自增ID

如果要获取新增数据的自增ID,可以使用下面的方式:

  1. <?php
  2. $user = new User;
  3. $user->name = 'thinkphp';
  4. $user->email = 'thinkphp@qq.com';
  5. $user->save();
  6. // 获取自增ID
  7. echo $user->id;

这里其实是获取模型的主键,如果你的主键不是id,而是user_id的话,其实获取自增ID就变成这样:

  1. <?php
  2. $user = new User;
  3. $user->name = 'thinkphp';
  4. $user->email = 'thinkphp@qq.com';
  5. $user->save();
  6. // 获取自增ID
  7. echo $user->user_id;

1.4 saveAll() 添加多条数据 不推荐使用!

  • 使用saveAll(),但是真的不推荐使用这个… …

    • saveAll 方法实际上是 循环数据数组,每一条数据进行一遍save方法,批量插入数据时慎用!!!每一条数据都会走一次数据库请求进行insert操作,数据量大的时候非常占用时间。批量插入数据建议使用Db::insertAll() 方法,只会进行一次数据库请求
  • saveAll方法新增数据返回的是包含新增模型(带自增ID)的数据集对象。

  1. <?php
  2. $user = new User;
  3. $list = [
  4. ['name'=>'thinkphp','email'=>'thinkphp@qq.com'],
  5. ['name'=>'onethink','email'=>'onethink@qq.com']
  6. ];
  7. $user->saveAll($list);

saveAll方法新增数据默认会自动识别数据是需要新增还是更新操作,当数据中存在主键的时候会认为是更新操作,如果你需要带主键数据批量新增,可以使用下面的方式:

  1. <?php
  2. $user = new User;
  3. $list = [
  4. ['id'=>1, 'name'=>'thinkphp', 'email'=>'thinkphp@qq.com'],
  5. ['id'=>2, 'name'=>'onethink', 'email'=>'onethink@qq.com'],
  6. ];
  7. $user->saveAll($list, false);

1.5 静态方法 create()

  • save方法不同的是,create方法返回的是当前模型的对象实例。
  • create 不支持插入多条数据

还可以直接静态调用create方法创建并写入:

  1. <?php
  2. $user = User::create([
  3. 'name' => 'thinkphp',
  4. 'email' => 'thinkphp@qq.com'
  5. ]);
  6. echo $user->name;
  7. echo $user->email;
  8. echo $user->id; // 获取自增ID

create方法的第二个参数可以传入允许写入的字段列表(传入true则表示仅允许写入数据表定义的字段数据),例如:

  1. <?php
  2. // 只允许写入name和email字段的数据
  3. $user = User::create([
  4. 'name' => 'thinkphp',
  5. 'email' => 'thinkphp@qq.com'
  6. ], ['name', 'email']);
  7. echo $user->name;
  8. echo $user->email;
  9. echo $user->id; // 获取自增ID

V5.1.14+版本开始,支持replace操作,使用下面的方法:

  1. <?php
  2. $user = User::create([
  3. 'name' => 'thinkphp',
  4. 'email' => 'thinkphp@qq.com'
  5. ], ['name','email'], true);

2. 删

  • 推荐使用 destory() 删除数据

2.1 通过ID删除数据

通过模型方法的 get 先查到数据然后,使用 delete 方法删除。V5.16 版本后返回值改为了 布尔值,而不是影响行数。

  1. <?php
  2. $user = User::get(1);
  3. $user->delete();
  4. // 删除一个用户通过 ID 5.16
  5. public function delOneUserById($id)
  6. {
  7. $offectRows = UserModel::get($id)->delete();
  8. return $offectRows ? '删除成功' : '删除失败';
  9. }

使用 destoy方法删除,也是可以通过主键删除。返回值也是布尔值。当destroy方法传入空值(包括空字符串和空数组)的时候不会做任何的数据删除操作,但传入0则是有效的

  1. <?php
  2. // 删除多个用户通过 ID
  3. public function delMulUserByID($arr)
  4. {
  5. return (UserModel::destroy($arr)) ? '删除成功' : '删除失败';
  6. }

2.2 条件删除

可以使用闭包

  1. <?php
  2. // 条件删除闭包
  3. public function delByConditions1()
  4. {
  5. $res = UserModel::destroy(function($query){
  6. $query->where('gender', '=', 2);
  7. });
  8. return $res ? '删除成功' : '删除失败';
  9. }

或者通过数据库类的查询条件删除

  1. <?php
  2. User::where('id','>',10)->delete();

直接调用数据库的delete方法的话无法调用模型事件。


3. 改

3.1 查找并更新

还是使用 save() 方法,修改

  1. <?php
  2. // 修改一个用户
  3. public function updOneUserById($id)
  4. {
  5. $user = UserModel::get($id);
  6. $user->name = 'thinkphp';
  7. $user->email = 'thinkphp@qq.com';
  8. $user->age = 17;
  9. return ($user->save()) ? '修改成功' : '修改失败';
  10. }

对于复杂的查询条件,也可以使用查询构造器来查询数据并更新

  1. <?php
  2. $user = User::where('status',1)
  3. ->where('name','liuchen')
  4. ->find();
  5. $user->name = 'thinkphp';
  6. $user->email = 'thinkphp@qq.com';
  7. $user->save();

save 方法更新数据,只会更新变化的数据,对于没有变化的数据是不会进行重新更新的。如果你需要强制更新数据,可以使用下面的方法:

  1. <?php
  2. $user = User::get(1);
  3. $user->name = 'thinkphp';
  4. $user->email = 'thinkphp@qq.com';
  5. $user->force()->save();

如果要执行SQL函数更新,那么在 **V5.1.8+** 版本可以使用下面的方法

  1. <?php
  2. $user = User::get(1);
  3. $user->name = 'thinkphp';
  4. $user->email = 'thinkphp@qq.com';
  5. $user->score = Db::raw('score+1');
  6. $user->save();

如果只是字段的增加/减少,也可以直接用inc/dec方式

  1. <?php
  2. $user = User::get(1);
  3. $user->name = 'thinkphp';
  4. $user->email = 'thinkphp@qq.com';
  5. $user->score = ['inc', 1];
  6. $user->save();

3.2 直接更新数据 save()

可以 save() 方法中直接添加第二个参数来设置

  1. <?php
  2. // 修改一条记录 直接修改数据
  3. public function updOneUserById2($id)
  4. {
  5. // save方法第二个参数为更新条件
  6. $res = (new UserModel())->save([
  7. 'name' => 'thinkphp',
  8. 'email' => 'thinkphp@qq.com'
  9. ],['id' => $id]);
  10. return $res ? '修改成功' : '修改失败';
  11. }

上面两种方式更新数据,如果需要过滤非数据表字段的数据,可以使用:

  1. <?php
  2. $user = new User;
  3. // 过滤post数组中的非数据表字段数据
  4. $user->allowField(true)->save($_POST,['id' => 1]);

如果你通过外部提交赋值给模型,并且希望指定某些字段写入,可以使用:

  1. <?php
  2. $user = new User();
  3. // post数组中只有name和email字段会写入
  4. $user->allowField(['name','email'])->save($_POST, ['id' => 1]);

最佳建议是在传入模型数据之前就进行过滤,例如:

  1. <?php
  2. $user = new User();
  3. // post数组中只有name和email字段会写入
  4. $data = Request::only(['name','email']);
  5. $user->save($data, ['id' => 1]);

3.3 批量更新数据

不使用 saveAll() 方法去作为批量更新,影响性能。

模型支持调用数据库的方法直接更新数据,例如:

  1. <?php
  2. User::where('id', 1)
  3. ->update(['name' => 'thinkphp']);

数据库的update方法返回影响的记录数

或者使用模型的静态update方法更新:

  1. User::update(['id' => 1, 'name' => 'thinkphp']);

模型的update方法返回模型的对象实例 上面两种写法的区别是第一种是使用的数据库的update方法,而第二种是使用的模型的update方法(可以支持模型的修改器、事件和自动完成)。

3.4 自动识别 isUpdate()

我们已经看到,模型的新增和更新方法都是save方法,系统有一套默认的规则来识别当前的数据需要更新还是新增。

  • 实例化模型后调用save方法表示新增;
  • 查询数据后调用save方法表示更新;
  • save方法传入更新条件后表示更新;

如果你的数据操作比较复杂,可以用isUpdate方法显式的指定当前调用save方法是新增操作还是更新操作。
显式更新数据:

  1. <?php
  2. // 实例化模型
  3. $user = new User;
  4. // 显式指定更新数据操作
  5. $user->isUpdate(true)
  6. ->save(['id' => 1, 'name' => 'thinkphp']);

显式新增数据:

  1. <?php
  2. $user = User::get(1);
  3. $user->name = 'thinkphp';
  4. // 显式指定当前操作为新增操作
  5. $user->isUpdate(false)->save();

4. 查

4.1 获取单个数据 get()

使用 get() 方法,通过ID查询数据

  1. <?php
  2. // 查询一个用户 get() 方法
  3. public function selOneUseById2($id)
  4. {
  5. return json(userModel::get($id));
  6. }

使用查询构造器

  1. <?php
  2. // 查询一个用户 查询构造器
  3. public function selOneUseById($id)
  4. {
  5. return json(userModel::where('id', '=', $id)->findOrEmpty());
  6. }

4.2 获取多条数据

使用模型的 all() 方法,通过ID查询多条数据

  1. <?php
  2. // 查询多条数据 all 方法
  3. public function selMulUserById($arr)
  4. {
  5. return json(userModel::all($arr));
  6. }

要更多的查询支持,一样可以使用查询构造器:

  1. <?php
  2. // 使用查询构造器查询
  3. $list = User::where('status', 1)->limit(3)->order('id', 'asc')->select();
  4. foreach($list as $key=>$user){
  5. echo $user->name;
  6. }

查询构造器方式的查询可以支持更多的连贯操作,包括排序、数量限制等。

V5.1.21+版本开始,可以在all方法之前使用Db类的查询链式操作,

  1. <?php
  2. // 使用查询构造器查询
  3. $list = User::where('status', 1)->limit(3)->order('id', 'asc')->all();
  4. foreach($list as $key=>$user){
  5. echo $user->name;
  6. }

4.3 自定义数据集对象

模型的all方法或者select方法返回的是一个包含多个模型实例的数据集对象(默认为\think\model\Collection),支持在模型中单独设置查询数据集的返回对象的名称,例如:

  1. <?php
  2. namespace app\index\model;
  3. use think\Model;
  4. class User extends Model
  5. {
  6. // 设置返回数据集的对象名
  7. protected $resultSetType = '\app\common\Collection';
  8. }

resultSetType属性用于设置自定义的数据集使用的类名,该类应当继承系统的think\model\Collection类。

4.4 使用查询构造器

在模型中仍然可以调用数据库的链式操作和查询方法,可以充分利用数据库的查询构造器的优势。
例如:

  1. <?php
  2. User::where('id',10)->find();
  3. User::where('status',1)->order('id desc')->select();
  4. User::where('status',1)->limit(10)->select();

使用查询构造器直接使用静态方法调用即可,无需先实例化模型。

4.5 获取某个字段或者某个列的值

  1. <?php
  2. // 获取某个用户的积分
  3. User::where('id',10)->value('score');
  4. // 获取某个列的所有值
  5. User::where('status',1)->column('name');
  6. // 以id为索引
  7. User::where('status',1)->column('name','id');

valuecolumn方法返回的不再是一个模型对象实例,而是纯粹的值或者某个列的数组。

4.6 动态查询

支持数据库的动态查询方法,例如:

  1. <?php
  2. // 根据name字段查询用户
  3. $user = User::getByName('thinkphp');
  4. // 根据email字段查询用户
  5. $user = User::getByEmail('thinkphp@qq.com');

4.7 聚合查询

同样在模型中也可以调用数据库的聚合方法查询,例如:

  1. <?php
  2. User::count();
  3. User::where('status','>',0)->count();
  4. User::where('status',1)->avg('score');
  5. User::max('score');

4.8 数据分批处理

模型也可以支持对返回的数据分批处理,这在处理大量数据的时候非常有用,例如:

  1. <?php
  2. User::chunk(100,function($users) {
  3. foreach($users as $user){
  4. // 处理user模型对象
  5. }
  6. });

4.9 使用游标查询

模型也可以使用数据库的cursor方法进行游标查询,返回生成器对象

  1. <?php
  2. foreach(User::where('status', 1)->cursor() as $user){
  3. echo $user->name;
  4. }

user变量是一个模型对象实例。

4.10 查询缓存

get方法和all方法的支持使用查询缓存,可以直接在第二个参数传入true表示开启查询缓存。

  1. <?php
  2. $user = User::get(1,true);
  3. $list = User::all('1,2,3',true);

如果要设置缓存标识,则必须在第三个参数传入缓存标识。

  1. <?php
  2. $user = User::get(1,'','user');
  3. $list = User::all('1,2,3','','user_list');

4.11 主库读取

如果你采用分布式数据库,如果写入数据后立刻进行该数据的读取,将会导致数据读取失败,原因是数据库同步尚未完成。
规范的解决方案是在写入数据后,不要马上从从库读取,而应该调用master方法读取主库。

  1. <?php
  2. $user = new User;
  3. $user->name = 'thinkphp';
  4. $user->email = 'thinkphp@qq.com';
  5. $user->save();
  6. // 从主库读取数据
  7. $user->master()->get($user->id);

或者对关键的逻辑启用事务,在事务中的数据库操作都是基于主库的。
V5.1.12+版本开始,你可以在数据库配置文件中设置

  1. <?php
  2. // 主库写入后从主从库读取
  3. 'read_master' => true

设置开启后,一旦你的模型有写入操作,那么该请求后续的同一个模型的读操作都会自动从主库读取。
如果你只需要对某个模型生效,可以在你完成主库写入操作后,执行下模型类的readMaster方法。

  1. <?php
  2. $user = new User;
  3. $user->name = 'thinkphp';
  4. $user->email = 'thinkphp@qq.com';
  5. $user->save();
  6. // 从主库读取数据
  7. $user->readMaster(true)->get($user->id);

注意上述设置和方法仅对模型查询有效,直接调用Db类查询无效。

模型查询的最佳实践原则是:在模型外部使用静态方法进行查询,内部使用动态方法查询,包括使用数据库的查询构造器。模型的查询始终返回对象实例,但可以和数组一样使用。