模型的字段设置

字段设置

  1. 模型的数据字段和表字段是对应关系,默认会自动获取,包括字段的类型;
  2. 自动获取会导致增加一次查询,如果在模型中配置字段信息,会减少内存开销;

QQ截图20200729141334.png

  1. 可以在模型设置$schema字段,明确定义字段信息,字段需要对应表写完整;
//设置字段信息,需要写完整的数据表字段
protected $schema = [
    'id' => 'int',
    'username' => 'string',
    'status' => 'int',
    'create_time' => 'datetime',
    '...' => '...'
];

QQ截图20200729142038.png

  1. 系统提供了一条命令,生成一个字段信息缓存,可以自动生成;
php think optimize:schema
  1. 生成后的字段缓存文件在runtimeschema文件加下;
  2. 我们可以先把这里的键值对复制到$schema属性上,开启trace测试效果;
  3. 这时,在控制器执行查询,会发现减少了一次SQL查询;
  4. 只不过,大可不必设置$schema属性,因为它只对模型有效;
  5. 如果想模型和数据库Db类同时有效,直接运用字段缓存文件即可;
  6. 默认情况下字段缓存文件是关闭状态,需要在config/database.php开启;
// 开启字段缓存
'fields_cache' => true, 
  1. 当数据获取到后,想要单独获取数据可以用->和数组方式来获取;
$user = UserModel::find(19);
echo $user->username;
echo $user['email'];

QQ截图20200729142320.png

  1. 如果我们在模型端把数据整理好,交给控制器直接调用,如下方式:
//模型端
public function getUsername($id)
{
    $obj = self::find($id);
    return $obj->getAttr('username');
}
//控制器端调用
$user = new UserModel();
return $user->getUsername(19);

QQ截图20200729143107.png

  1. 字段的赋值操作,也可以是->和数组方式,作用就是提交给模型处理;
$user = new UserModel();
$user->username = 'Mr.Lee';
// 实测只有对象方式生效
$user['email'] = 'lee@163.com';

QQ截图20200729143359.png

  1. 默认情况下,字段是严格区分大小写的,也就是需要和数据表字段保持一致;
$user = UserModel::find(19);
echo $user->create_time;

QQ截图20200729143802.png

  1. 我们可以在模型属性$strict设置为false即可实现非严格字段;
echo $user->createTime; //并非肆无忌惮的不严格,只能首字母大写

QQ截图20200729150353.png

模型的获取器和修改器

模型获取器

  1. 获取器的作用是对模型实例的数据做出自动处理;
  2. 一个获取器对应模型的一个特殊方法,该方法为public;
  3. 方法名的命名规范为:getFieldAttr();
  4. 举个例子,数据库表示状态status字段采用的是数值;
  5. 而页面上,我们需要输出status字段希望是中文,就可以使用获取器;
  6. User模型端,我创建一个对外的方法,如下:
public function getStatusAttr($value)
{
    $status = [-1=>'删除', 0=>'禁用', 1=>'正常', 2=>'待审核'];
    return $status[$value];
}
  1. 然后,在控制器端,直接输出数据库字段的值即可得到获取器转换的对应值;
$user = UserModel::find(19);
return $user->status;

QQ截图20200729151833.png

  1. 除了getFieldAttrField可以是字段值,也可以是自定义的虚拟字段;
// 模型
public function getNothingAttr($value, $data)
{
    $myGet = [-1=>'删除', 0=>'禁用', 1=>'正常', 2=>'待审核'];
    return $myGet[$data['status']];
}

// 控制器
return $user->nothing;

QQ截图20200729152054.png

  1. Nothing这个字段不存在,而此时参数$value只是为了占位,并未使用;
  2. 第二个参数$data得到的是筛选到的数据,然后得到最终值;
  3. 如果你定义了获取器,并且想获取原始值,可以使用getData()方法;
return $user->getData('status');

QQ截图20200729152154.png

  1. 直接输出无参数的getData(),可以得到原始值,而$user输出是改变后的;
dump($user->getData());
dump($user);

QQ截图20200729152336.png

  1. 使用WithAttr在控制器端实现动态获取器,比如设置所有email为大写;
$user = UserModel::WithAttr('email', function ($value) {
    return strtoupper($value);
})->select();
return json($user);

QQ截图20200729152554.png

  1. 使用WithAttr在控制器端实现动态获取器,比如设置status翻译为中文;
$user = UserModel::WithAttr('status', function ($value) {
    $status = [-1=>'删除', 0=>'禁用', 1=>'正常', 2=>'待审核'];
    return $status[$value];
})->select();
return json($user);

QQ截图20200729152721.png

  1. 同时定义了模型获取器和动态获取器,那么动态获取器优先级更高;

模型修改器

  1. 模型修改器的作用,就是对模型设置对象的值进行处理;
  2. 比如,我们要新增数据的时候,对数据就行格式化、过滤、转换等处理;
  3. 模型修改器的命名规则为:setFieldAttr;
  4. 我们要设置一个新增,规定邮箱的英文都必须大写,修改器如下:
public function setEmailAttr($value)
{
    return strtoupper($value);
}

QQ截图20200729153047.png

  1. 除了新增,会调用修改器,修改更新也会触发修改器;
  2. 模型修改器只对模型方法有效,调用数据库的方法是无效的,比如->insert();

模型的查询范围

模型查询范围

  1. 在模型端创建一个封装的查询或写入方法,方便控制器端等调用;
  2. 比如,封装一个筛选所有性别为男的查询,并且只显示部分字段5条;
  3. 方法名规范:前缀scope,后缀随意,调用时直接把后缀作为参数使用;
public function scopeMale($query)
{
    $query->where('gender', '男')->field('id,username,gender,email')->limit(5);
}
  1. 在控制器端,我们我们直接调用并输出结果即可;
// $result = UserModel::scope('male')->select();
$result = UserModel::male()->select();
return json($result);

QQ截图20200729154647.png

  1. 查询封装可以传递参数,比如,通过邮箱查找某人;
//模型
public function scopeEmail($query, $value)
{
    $query->where('email', 'like', '%'.$value.'%');
}
// 控制器
$result = UserModel::scope('email', 'xiao')->select();
//$result = UserModel::email('xiao')->select();
return json($result);

QQ截图20200729154855.png

  1. 也可以实现多个查询封装方法连缀调用,比如找出邮箱xiao并大于80分的;
//模型
public function scopeEmail($query, $value)
{
    $query->where('email', 'like', '%'.$value.'%');
}
public function scopePrice($query, $value)
{
    $query->where('price', '>', $value);
}
// 控制器
$result = UserModel::scope('email', 'xiao')->scope('price', 80)->select();
//$result = UserModel::email('xiao')->price(80)->select();
return json($result);

QQ截图20200729155130.png

  1. 查询范围只能使用find()select()两种方法;
  2. 全局范围查询,就是在此模型下不管怎么查询都会加上全局条件;
// 定义全局的查询范围
protected $globalScope = ['status'];
//全局范围
public function scopeStatus($query)
{
    $query->where('status',1);
}

QQ截图20200729155326.png

  1. 在定义了全局查询后,如果想取消这个查询的所有全局查询,可以用下面方法;
UserModel::withoutGlobalScope();
  1. 在定义了全局查询后,如果想取消这个查询的部分全局查询,可以添加参数指定;
UserModel::withoutGlobalScope(['status']);

QQ截图20200729155738.png

模型的搜索器和数据集

模型搜索器

  1. 搜索器是用于封装字段(或搜索标识)的查询表达式,类似查询范围;
  2. 一个搜索器对应模型的一个特殊方法,该方法为public;
  3. 方法名的命名规范为:searchFieldAttr();
  4. 举个例子,我们要封装一个邮箱字符模糊查询,然后封装一个时间限定查询;
  5. User模型端,我创建两个对外的方法,如下:
public function searchEmailAttr($query, $value, $data)
{
    $query->where('email', 'like', $value.'%');
}
public function searchCreateTimeAttr($query, $value, $data)
{
    $query->whereBetweenTime('create_time', $value[0], $value[1]);
}
  1. 然后,在控制器端,通过withSearch()方法实现模型搜索器的调用;
$result = UserModel::withSearch(['email', 'create_time'],[
    'email' => 'xiao',
    'create_time' => ['2014-1-1', '2017-1-1']
])->select();

QQ截图20200729161622.png

  1. withSearch()中第一个数组参数,限定搜索器的字段,第二个则是表达式值;
  2. 如果想在搜索器查询的基础上再增加查询条件,直接使用链式查询即可;
$result = UserModel::withSearch(['email', 'create_time'],[
    'email' => 'xiao',
    'create_time' => ['2014-1-1', '2017-1-1']
])->where('gender', '女')->select();

QQ截图20200729162446.png

  1. 如果你想在搜索器添加一个可以排序的功能,具体如下:
// 模型
public function searchEmailAttr($query, $value, $data)
{
    $query->where('email', 'like', $value.'%');
    if (isset($data['sort'])) {
        $query->order($data['sort']);
    }
}
// 控制器
$result = UserModel::withSearch(['email', 'create_time'],[
    'email' => 'xiao',
    'create_time' => ['2014-1-1', '2017-1-1'],
    'sort' => ['price'=>'desc']
])->select();

QQ截图20200729163152.png

  1. 搜索器的第三个参数$data,可以得到withSearch()方法第二参数的值;
  2. 字段也可以设置别名:'create_time'=>'ctime'

模型数据集

  1. 数据集也是直接继承collection类,所以和数据库方式一样;
  2. 数据集对象和数组操作方法一样,循环遍历、删除元素等;
  3. 判断数据集是否为空,我们需要采用isEmpty()方法;
$resut = UserModel::where('id', 111)->select();
if ($resut->isEmpty()) {
    return '没有数据!';
}

QQ截图20200729163254.png

  1. 更多数据集方法,直接参考数据库那篇的表格即可;
  2. 使用模型方法hidden()可以隐藏某个字段,使用visible()显示只某个字段;
  3. 使用append()可以添加某个获取器字段,使用withAttr()对字段进行函数处理;
$result = UserModel::select();
$result->hidden(['email'])->append(['nothing'])->withAttr('email',
function ($value) {
return strtoupper($value);
});
return json($result);

QQ截图20200729163524.png

模型的自动时间戳和只读字段

模型自动时间戳

  1. 如果你想全局开启,在database.php中,设置为true;
// 自动写入时间戳字段
'auto_timestamp' => true, 
  1. 如果你只想设置某一个模型开启,需要设置特有字段;
//开启自动时间戳
protected $autoWriteTimestamp = true;
  1. 当然,还有一种方法,就是全局开启,单独关闭某个或某几个模型为false;
  2. 自动时间戳开启后,会自动写入create_timeupdate_time两个字段;
  3. 此时,它们的默认的类型是int,如果是时间类型,可以更改如下:
'auto_timestamp' => 'datetime', //或
protected $autoWriteTimestamp = 'datetime';
  1. 都配置完毕后,当我们新增一条数据时,无须新增create_time会自动写入时间;
  2. 同理,当我们修改一条数据时,无须修改update_time会自动更新时间;
  3. 自动时间戳只能在模型下有效,数据库方法不可以使用;
  4. 如果创建和修改时间戳不是默认定义的,也可以自定义;
protected $createTime = 'create_at';
protected $updateTime = 'update_at';
  1. 如果业务中只需要create_time而不需要update_time,可以关闭它;
protected $updateTime = false;
  1. 也可以动态实现不修改update_time,具体如下:
$user->isAutoWriteTimestamp(false)->save();

模型只读字段

  1. 模型中可以设置只读字段,就是无法被修改的字段设置;
  2. 我们要设置usernameemail不允许被修改,如下:
protected $readonly = ['username', 'email'];
  1. 除了在模型端设置,也可以动态设置只读字段;
$user->readonly(['username', 'email'])->save();
  1. 同样,只读字段只支持模型方式不支持数据库方式;

模型的数据类型和转换

模型类型转换

  1. 系统可以通过模型端设置写入或读取时对字段类型进行转换;
  2. 我们这里,通过读取的方式来演示部分效果;
  3. 在模型端设置你想要类型转换的字段属性,属性值为数组;
protected $type = [
'price' => 'integer',
'status' => 'boolean',
'create_time' => 'datetime:Y-m-d'
];
  1. 数据库查询读取的字段很多都是字符串类型,我们可以转换成如下类型:
  • integer(整型)
  • float(浮点型)
  • boolean(布尔型)
  • array(数组)
  • object(对象)
  • serialize(序列化)
  • json(json)
  • timestamp(时间戳)
  • datetime(日期)
  1. 由于数据库没有那么多类型演示,常用度不显著,我们提供几个方便演示的;
public function typeC()
{
    $user = UserModel::find(19);
    var_dump($user->price);
    var_dump($user->status);
    var_dump($user->create_time);
}
  1. 类型转换还是会调用属性里的获取器等操作,编码时要注意这方面的问题;
  2. 废弃字段,当某个字段在开发项目版本升级中不再使用,可以设置为废弃字段;
  3. 设置废弃字段后,这个字段就不在查询数据列表里了,写入忽略(存疑);
//设置废弃字段
protected $disuse = ['status', 'uid'];