##1.模型的定义
模型会自动对应数据表(表名是模型名加前缀且驼峰式写法),模型类的命名规则是除去表前缀的数据表名称,采用驼峰法命名,并且首字母大写(模型自动对应的数据表名称都是遵循小写+下划线规范,如果你的表名有大写的情况,必须通过设置模型的table属性。)
模型名 | 约定对应数据表名(假设数据库的前缀定义是 think_) |
---|---|
User | think_user |
UserType | think_user_type |
如果你希望给模型类添加后缀,需要设置name属性或者table属性。 |
<?php
namespace app\model;
use think\Model;
class UserModel extends Model
{
protected $name = 'user';
}
##2.模型设置
默认主键为id,如果你没有使用id作为主键名,需要在模型中设置属性:
<?php
namespace app\model;
use think\Model;
class User extends Model
{
protected $pk = 'uid';
}
如果你想指定数据表甚至数据库连接的话,可以使用:
<?php
namespace app\model;
use think\Model;
class User extends Model
{
// 设置当前模型对应的完整数据表名称
protected $table = 'think_user';
// 设置当前模型的数据库连接
protected $connection = 'db_config';
}
在Model.php中有一系列的protected数据是用于设置模型的
属性 | 描述 |
---|---|
name | 模型名(相当于不带数据表前后缀的表名,默认为当前模型类名) |
table | 数据表名(默认自动获取) |
suffix | 数据表后缀(默认为空) |
pk | 主键名(默认为id) |
connection | 数据库连接(默认读取数据库配置) |
query | 模型使用的查询类名称 |
field | 模型允许写入的字段列表(数组) |
schema | 模型对应数据表字段及类型 |
type | 模型需要自动转换的字段及类型 |
strict | 是否严格区分字段大小写(默认为true) |
disuse | 数据表废弃字段(数组) |
##模型初始化 | |
只要调用model就会触发初始化,但只有触发一次 | |
模型支持初始化,只需要定义init方法,例如: |
<?php
namespace app\model;
use think\Model;
class User extends Model
{
// 模型初始化
protected static function init()
{
//TODO:初始化内容
}
}
##模型操作
模型的操作方法无需和数据库查询一样调用必须首先调用table或者name方法,因为模型会按照规则自动匹配对应的数据表,例如:
Db::name('user')->where('id','>',10)->select();
改成模型操作的话就变成
User::where('id','>',10)->select();
##总结模型的定义 2022.11.07
模型就是和数据库自动关联,模型名与数据库表名相关,在database中定义好数据库,模型的$connection与数据库关联,通过在模型中改变protected的各种参数可以改变该模型对应的主键数据表名等等参数,相当于便捷版的数据库操作
#模型的操作
##添加一条数据
第一种是实例化模型对象后赋值并保存:
$user = new User;
$user->name = 'thinkphp';
$user->email = 'thinkphp@qq.com';
$user->save();
也可以直接传入数据到save方法批量赋值:
$user = new User;
$user->save([
'name' => 'thinkphp',
'email' => 'thinkphp@qq.com'
]);
最佳的建议是模型数据赋值之前就进行数据过滤,例如:
$user = new User;
// 过滤post数组中的非数据表字段数据
$data = Request::only(['name','email']);
$user->save($data);
获取自增id
$user = new User;
$user->name = 'thinkphp';
$user->email = 'thinkphp@qq.com';
$user->save();
// 获取自增ID
echo $user->id;
##批量插入
用saveAll()
$dataAll=
[
[
'name' => 'ahu1' //可以进行批量复制插入
],
[
'name' => 'ahu2' //可以进行批量复制插入
],
[
'name' => 'ahu3' //可以进行批量复制插入
]
];
$user->saveAll($dataAll);
echo 'saveall成功';
saveAll方法新增数据返回的是包含新增模型(带自增ID)的数据集对象。
saveAll方法新增数据默认会自动识别数据是需要新增还是更新操作,当数据中存在主键的时候会认为是更新操作。
##静态方法
还可以直接静态调用create方法创建并写入:
$user = User::create([
'name' => 'thinkphp',
'email' => 'thinkphp@qq.com'
]);
echo $user->name;
echo $user->email;
echo $user->id; // 获取自增ID
和save方法不同的是,create方法返回的是当前模型的对象实例。
create方法默认会过滤不是数据表的字段信息,可以在第二个参数可以传入允许写入的字段列表
// 只允许写入name和email字段的数据
$user = User::create([
'name' => 'thinkphp',
'email' => 'thinkphp@qq.com'
], ['name', 'email']);
echo $user->name;
echo $user->email;
echo $user->id; // 获取自增ID
最佳实践
新增数据的最佳实践原则:使用create方法新增数据,使用saveAll批量新增数据。
##查找并更新
在取出数据后,更改字段内容后使用save方法更新数据。这种方式是最佳的更新方式。
$user = User::find(1);
$user->name = 'thinkphp';
$user->email = 'thinkphp@qq.com';
$user->save();
对于复杂的查询条件,也可以使用查询构造器来查询数据并更新
$user = User::where('status',1)
->where('name','liuchen')
->find();
$user->name = 'thinkphp';
$user->email = 'thinkphp@qq.com';
$user->save();
实例化模型后调用save方法表示新增;
查询数据后调用save方法表示更新;
##总结模型的操作 2022.11.8
模型的操作无非是增删改查,通过save进行单个操作saveAll进行批量,当该模型实例化之后则是新增,当模型查询数据之后则是更新,模型的查询则通过where可以进行复杂的查询
##模型的字段设置
还没学
##模型的获取器和修改器
###获取器
获取器的作用是对模型实例的(原始)数据做出自动处理。一个获取器对应模型的一个特殊方法(该方法必须为public类型)
获取器的命名规范getFieldNameAttr
FieldName为数据表字段的驼峰转换,=相当于该获取器与该字段相绑定=
在模型文件中
<?php
namespace app\model;
use think\Model;
class User extends Model
{
public function getStatusAttr($value)
{
$status = [-1=>'删除',0=>'禁用',1=>'正常',2=>'待审核'];
return $status[$value];
}
}
在控制器中调用
$user = User::find(1);
echo $user->status; // 例如输出“正常”
获取器还可以定义数据表中不存在的字段,例如:
<?php
namespace app\model;
use think\Model;
class User extends Model
{
public function getStatusTextAttr($value,$data)
{
$status = [-1=>'删除',0=>'禁用',1=>'正常',2=>'待审核'];
return $status[$data['status']];
}
}
获取器方法的第二个参数$data传入的是当前的所有数据数组。
我们就可以直接使用status_text字段的值了,例如:
$user = User::find(1);
echo $user->status_text; // 例如输出“正常”
###获取原始数据
如果你定义了获取器的情况下,希望获取数据表中的原始数据,可以使用:
$user = User::find(1);
// 通过获取器获取字段
echo $user->status;
// 获取原始字段数据
echo $user->getData('status');
// 获取全部原始数据
dump($user->getData());
##动态获取器
可以支持对模型使用动态获取器,无需在模型类中定义获取器方法。
User::withAttr('name', function($value, $data) {
return strtolower($value);
})->select();
withAttr方法支持多次调用,定义多个字段的获取器。另外注意,withAttr方法之后不能再使用模型的查询方法,必须使用Db类的查询方法。
== 如果同时还在模型里面定义了相同字段的获取器,则动态获取器优先,也就是可以临时覆盖定义某个字段的获取器。==
##修改器
和获取器相反,修改器的主要作用是对模型设置的数据对象值进行处理。
修改器方法的命名规范为:
setFieldNameAttr
##总结模型获取器修改器 2022.11.8
获取器的话就是和字段相关联将该字段的数据直接转换成自己想要的方式,但是要特别注意字段名与该获取器的命名,相当于原始数据用数字标记然后通过获取器,转成自己所表达的意思,修改器
##模型的查询范围
可以对模型的查询和写入操作进行封装
在model层中定义一个scope开头的
scopeName 中 Name可以自己定义,相当于单独是个方法,可以有自己的名字
<?php
namespace app\model;
use think\Model;
class User extends Model
{
public function scopeThinkphp($query)
{
$query->where('name','thinkphp')->field('id,name');
}
public function scopeAge($query)
{
$query->where('age','>',20)->limit(10);
}
}
就可以进行下面的条件查询:
// 查找name为thinkphp的用户
User::scope('thinkphp')->find();
// 查找年龄大于20的10个用户
User::scope('age')->select();
// 查找name为thinkphp的用户并且年龄大于20的10个用户
User::scope('thinkphp,age')->select();
可以直接使用闭包函数进行查询,例如:
User::scope(function($query){
$query->where('age','>',20)->limit(10);
})->select();
复制
在查询的时候可以如下使用:
// 查询email包含thinkphp和分数大于80的用户
User::email('thinkphp')->score(80)->select();
查询范围的方法可以定义额外的参数
例如User模型类定义如下:
<?php
namespace app\model;
use think\Model;
class User extends Model
{
public function scopeEmail($query, $email)
{
$query->where('email', 'like', '%' . $email . '%');
}
public function scopeScore($query, $score)
{
$query->where('score', '>', $score);
}
}
全局查询范围
支持在模型里面设置globalScope属性,定义全局的查询范围
<?php
namespace app\model;
use think\Model;
class User extends Model
{
// 定义全局的查询范围
protected $globalScope = ['status'];
public function scopeStatus($query)
{
$query->where('status',1);
}
}
然后,执行下面的代码:
$user = User::find(1);
最终的查询条件会是
status = 1 AND id = 1
如果需要动态关闭所有的全局查询范围,可以使用:
// 关闭全局查询范围
User::withoutGlobalScope()->select();
可以使用withoutGlobalScope方法动态关闭部分全局查询范围。
User::withoutGlobalScope(['status'])->select();
查询范围总结 2022.11.10
查询范围就是在模型层定义一个带特定前缀scope的函数,函数名scope之后(例如scopeName)将’name’放入模型的scope中,调用时可以用 模型名::scope[‘name’]
搜索器
搜索器的作用是用于封装字段(或者搜索标识)的查询条件表达式,一个搜索器对应一个特殊的方法(该方法必须是public类型),方法命名规范为:
searchFieldNameAttr
FieldName为数据表字段的驼峰转换,搜索器仅在调用withSearch方法的时候触发。
搜索器的场景包括:
限制和规范表单的搜索条件;
预定义查询条件简化查询;
例如,我们需要给User模型定义name字段和时间字段的搜索器,可以使用:
<?php
namespace app\model;
use think\Model;
class User extends Model
{
public function searchNameAttr($query, $value, $data)
{
$query->where('name','like', $value . '%');
}
public function searchCreateTimeAttr($query, $value, $data)
{
$query->whereBetweenTime('create_time', $value[0], $value[1]);
}
}
然后,我们可以使用下面的查询
User::withSearch(['name','create_time'], [
'name' => 'think',
'create_time' => ['2018-8-1','2018-8-5'],
'status' => 1
])
->select();
数据集
模型的select查询方法返回数据集对象 think\model\Collection,该对象继承自think\Collection,因此具有数据库的数据集类的所有方法,而且还提供了额外的模型操作方法。
基本用法和数组一样,例如可以遍历和直接获取某个元素。
// 模型查询返回数据集对象
$list = User::where('id', '>', 0)->select();
// 获取数据集的数量
echo count($list);
// 直接获取其中的某个元素
dump($list[0]);
// 遍历数据集对象
foreach ($list as $user) {
dump($user);
}
// 删除某个元素
unset($list[0]);
自动时间戳
第一种方式是全局开启,在数据库配置文件中进行设置:
// 开启自动写入时间戳字段
'auto_timestamp' => true,
第二种是在需要的模型类里面单独开启:
<?php
namespace app\model;
use think\Model;
class User extends Model
{
protected $autoWriteTimestamp = true;
}
一旦配置开启的话,会自动写入create_time和update_time两个字段的值,默认为整型(int),如果你的时间字段不是int类型的话,可以直接使用:
// 开启自动写入时间戳字段
'auto_timestamp' => 'datetime',
或者
<?php
namespace app\model;
use think\Model;
class User extends Model
{
protected $autoWriteTimestamp = 'datetime';
}
默认的创建时间字段为create_time,更新时间字段为update_time,支持的字段类型包括timestamp/datetime/int。
写入数据的时候,系统会自动写入create_time和update_time字段,而不需要定义修改器
只读字段
只读字段用来保护某些特殊的字段值不被更改,这个字段的值一旦写入,就无法更改。 要使用只读字段的功能,我们只需要在模型中定义readonly属性:
更改模型中的$readonly并指定对应的字段
<?php
namespace app\model;
use think\Model;
class User extends Model
{
protected $readonly = ['name', 'email'];
}
支持动态设置只读字段,例如:
$user = User::find(5);
// 更改某些字段的值
$user->name = 'TOPThink';
$user->email = 'Topthink@gmail.com';
$user->address = '上海静安区';
// 保存更改后的用户数据
$user->readonly(['name','email'])->save();
只读字段仅针对模型的更新方法,如果使用数据库的更新方法则无效,例如下面的方式无效。
(即直接对模型进行更改然后save是模型的操作,通过where查询到之后更改属于数据的操作,是否在数据库生成数据库语句来判断是模型操作还是数据库操作)
$user = new User;
// 要更改字段值
$data['name'] = 'TOPThink';
$data['email'] = 'Topthink@gmail.com';
$data['address'] = '上海静安区';
// 保存更改后的用户数据
$user->where('id', 5)->update($data);
软删除
相当于自动时间戳,在模型中先申明好softdlete trait
在实际项目中,对数据频繁使用删除操作会导致性能问题,软删除的作用就是把数据加上删除标记,而不是真正的删除,同时也便于需要的时候进行数据的恢复要使用软删除功能,需要引入SoftDelete trait,例如User模型按照下面的定义就可以使用软删除功能:
<?php
namespace app\model;
use think\Model;
use think\model\concern\SoftDelete;
class User extends Model
{
use SoftDelete;
protected $deleteTime = 'delete_time';
}
deleteTime属性用于定义你的软删除标记字段,ThinkPHP的软删除功能使用时间戳类型(数据表默认值为Null),用于记录数据的删除时间。
可以支持defaultSoftDelete属性来定义软删除字段的默认值,在此之前的版本,软删除字段的默认值必须为null
类型转换
设置model的protected的$type的值就可进行模型转换
支持给字段设置类型自动转换,会在写入和读取的时候自动进行类型转换处理,例如:
<?php
namespace app\model;
use think\Model;
class User extends Model
{
protected $type = [
'status' => 'integer',
'score' => 'float',
'birthday' => 'datetime',
'info' => 'array',
];
}
下面是一个类型自动转换的示例:
$user = new User;
$user->status = '1';
$user->score = '90.50';
$user->birthday = '2015/5/1';
$user->info = ['a'=>1,'b'=>2];
$user->save();
var_dump($user->status); // int 1
var_dump($user->score); // float 90.5;
var_dump($user->birthday); // string '2015-05-01 00:00:00'
var_dump($user->info);// array (size=2) 'a' => int 1 'b' => int 2
模型输出
模型数据的模板输出可以直接把模型对象实例赋值给模板变量,在模板中可以直接输出,例如:
<?php
namespace app\controller;
use app\model\User;
use think\facade\View;
class Index
{
public function index()
{
$user = User::find(1);
View::assign('user', $user);
return View::fetch();
}
}
在模板文件中可以使用
{$user.name}
{$user.email}
模板中的模型数据输出一样会调用获取器。
数组转换
用toArry()可以将模型示例转换成数组输出
可以使用toArray方法将当前的模型实例输出为数组,例如:
$user = User::find(1);
dump($user->toArray());
支持设置不输出的字段属性:
$user = User::find(1);
dump($user->hidden(['create_time','update_time'])->toArray());
数组输出的字段值会经过获取器的处理,也可以支持追加其它获取器定义(不在数据表字段列表中)的字段,例如:
$user = User::find(1);
dump($user->append(['status_text'])->toArray());
关联模型
建立好两张表,创建两个模型,两个空模型 ,在profile表中写一个方法,正向关联hasone,第一个参数是要关联的模型(附表),第二个参数是外键默认user_id,在使用的时候,控制器中通过find()查询到一条数据,$user->profile->hobby通过
return $this->hasOne(Profile::class,'user_id');
一对一关联
关联定义
定义一对一关联,例如,一个用户都有一个个人资料,我们定义User模型如下:
<?php
namespace app\model;
use think\Model;
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
hasOne方法的参数包括:
hasOne(‘关联模型类名’, ‘外键’, ‘主键’);
除了关联模型外,其它参数都是可选。
关联模型(必须):关联模型类名
外键:默认的外键规则是当前模型名(不含命名空间,下同)+_id ,例如user_id
主键:当前模型主键,默认会自动获取也可以指定传入
关联查询
定义好关联之后,就可以使用下面的方法获取关联数据:
$user = User::find(1);
// 输出Profile关联模型的email属性
echo $user->profile->email;