前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ThinkPHP6.0学习笔记-模型操作

ThinkPHP6.0学习笔记-模型操作

作者头像
Mirror王宇阳
发布2020-11-13 11:13:32
3.6K0
发布2020-11-13 11:13:32
举报

ThinkPHP模型

模型定义

app目录下创建Model目录,即可创建模型文件

定义一个和数据库表相匹配的模型

代码语言:javascript
复制
use think\Model;

class User extends Model
{
    
}

User会自动匹配对于数据库中的数据表tp_user

模型命名后缀,是为了防止关键字冲突,可以开启应用类后缀:创建Class UserModel use think\Model; class UserModel extends Model { //指向数据表 protected $name = 'user'; protected $table = 'tp_user'; }

模型类的定义需要去除表前缀,采用驼峰式命名首字母大写

代码语言:javascript
复制
tp_user 		==>		User
tp_user_type	==> 	UserType

创建于数据表相匹配的模型类后,可以在使用User::*方法来调用数据库进行操作

代码语言:javascript
复制
namespace app\controller;
use think\facade\Db;
use app\model;

class DataTest
{
    public function index()
    {
        $user = model\User::select();
        return json($user);

    }
}
设置模型

模型中默认的主键为id,也可以在模型类中设置主键字段名$pk

代码语言:javascript
复制
protected $pk = 'uid';

在控制器中调用模型操作,发生重名可以设置别名

代码语言:javascript
复制
use app\model\User as UserModel;

模型类中可以定义指向数据库表

代码语言:javascript
复制
protected $table 	= 	'tp_user';	//包含前缀
protected $name		=	'user';		//不含前缀

模型类初始化操作(控制器也有),但是必须设置static静态方法

代码语言:javascript
复制
protected static funtion init()
{
	echo "初始化";
}

模型设置属性:

属性

描述

name

指向数据表名(无前缀),默认为当前模型名

table

数据表

suffix

数据表后缀(默认为空)

pd

设置数据表主键字段名(默认为id)

connection

数据库连接(默认加载数据库配置database.php)

query

模型使用的查询类名称

field

指定允许操作的字段(支持数组)

schema

模型对应数据表字段和类型

type

模型需要自动转换的字段和类型

strict

是否严重区分字段大小(默认true)

disuse

数据表废弃字段


模型字段

模型的数据字段和对应的数据表字段是对应的,默认会自动获取(以及类型),自动获取的过程会加一次查询操作(浪费资源),thinkphp支持自定义字段信息。

代码语言:javascript
复制
$schema = [
    '[字段名]'	=> 	'[字段类型]';
]

schema需要定义整个数据表字段,对单个字段定义需要自动转换的类型可以使用type

如果需要废弃(忽略)数据表中的字段,可以使用$disuse来定义

代码语言:javascript
复制
protected $disuse = [‘type’,'status'];

获取数据

代码语言:javascript
复制
$user = User::find(1);
echo $user->create_time;  
echo $user->name;

由于模型类实现了ArrayAccess接口,所以可以当成数组使用。

代码语言:javascript
复制
$user = User::find(1);
echo $user['create_time'];  
echo $user['name'];

如果你是在模型内部获取数据的话,需要改成:

代码语言:javascript
复制
$user = $this->find(1);
echo $user->getAttr('create_time');  
echo $user->getAttr('name');

模型赋值、 实例化

给模型对象赋值

代码语言:javascript
复制
$user = new User() ; 
$user->table = 'tp_user';
$user->score = 100 ; 

上述代码会将模型对象打包赋值给$user,可以对其封装的模型对象进行操作“修改器”

同时也可以使用

代码语言:javascript
复制
$data['name'] = 'thinkphp';
$data['score'] = 100;
$user = new User($data);

或者使用

代码语言:javascript
复制
$user = new User();
$data['name'] = 'thinkphp';
$data['score'] = 100;
$user->data($data);

data方法支持使用修改器

代码语言:javascript
复制
$user = new User();
$data['name'] = 'thinkphp';
$data['score'] = 100;
$user->data($data, true);

如果需要对数据进行过滤,可以使用

代码语言:javascript
复制
$user = new User();
$data['name'] = 'thinkphp';
$data['score'] = 100;
$user->data($data, true, ['name','score']);

表示只设置data数组的namescore数据。


最佳操作实践
新增操作:

新增数据的最佳实践原则:使用create静态方法新增数据,使用saveAll批量新增数。

删除操作:

删除的最佳实践原则是:如果删除当前模型数据,用delete方法,如果需要直接删除数,使用destroy静态方法。

更新操作:

更新的最佳实践原则是:如果需要使用模型事件,那么就先查询后更新,如果不需要使用事件或者不查询直接更新,直接使用静态的Update方法进行条件更新,如非必要,尽量不要使用批量更新。


新增-save()

模型数据的新增和数据库的新增数据有所区别,数据库的新增只是单纯的写入给定的数据,而模型的数据写入会包含修改器、自动完成以及模型事件等环节,数据库的数据写入参考数据库章节。

第一步:实例化模型对象

代码语言:javascript
复制
$user = new UserModel();
$user = new \app\model\UserModel();

第二步:新增数据

代码语言:javascript
复制
public function index()
{
    $user = new model\UserModel();
    $user->username     =   '诸葛大力';
    $user->password     =   'gouzaizai';
    $user->gender       =   '女';
    $user->email        =   'chengguo@gmail.com';
    $user->price        =    100 ;
    $user->uid          =    0000;
    $user->details      =    '000';
    
    $user->save();
}

上述的只是一个普通方法,除此还有传递数据数组的方式

代码语言:javascript
复制
public function index()
{
    $user = new model\UserModel();
    $data = [
        'username'     =>   '诸葛大力',
        'password'     =>   'gouzaizai',
        'gender'       =>   '女',
        'email'        =>   'chengguo@gmail.com',
        'price'        =>    100 ,
        'uid'          =>    0000,
        'details'      =>    '000',
    ];
    $user->save($data);
}
allowField()限制写入允许字段
代码语言:javascript
复制
$user
    // 限制只允许写入‘username、password’字段
    ->allowField(['username','password'])
    ->save($data);
replace()新增

实现REPLACE into新增(修改新增)

代码语言:javascript
复制
$user->replace()->save($data);

如果$data数据表已存在会更新;

代码语言:javascript
复制
REPLACE INTO `tp_user` SET `id` = 301 , `username` = '诸葛大力' , `password` = 'gouzaizai' , `gender` = '女' , `email` = 'chengguo@gmail.com' , `price` = 100 , `uid` = 0 , `details` = '000' , `create_time` = '2020-04-10 11:40:20.660840' , `update_time` = '2020-04-10 11:40:20.660840'
获取自增ID
代码语言:javascript
复制
$user->id;
$user->uid;
……
批量新增数据saveAll() *

可以批量添数据,返回新增数组

代码语言:javascript
复制
$user = new User;
$list = [
   ['name'=>'thinkphp','email'=>'thinkphp@qq.com'],
   ['name'=>'onethink','email'=>'onethink@qq.com']
];
$user->saveAll($list);

saveAll方法新增数据返回的是包含新增模型(带自增ID)的数据集对象。

saveAll方法新增数据默认会自动识别数据是需要新增还是更新操作,当数据中存在主键的时候认为是更新操作。

静态方法 create() *
代码语言:javascript
复制
$user = UserModel::create([
    'username' => '李白',
    'password' => '123',
    'gender' => '男',
    'email' => 'libai@163.com',
    'price' => 100,
    'details' => '123',
    'uid' => 1011
], ['username', 'password', 'details'], false);

参数 1 是新增数据数组,必选 参数 2 是允许写入的字段,可选 参数 3 为是否 replace 写入,默认 false 为 Insert 写入

新增数据的最佳实践原则:使用create方法新增数据,使用saveAll批量新增数据。


删除-delete()
删除当前模型
代码语言:javascript
复制
$user = new User();
$user->delete();
根据主键删除
代码语言:javascript
复制
User::destroy(1);
User::destroy([1,2,3]);
条件删除
代码语言:javascript
复制
User::where('id',10)->delete();
代码语言:javascript
复制
User::destroy(function($query){
    $query->where('id',10);
});

删除的最佳实践原则是:如果删除当前模型数据,用delete方法,如果需要直接删除数据,使用destroy静态方法。


更新

使用find()方法获取数据,通过save()方法提交修改

代码语言:javascript
复制
$user = new model\UserModel();
$data = [
    'username'  =>  '诸葛大力',
    'gender'    =>  '女'
];
$user->find(305)->save($data);

使用where()方法结合find()方法设置查询条件并获取数据

代码语言:javascript
复制
$user = new model\UserModel();
$data = [
    'username'  =>  '齐天大圣'
];
$user->where('username','孙悟空')
    ->find()->save($data);    

save()只会更新数据有区别的地方;强制使用save()更新数据需要使用force()

Db::raw()执行SQL函数方法同样可以实现更新

代码语言:javascript
复制
$user = model\UserModel::find(305);
$user->price = Db::raw('price+10');
$user->save();

使用allowField()方法,允许要更新的字段

代码语言:javascript
复制
$user = model\UserModel::find(305);
$data = [
    'username' => '李白',
    'password' => '123',
    'gender' => '男',
    'email' => 'libai@163.com',
    'price' => 100,
    'details' => '123',
    'uid' => 1011
];
$user->allowField(['username'])->save($data);

使用saveAll()方法可以批量修改数据,返回被修改数据集;批量saveAll()更新只可以通过主键来进行

静态方法::update()更新

代码语言:javascript
复制
$data = [    'username=> '李白',
    'password' => '123',
    'gender' => '男',
    'email' => 'libai@163.com',
    'price' => 100,
    'details' => '123',
    'uid' => 1011
];
model\UserModel::update($data,['id'=> 304],['username']);

参数 1 是数据数组,必选 参数 2 更新条件,可选 参数 3 是允许写入的字段,可选


save()

模型的新增、更新都需要save()进行执行,具有自动识别; 实例化模型后调用save()表示新增,查询数据后调用save()表示修改

更新的最佳实践原则是:如果需要使用模型事件,那么就先查询后更新,如果不需要使用事件或者不查询直接更新,直接使用静态的Update方法进行条件更新,如非必要,尽量不要使用批量更新。

查询
模型普通查询
  • 使用find()通过主键查询想要的数据(可以在模型类中设置主键字段) 调用find()方法是,如果数据不存在返回Null,使用findOrEmpty()方法,数据不存返回空模型 使用isEmpty()方法判断是否为空模型
  • 使用where()进行条件筛选查询
  • 使用select()方法,查询多条指定主键的字段,不指定就是全部字段 $user = model\UserModel::select([304,305]); foreach ($user as $key=>$value) { echo $value->username; }
  • 支持查询构造器的各种方法

模型动态查询

getBy[字段名]:可以获取对应字段的value(驼峰命名,字段首字母大写)

代码语言:javascript
复制
$user = model\UserModel::getByUsername('李白');

模型聚合查询

支持max min sum count avg 等方法

代码语言:javascript
复制
UserModel::max('price');

模型分批查询

支持使用chunk()进行分批处理数据

代码语言:javascript
复制
User::chunk(100, function ($users) {
    foreach($users as $user){
        // 处理user模型对象
    }
});

模型游标查询

支持使用cursor()方法进行游标查询,返回生成器对象

代码语言:javascript
复制
foreach(User::where('status', 1)->cursor() as $user){
	echo $user->name;
}

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


模型查询的最佳实践原则是:在模型外部使用静态方法进行查询,内部使用动态方法查询,包括使用数据库的查询构造器。


模型获取器

获取器的作用是对模型实例的数据做出自动处理

每一个获取器对应模型的一个特殊方法,方法要求public

获取器命名规范

get[FieldName]Attr(),FieldName为数据表字段的驼峰转换,定义了获取器自动触发

  • 模型的数据对象取值操作 $model->field_name
  • 模型的序列化输出操作$model->toArray()
  • 显式调用getAttr方法 $this->getAttr('field_name')
获取器常见场景以及基本使用
  • 时间日期字段的格式化输出
  • 集合或枚举类型的输出
  • 数字状态字段的输出
  • 组合字段的输出
代码语言:javascript
复制
// 状态值的转换输出

// 模型类
public function getStatusAttr($value)
{
    $status = [-1=>'删除' , 0=>'禁用' , 1=>'正常' , 2=>"待审核"];
    return $status[$value];
}

// 控制端
$user = model\UserModel::find(19);
return $user->status;

通俗理解: 在控制端正常的查看字段值,模型类定义一个获取器(一个字段可以对应一个模型类中的特殊方法获取器方法)),获取器就会对控制端的字段查询进行获取并进行自定义的处理方法。

获取器还可以定义数据表不存在的字段,在控制端用户可以正常的按照字段名读取的方式来访问

代码语言:javascript
复制
<?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']];
    }
}

获取器方法的第二个参数传入的是当前的所有数据数组。

我们就可以直接使用status_text字段的值了,例如:

代码语言:javascript
复制
$user = User::find(1);
echo $user->status_text; // 例如输出“正常”

这里也就是为了解决多种处理方法并规避冲突的写法;因为如果获取器定义以后就无法在控制端获原始的字段值,不过也还有另外一种getData()方法获得原始字段值:

代码语言:javascript
复制
$user = User::find(1);
// 通过获取器获取字段
echo $user->status;
// 获取原始字段数据
echo $user->getData('status');
// 获取全部原始数据
dump($user->getData());
动态获取器

可以支持对模型使用动态获取器,无需在模型类中定义获取器方法,在控制端使用动态获取器:

代码语言:javascript
复制
$user = model\UserModel::find(19)
      ->withAttr('status',function($value,$data){
            $status = [
                -1=>'删除',0=>'禁用',
                1=>'正常',2=>'待审核'
            ];
            return $status[$value];
        });
echo $user->status;

withAttr方法支持多次调用,定义多个字段的获取器。另外注意,withAttr方法之后不能再使用模型的查询方法,必须使用Db类的查询方法。


模型修改器

模型修改器的左右:对模型设置对象的值进行处理 在新增数据的时候,可以利用修改器对数据进行格式化、转换等处理;处理数据新增,还有数据更新也可能触发修改器

模型修改器命名规范:

setFieldNameAttr()

ps:修改器只对模型方法有效

代码语言:javascript
复制
public function setEmailAttr($value)
{
    return strtolower($value);
} 
代码语言:javascript
复制
$user = model\UserModel::create([
    'username' => '李白',
    'password' => '123',
    'gender' => '男',
    'email' => 'LIBai@163.com',
    'price' => 100,
    'details' => '123',
    'uid' => 1011
], ['username', 'password', 'details','email'], false);

echo $user->save();

查询范围

在模型类创建一个封装的查询和写入方法,有便于控制端调用

查询封装方法 scope()

前缀scope ,后缀自定义,在调用时吧后缀当做参数即可。

代码语言:javascript
复制
public function scopeMale($query)
{
    $query->where('gender','男')
          ->field('id,username,gender,email')
          ->limit(5);
}
代码语言:javascript
复制
$user = model\UserModel::scope('male')->select();
return "<hr>".Db::getLastSql()."<hr>".$user;
代码语言:javascript
复制
public function scopeEmail($query, $value)
{
    $query->where('email','like','%'.$value.'%');
}
代码语言:javascript
复制
$user = UserModel::scope('email','xiao')->select();
return "<hr>".Db::getLastSql()."<hr>".$user;

scope() 的第一个参数是调用的封装方法,第二个参数是封装方法可以接收的数据

支持多个查询封装方法连缀调用

代码语言:javascript
复制
$user = UserModel::scope('email','xiao')
    ->scope('price',80)
    ->select();
return "<hr>".Db::getLastSql()."<hr>".$user;

在使用查找范围scope()后,指定使用find() select()查询; 在模型类中的查询封装方法中可以使用包括修改器、获取器等在内的模型操作方法。 public function getGenderAttr($value,$data) { $gender = ['男'=>'纯爷们','女'=>'小姑娘']; return $gender[$value]; } public function scopeMale($query) { $query->where('gender','男') ->field('id,username,gender,email') ->limit(5); }

全局查找范围

支持在模型中设置globaScope属性,定义全局查找范围

代码语言:javascript
复制
class User extends Model
{
    // 定义全局的查询范围
    protected $globalScope = ['status'];

    public function scopeStatus($query)
    {
        $query->where('status',1);
    }
}

然后,执行下面的代码:

代码语言:javascript
复制
$user = User::find(1);

最终的查询条件会是

代码语言:javascript
复制
status = 1 AND id = 1

如果需要动态关闭所有的全局查询范围,可以使用:

代码语言:javascript
复制
// 关闭全局查询范围
User::withoutGlobalScope()->select();

可以使用withoutGlobalScope方法动态关闭部分全局查询范围。

代码语言:javascript
复制
User::withoutGlobalScope(['status'])->select();

模型搜索器

搜索器用于封装字段或搜索标识的表达式,类似查找范围

一个搜索器对应模型的一个特殊方法

命名规范

search[FieldName]Attr()

User模型定义name字段和时间字段的搜索器,可以使用:

代码语言:javascript
复制
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]);
    }    
}

然后,我们可以使用下面的查询

代码语言:javascript
复制
User::withSearch(['name','create_time'], [
    'name'			=>	'think',
    'create_time'	=>	['2018-8-1','2018-8-5'],
    'status'		=>	1
    ])->select();

最终生成的SQL语句类似于

代码语言:javascript
复制
SELECT * FROM `think_user` WHERE  `name` LIKE 'think%' AND create_time` BETWEEN '2018-08-01 00:00:00' AND '2018-08-05 00:00:00' 

可以看到查询条件中并没有status字段的数据,因此可以很好的避免表单的非法查询条件传入,在这个示例中仅能使用namecreate_time条件进行查询。

事实上,除了在搜索器中使用查询表达式外,还可以使用其它的任何查询构造器以及链式操作。

例如,你需要通过表单定义的排序字段进行搜索结果的排序,可以使用

代码语言:javascript
复制
class User extends Model 
{
    public function searchNameAttr($query, $value, $data)
    {
        $query->where('name','like', $value . '%');
        if (isset($data['sort'])) {
        	$query->order($data['sort']);
        }        
    }
    public function searchCreateTimeAttr($query, $value, $data)
    {
        $query->whereBetweenTime('create_time', $value[0], $value[1]);
    }      
}

然后,我们可以使用下面的查询

代码语言:javascript
复制
User::withSearch(['name','create_time', 'status'], [
		'name'			=>	'think',
    	'create_time'	=>	['2018-8-1','2018-8-5'],
        'status'		=>	1,
        'sort'			=>	['status'=>'desc'],
    ])
	->select();

最终查询的SQL可能是

代码语言:javascript
复制
SELECT * FROM `think_user` WHERE  `name` LIKE 'think%' AND `create_time` BETWEEN '2018-08-01 00:00:00' AND '2018-08-05 00:00:00' ORDER BY `status` DESC

你可以给搜索器定义字段别名,例如:

代码语言:javascript
复制
User::withSearch(['name'=>'nickname','create_time', 'status'], [
		'nickname'		=>	'think',
    	'create_time'	=>	['2018-8-1','2018-8-5'],
        'status'		=>	1,
        'sort'			=>	['status'=>'desc'],
    ])
	->select();

搜索器通常会和查询范围进行比较,搜索器无论定义了多少,只需要一次调用,查询范围如果需要组合查询的时候就需要多次调用


模型数据集

数据集直接继承collection类,和数据的数据集方式一样、操作一样。

参照官方技术文档


模型自动时间戳

系统支持自动写入创建和更新的时间戳字段(默认会关闭),具体配置方法:

  • 全局开启:在database.php文件中修改auto_timestamp为truely
  • 在模型类中单独开启自动时间戳:$autoWriteTimestamp class User extends Model { protected $autoWriteTimestamp = true; } 也或者单独关闭自动时间戳 class User extends Model { protected $autoWriteTimestamp = false; } 配置开启后会自动添加自动时间戳写入create_timeupdate_time两个 端,默认的类型是int,如果是时间类型,可以设置如下
代码语言:javascript
复制
protected $autoWriteTimestamp = [自动时间戳字段]

同时也可以自定义两个自动时间戳:

代码语言:javascript
复制
protected $createTime = '[字段]';
protected $updateTime = '[字段]';
只读字段

在模型类中定义readonly属性:静态

代码语言:javascript
复制
protected $readonly = ['字段1','字段2',……]

动态设置制度字段:

代码语言:javascript
复制
$user->readonly(['字段1',……])->save();
系统转换

系统可以通过模型端设置写入或读取时对字段类型进行转换type

php rotected $type = [ 'price' => 'integer', 'status' => 'boolean', ; 设置废弃字段 $disuse

JSON字段 *
数据库JSON

数据库写入JSON字段,直接通过数组方式即可完成:

代码语言:javascript
复制
data = [
'username' 	=> '李白',
   'password' 	=> '123',
   'gender'	=> '男',
   'email' 	=> 'LIBai@163.com',
   'price'		=> 100,
   'details' 	=> '123',
   'list'		=> ['username'=>李白,'gender'=>'女']
];
Db::table('tp_user')
->json(['list']) // 定义list字段为json类型字段
->insert($data)

查询数据,需要转换JSON数据格式:

代码语言:javascript
复制
Db::table('tp_user')->json(['list'])->find(288);

将JSON字段里的数据作为查询条件

代码语言:javascript
复制
$user = Db::table('tp_user')->json(['list'])->where('list-username','李白')->find();

修改JSON字段数据

代码语言:javascript
复制
$data['list'] = ['username'=>'李白','gender'=>'男'];
Db::table('tp_user')->json(['list'])->where('id',220)->update($data);
模型JSON

设置写入JSON字段的字符字段:

代码语言:javascript
复制
protetced $json = ['list'];

使用模型方法新增JSON数据的字段

代码语言:javascript
复制
$user = new User;
$user->name = 'thinkphp';
$user->info = [
	'email'    => 'thinkphp@qq.com',
    'nickname '=> '流年',
];
$user->save();

具体的请参考官方文档JSON部分


模型软删除

在实际项目中,对数据频繁使用删除操作会导致性能问题,软删除的作用就是把数据加上删除标,而不是真正的删除,同时也便于需要的时候进行数据的恢复。

要使用软删除功能,需要引入SoftDeletetrait,例如User模型按照下面的定义就可以使用软删除功能:

代码语言:javascript
复制
use SoftDelete ; 
rotected $deleteTime = 'delete_time';

delete_time默认为Null,类型为时间戳类型,用于记录数据的删除时间。

确定开启软删除和字段后

代码语言:javascript
复制
// 软删除
User::destroy(1);
// 真实删除
User::destroy(1,true);

$user = User::find(1);
// 软删除
$user->delete();
// 真实删除
$user->force()->delete();

软删除后系统会忽略该条数据,模型会自动屏蔽数据

  • 使用withTrashed()取消屏蔽软删除的数据
  • 使用onlyTrashend()可以查询被软删除的数据
  • 使用restore()可以恢复被软删除的数据
  • 如果需要强制删除,需要使用force()强制安排

模型和数据库的事件
数据库事件

执行增删改查操作的时候,可以同时出发一些事件来执行额外的操作;额外的操作事件可以部署在构造方法中等待激活后执行;数据库事件方法是Db::event('事件名','执行函数')

事件名

描述

before_select

select查询前回调

before_find

find查询前回调

after_insert

insert操作后回调

after_update

update操作后回调

after_delete

delete操作后回调

一般情况下,数据库事件卸载控制器端的初始化方法里,有利于统一管理

代码语言:javascript
复制
public function initialize()
{
	Db::event('before_select', function($query){
		echo "执行了批量的查询操作"
	});
}
模型事件

事件 描述 事件方法名 after_read 查询后 onAfterRead before_insert 新增前 onBeforeInsert after_insert 新增后 onAfterInsert before_update 更新前 onBeforeUpdate after_update 更新后 onAfterUpdate before_write 写入前 onBeforeWrite after_write 写入后 onAfterWrite before_delete 删除前 onBeforeDelete after_delete 删除后 onAfterDelete before_restore 恢复前 onBeforeRestore after_restore 恢复后 onAfterRestore

在模型类中使用静态方法调用即可完`成事件触发


关联模型

关联模型:将数据表与表之间进行关联和对象化;

关联方式

关联方式

描述

hasOne

一对一关联

belongsTo

一对一关联-反向

hasMany

一对多关联

hasOneThrough

远程一对一

hasManyThrough

远程一对多

belongsToMany

多对多关联

morphMany

多态一对多

morphOne

多态一对一

morphTo

多态关联

实例:

主表:tp_user 主键:id

附属表:tp_profile 字段:user_id hobby 外键user_id

主表的主键与附属表的外键进行关联

一对一关联 hasOne
  • 关联定义: hasOne('关联模型类名','外键','主键') 关联模型: 外键:默认的外键规则是当前的模型名+_id 主键:当前模型的主键,自动获取也可以指定 class UserModel extends Model { protected $table = 'tp_user'; public function profile() { return $this->hasOne(ProfileModel::class , 'user_id' , 'id'); } } class ProfileModel extends Model { protected $table = 'tp_profile'; } $user = model\UserModel::find(19); //return json($user->profile); return $user->profile->hobby; 使用save()设置关联修改,通过主表修改附表字段的值 $user = UserModel::find(19); $user->profile->save(['hobby'=>'美女']); ->profile()方法新增数据 $user->profile()->save(['hobby'=>'靓女']);

  • 相对关联(反向) belongsTo('关联模型','外键','关联主键') belonsTo模式适合于附属表关联主表 class ProfileModel extends Model { protected $table = 'tp_profile'; public function user() { return $this->belongsTo(UserModel::class , 'user_id' , id'); } } $profile = ProfileModel::find(1); return $profile->user; 使用hasOne()模拟belongsTo() $user = UserModel::hasWhere('profile',['id'=>19])->find(); // 这里的profile是user模型勒种的方法而不是profile模型类 $user = UserModel::hasWhere('profile',function($query){ $query->where('profile.id',19); })-select();

一对多关联-hasMany

hasMany模式适合主表关联附表,实现一对多查询;与一对一查询的主要区别就是,hasMany可以实现查询返回多条。

代码语言:javascript
复制
hasMany('关联模型',['外键','主键']);

使用->profile()方法模式,可以对数据进行筛选

代码语言:javascript
复制
$user->profile()->where('id','>',19)->select()

调用属性方式会直接返回结果,调用方法方式可以进行中间处理

使用has()方法查询关联附表的主表内容

代码语言:javascript
复制
$user = UserModel::has('profile','>=',2)->select();
return $user;

这里的查询是在附表中判断数据与主表的关联内容 上述代码的主要功能:在附表中查找与主表有两次以上关联的数据,例如id=19在附表中两两条关联数据

使用haswhere查询关联附表的处理内容(反向关联)

代码语言:javascript
复制
$user = UserModel::hasWhere('profile',['status'=>1])->select();
return $user;
代码语言:javascript
复制
$user = UserModel::hasWhere('profile',function($query){
    $query->where('status','>=',1);
})->select();

使用save()/saveAll() 新增数据

代码语言:javascript
复制
$user = UserModel::find(20);
// 新增单条数据
$user->profile()->save(['hobby'=>'计算机','status'=>'1']);
// 新增批量数据
$user->profile()->saveAll([
    ['hobby'=>'计算机','status'=>'1'],
    ['hobby'=>'游戏机','status'=>'1']
]);

使用together()删除主表内容时,附表关联的内容全部删除

代码语言:javascript
复制
$user = UserModel::with('profile')->find(20);
$user->together(['profile'])->delete();

关联预载入 with

普通的关联查询,会循环执行多次SQL查询;

代码语言:javascript
复制
$list = UserModel::select([19,20,21]);
foreach($list as $user)
{
    dump($user->profile);
}

采用关联预载入的方式 ,可以减少多次查询的耗时;

代码语言:javascript
复制
with(['关联数据表1','关联数据表2']);

延迟预载入:先执行select()再执行load()

关联统计

使用withCount()可以统计主表关联附表的个数,使用profile_count

代码语言:javascript
复制
$list = UserModel::withCount(['profile'])->select([19,20,21]);
foreacth($list as $user)
{
	echo $user->profile_count;
}

关联统计的输出采用"[关联方法名]_count"的结构

同时支持withMax``withMin``withSum``withAvg……

关联输出

使用hidden()方法,隐藏主表字段或附表字段

代码语言:javascript
复制
$list = Usermodel::with('profile')->select();
return json($list->hidden(['profile.status']));

使用visible()方法,只显示指定字段

使用append()方法,添加额外的字段

多对多关联

三张表: access表包含了user和role表的关联ID

代码语言:javascript
复制
belongsToMany('关联模型','中间表',['外键','关联键'])

关联模型:模型名或类名 中间表:{需要继承Pivot} 外键: 关联键:中间表的当前模型关联键名

参考官方文档


本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ThinkPHP模型
    • 模型定义
      • 设置模型
        • 模型字段
          • 最佳操作实践
            • 新增操作:
            • 删除操作:
            • 更新操作:
          • 新增-save()
            • allowField()限制写入允许字段
            • replace()新增
            • 获取自增ID
            • 批量新增数据saveAll() *
            • 静态方法 create() *
          • 删除-delete()
            • 删除当前模型
            • 根据主键删除
            • 条件删除
          • 更新
            • save()
          • 查询
            • 模型普通查询
            • 模型动态查询
            • 模型聚合查询
            • 模型分批查询
            • 模型游标查询
          • 模型获取器
            • 获取器命名规范
            • 获取器常见场景以及基本使用
            • 动态获取器
          • 模型修改器
            • 模型修改器命名规范:
          • 查询范围
            • 查询封装方法 scope()
            • 全局查找范围
          • 模型搜索器
            • 命名规范
          • 模型数据集
            • 模型自动时间戳
              • 只读字段
                • 系统转换
                  • JSON字段 *
                    • 数据库JSON
                    • 模型JSON
                  • 模型软删除
                    • 模型和数据库的事件
                      • 数据库事件
                      • 模型事件
                    • 关联模型
                      • 关联方式
                      • 一对一关联 hasOne
                      • 一对多关联-hasMany
                      • 关联预载入 with
                      • 关联统计
                      • 关联输出
                      • 多对多关联
                  相关产品与服务
                  数据库
                  云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档