JSON字段

数据库JSON

  1. 数据库写入JSON字段,直接通过数组的方式即可完成;
$data = [
    'username' => '辉夜',
    'password' => '123',
    'gender' => '女',
    'email' => 'huiye@163.com',
    'price' => 90,
    'details' => '123',
    'uid' => 1011,
    'status' => 1,
    'list' => ['username'=>'辉夜', 'gender'=>'女','email'=>'huiye@163.com'],
];
Db::name('user')->json(['list'])->insert($data);

QQ截图20200730101212.png

  1. 如果要查询数据时,正确转换json数据格式,也需要设置json方法;
Db::name('user')->json(['list'])->find(278);

QQ截图20200730101516.png

  1. 如果要将json字段里的数据作为查询条件,可以通过如下方式实现:
$user = Db::name('user')->json(['list'])->where('list->username', '辉夜')->find();

QQ截图20200730101720.png

  1. 如果想完全修改json数据,可以使用如下的方式实现:
$data['list'] = ['username'=>'李白', 'gender'=>'男', 'email'=>'libai@163.com'];
Db::name('user')->json(['list'])->where('id', 278)->update($data);

QQ截图20200730101804.png

  1. 如果只想修改json数据里的某一个项目,可以使用如下的方式实现:
$data['list->username'] = '李黑';
Db::name('user')->json(['list'])->where('id', 278)->update($data);

QQ截图20200730101830.png

模型JSON

  1. 想要写入json字段的字符字段,需要设置;
protected $json = ['list'];
  1. 使用模型方式去新增包含json数据的字段;
$user = new UserModel();
$user->username = '李白';
$user->password = '123';
$user->gender = '男';
$user->email = 'libai@163.com';
$user->price = 100;
$user->uid = 1011;
$user->status = 1;
$user->details = 123;
$user->list = ['username'=>'辉夜', 'gender'=>'女','email'=>'huiye@163.com'];
$user->save();

QQ截图20200730101945.png

  1. 也可以通过对象的方式,进行对json字段的写入操作;
$list = new \StdClass();
$list->username = '辉夜';
$list->gender = '女';
$list->email = 'huiye@163.com';
$list->uid = 1011;
$user->list = $list;

QQ截图20200730102122.png

  1. 通过对象调用方式,直接获取json里面的数据;
$user = UserModel::find(278);
return $user->list->username;

QQ截图20200730102334.png

  1. 通过json的数据查询,获取一条数据;
$user = UserModel::where('list->username', '辉夜')->find();
return $user->list->email;

QQ截图20200730102407.png

  1. 更新修改json数据,直接通过对象方式即可;
$user = UserModel::find(278);
$user->list->username = '李白';
$user->save();

QQ截图20200730102559.png

模型的软删除

模型软删除

  1. 介于数据库软删除没有太多的可操作的方法,官方手册推荐使用模型软操作;
  2. 首先,需要在模型端设置软删除的功能,引入SoftDelete,它是trait;
use SoftDelete;
protected $deleteTime = 'delete_time';
  1. delete_time默认我们设置的是null,如果你想更改这个默认值,可以设置:
//protected $defaultSoftDelete = 0;
  1. 软删除和方法如下,包括destroy()delete():
UserModel::destroy(289);
UserModel::find(287)->delete();

QQ截图20200730105840.png

  1. 默认情况下,开启了软删除功能的查询,模型会自动屏蔽被软删除的数据;
$user = UserModel::select();
return json($user);
  1. 在开启软删除功能的前提下,使用withTrashed()方法取消屏蔽软删除的数据;
$user = UserModel::withTrashed()->select();
return json($user);

QQ截图20200730110246.png

  1. 如果只想查询被软删除的数据,使用onlyTrashed()方法即可;
$user = UserModel::onlyTrashed()->select();
return json($user);

QQ截图20200730110321.png

  1. 如果想让某一条被软删除的数据恢复到正常数据,可以使用restore()方法;
$user = UserModel::onlyTrashed()->find();
$user->restore();
  1. 如果想让一条软删除的数据真正删除,在恢复正常后,使用force()delete();
$user = UserModel::onlyTrashed()->find(193);
$user->restore();
$user->force()->delete(); //或 UserModel::destroy(288, true)

模型和数据库的事件

数据库事件

  1. 当你执行增删改查的时候,可以触发一些事件来执行额外的操作;
  2. 这些额外的操作事件,可以部署在构造方法里等待激活执行;
  3. 数据库事件方法为Db::event('事件名', '执行函数'),具体事件名如下:

QQ截图20200730111322.png

  1. 数据库事件只支持:find,select,update,delete,insert这几个方法;
  2. 在控制器端,事件一般可以写在初始化方法里,方便统一管理;
public function initialize()
{
    Db::event('before_select', function ($query) {
        echo '执行了批量查询操作!';
    });
    Db::event('after_update', function ($query) {
        echo '执行了修改操作!';
    });
}

QQ截图20200730111506.png

模型事件

  1. 支持的事件类型更加的丰富,具体如下:

QQ截图20200730111530.png

  1. 在模型端使用静态方法调用即可完成事件触发;
protected static function onAfterRead($query)
{
    echo '执行了查询方法';
}
protected static function onBeforeUpdate($query)
{
    echo '准备修改中...';
}
protected static function onAfterUpdate($query)
{
    echo '修改完毕...';
}

QQ截图20200730111810.png

关联模型初探

关联模型定义

  1. 关联模型,顾名思义,就是将表与表之间进行关联和对象化,更高效的操作数据;
  2. 我们已经有了一张tp_user表,主键为id;我们需要一个附属表,来进行关联;
  3. 附属表tp_profile,建立两个字段user_idhobby,外键是user_id;
  4. 创建User模型和Profile模型,均为空模型;
  5. User模型端,需要关联Profile,具体方式如下:
class User extends Model
{
    public function profile()
    {
        //hasOne 表示一对一关联,参数一表示附表,参数二外键,默认 user_id
        return $this->hasOne(Profile::class,'user_id');
    }
}
  1. 创建一个控制器用于测试输出;
$user = UserModel::find(21);
return json($user->profile);
return $user->profile->hobby;

QQ截图20200730135903.png

  1. 对于关联方式,系统提供了9种方案,具体如下:

QQ截图20200730135941.png

  1. 上面的例子,我们采用了一对一的关联模型,它还有相对的反向关联;
class Profile extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

QQ截图20200730140153.png

  1. 正反向关联也就是关联关系和相对的关联关系,具体如下表:

QQ截图20200730140209.png

一对一关联查询

hasOne模式

  1. hasOne模式,适合主表关联附表,具体设置方式如下:
  • hasOne('关联模型','外键','主键');
  • return $this->hasOne(Profile::class,'user_id','id');
  • 关联模型(必须):关联的模型名或者类名
  • 外键:默认的外键规则是,当前模型名_id(不含命名空间),例如user_id
  • 主键:当前模型主键,默认会自动获取也可以指定传入
  1. 在上一节课,我们了解了表与表关联后,实现的查询方案;
$user = UserModel::find(21);
return $user->profile->hobby;

QQ截图20200730135903.png

  1. 使用save()方法,可以设置关联修改,通过主表修改附表字段的值;
$user = UserModel::find(19);
$user->profile->save(['hobby'=>'酷爱小姐姐']);

QQ截图20200730141312.png

  1. ->profile属性方式可以修改数据,->profile()方法方式可以新增数据;
$user->profile()->save(['hobby'=>'不喜欢吃青椒']);

QQ截图20200730141423.png

belongsTo模式

  1. belongsTo 模式,适合附表关联主表,具体设置方式如下:
  • belongsTo('关联模型','外键','关联主键');
  • return $this->belongsTo(Profile::class,'user_id', 'id');
  • 关联模型(必须):模型名或者模型类名
  • 外键:当前模型外键,默认的外键名规则是关联模型模型名_id(不含命名空间)
  • 关联主键:关联模型主键,一般会自动获取也可以指定传入
  1. 对于belongsTo()的查询方案,上一节课已经了解过,如下:
$profile = ProfileModel::find(1);
return $profile->user->email;

QQ截图20200730140153.png

  1. 使用hasOne()也能模拟belongsTo()来进行查询;
//参数一表示的是 User 模型类的 profile 方法,而非 Profile 模型类
$user = UserModel::hasWhere('profile', ['id'=>2])->find();
return json($user);

QQ截图20200730141839.png

//采用闭包,这里是两张表操作,会导致 id 识别模糊,需要指明表
$user = UserModel::hasWhere('profile', function ($query) {
    $query->where('id', 2);
})->select();
return json($user);

QQ截图20200730142044.png

一对多关联查询

hasMany 模式

  1. hasMany模式,适合主表关联附表,实现一对多查询,具体设置方式如下:
  • hasMany('关联模型','外键','主键');
  • return $this->hasMany(Profile::class,'user_id', 'id');
  • 关联模型(必须):模型名或者模型类名
  • 外键:关联模型外键,默认的外键名规则是当前模型名_id
  • 主键:当前模型主键,一般会自动获取也可以指定传入
  1. 在上一节课,我们了解了表与表关联后,实现的查询方案;
$user = UserModel::find(19);
return json($user->profile);

QQ截图20200730144822.png

  1. 使用->profile()方法模式,可以进一步进行数据的筛选;
$user->profile()->where('id', '>', 10)->select();
$user->profile->where('id', '>', 10);

QQ截图20200730145032.png

  1. 使用has()方法,查询关联附表的主表内容,比如大于等于2条的主表记录;
UserModel::has('profile', '>=', 2)->select();

QQ截图20200730145445.png

  1. 使用hasWhere()方法,查询关联附表筛选后记录,比如兴趣审核通过的主表记录;
UserModel::hasWhere('profile', ['status'=>1])->select();

QQ截图20200730145551.png

  1. 使用save()saveAll()进行关联新增和批量关联新增,方法如下:
$user = UserModel::find(19);
$user->profile()->save(['hobby'=>'测试喜好', 'status'=>1]);
$user->profile()->saveAll([
    ['hobby'=>'测试喜好', 'status'=>1],
    ['hobby'=>'测试喜好', 'status'=>1]
]);

QQ截图20200730145633.png

  1. 使用together()方法,可以删除主表内容时,将附表关联的内容全部删除;
  • 该操作为事务操作,表引擎必须为InnoDB,否则会出现以下报错
  • SQLSTATE[HY000]: General error: 1785 Statement violates GTID consistency: Updates to non-transactional tables can only be done in either autocommitted statements or single-statement transactions, and never in the same statement as updates to transactional tables.
$user = UserModel::with('profile')->find(227);
$user->together(['profile'])->delete();

QQ截图20200730150027.png

关联预载入

关联预载入

  1. 在普通的关联查询下,我们循环数据列表会执行n+1次SQL查询;
$list = UserModel::select([19, 20, 21]);
foreach ($list as $user) {
    dump($user->profile);
}

QQ截图20200730151114.png

  1. 上面继续采用一对一的构建方式,打开trace调试工具,会得到四次查询;
  2. 如果采用关联预载入的方式,将会减少到两次,也就是起步一次,循环一次;
$list = UserModel::with(['profile'])->select([19, 20, 21]);
foreach ($list as $user) {
    dump($user->profile);
}

QQ截图20200730151200.png

  1. 关联预载入减少了查询次数提高了性能,但是不支持多次调用;
  2. 如果你有主表关联了多个附表,都想要进行预载入,可以传入多个模型方法即可;
  3. 为此,我们再创建一张表tp_book,和tp_profile一样,关联tp_user;
$list = UserModel::with(['profile','book'])->select([19, 20, 21]);
foreach ($list as $user) {
    dump($user->profile.$user->book);
}

QQ截图20200730152012.png

  1. 如果想要在关联模型实现链式操作,可以使用闭包,比如添加->field();
$list = UserModel::field('id,username')->with(['profile'=>function ($query) {
    $query->field('user_id, hobby');
}])->select([19,20,21]);

QQ截图20200730152255.png

  1. 关联预载入还提供了一个延迟预载入,就是先执行select()load()载入;
$list = UserModel::select([19, 20, 21]);
$list->load(['profile']);
foreach ($list as $user) {
    dump($user->profile);
}

QQ截图20200730152447.png

关联统计和输出

关联统计

  1. 使用withCount()方法,可以统计主表关联附表的个数,输出用profile_count;
$list = UserModel::withCount(['profile'])->select([19,20,21]);
foreach ($list as $user) {
    echo $user->profile_count;
}

QQ截图20200730154052.png

  1. 关联统计的输出采用关联方法名_count,这种结构输出;
  2. 不单单支持Count,还有如下统计方法,均可支持;
  3. withMax(),withMin(),withSum(),withAvg()等;
  4. 除了withCount()不需要指定字段,其它均需要指定统计字段;
$list = UserModel::withSum(['profile'], 'status')->select([19,20,21]);
foreach ($list as $user) {
    echo $user->profile_sum.'<br>';
}

QQ截图20200730154223.png

  1. 对于输出的属性,可以自定义:
$list = UserModel::withSum(['profile'=>'p_s'], 'status')->select([19,20,21]);
foreach ($list as $user) {
    echo $user->p_s.'<br>';
}

QQ截图20200730154513.png

关联输出

  1. 使用hidden()方法,隐藏主表字段或附属表的字段;
$list = UserModel::with('profile')->select();
// return json($list->hidden(['profile.status']));

return json($list->hidden(['username','password','profile'=>['status','id']]));

QQ截图20200730154833.png

  1. 使用visible()方法,只显示相关的字段;
$list->visible(['profile.status']);

QQ截图20200730155011.png

  1. 使用append()方法,添加一个额外字段,比如另一个关联的对象模型;
$list->append(['book']);

QQ截图20200730155145.png

多对多关联查询

多对多关联

  1. 复习一下一对一,一个用户对应一个用户档案资料,是一对一关联;
  2. 复习一下一对多,一篇文章对应多个评论,是一对多关联;
  3. 多对多怎么理解,分解来看,一个用户对应多个角色,而一个角色对应多个用户;
  4. 那么这种对应关系,就是多对多关系,最经典的应用就是权限控制;
  5. 首先,我们来看多对多关系的三张表,具体如下:

QQ截图20200730163916.png

  1. tp_user:用户表;tp_role:角色表;tp_access:中间表;
  2. access表包含了userrole表的关联id,多对多模式;
  3. User.php的模型中,设置多对多关联,方法如下:
public function roles()
{
    return $this->belongsToMany(Role::class, Access::class);
}
  1. roles方法中,belongsToMany为多对多关联,具体参数如下:
  • belongsToMany('关联模型','中间表','外键','关联键');
  • $this->belongsToMany(Role::class, Access::class, 'role_id', 'user_id');
  1. Role.phpAccess.php创建一个空模型即可,无须创建任何;
  2. 注意:Role继承Model即可,而中间表需要继承Pivot;
  3. 测试查询方式如下:
//得到一个用户:蜡笔小新
$user = UserModel::find(21);
//获取这个用户的所有角色
$roles = $user->roles;
//输出这个角色所具有的权限
return json($roles);

QQ截图20200730164636.png

  1. 当我们要给一个用户创建一个角色时,用到多对多关联新增;
  2. 而关联新增后,不但会给tp_role新增一条数据,也会给tp_access新增一条;
$user->roles()->save(['type'=>'测试管理员']);
// $user->roles()->saveAll([[...],[...]]);

QQ截图20200730164905.png

  1. 一般来说,上面的这种新增方式,用于初始化角色比较合适;
  2. 也就是说,各种权限的角色,并不需要再新增了,都是初始制定好的;
  3. 那么,我们真正需要就是通过用户表新增到中间表关联即可;
$user->roles()->save(1);

QQ截图20200730165027.png

$user->roles()->save(Role::find(1));
$user->roles()->saveAll([1,2,3]);

QQ截图20200730165108.png

$user->roles()->attach(1);
$user->roles()->attach(2, ['details'=>'测试详情']);

QQ截图20200730165139.png

  1. 除了新增,还有直接删除中间表数据的方法:
$user->roles()->detach(2);

QQ截图20200730165236.png