Hyperf 框架中使用分表时关联关系查询报错的问题
1. 运行环境
1). 当前使用的 hyperf Eloquent 版本?
composer show hyperf/database
name : hyperf/database
descrip. : A flexible database library.
keywords : database, hyperf, php
versions : * v3.1.13
released : 2024-03-13, 1 month ago
type : library
license : MIT License (MIT) (OSI approved) https://spdx.org/licenses/MIT.html#licenseText
homepage : https://hyperf.io
source : [git] https://github.com/hyperf/database.git 01c42204188d2f2a14f331554129f989a1f8e2b1
dist : [zip] https://api.github.com/repos/hyperf/database/zipball/01c42204188d2f2a14f331554129f989a1f8e2b1 01c42204188d2f2a14f331554129f989a1f8e2b1
path : /opt/www/vendor/hyperf/database
names : hyperf/database
2. 问题描述?
分表逻辑:
我在 Hyperf 框架中实现了一个按周分表的功能,将数据按照时间分散存储到不同的表中,分表逻辑是按照时间维度,每周一个分表,表名格式为 “原表名_年份周数” 的形式。例如,原表名为 “log”,那么第一周的分表名为 “log_202401”,第二周的分表名为 “log_202402”,以此类推
分表代码
// 获取分表模型
trait ShardingTrait
{
abstract public function setTable($tableName);
abstract public function tableName():string;
public function getTableName(?Carbon $date = null): string
{
$date = $date ? $date->toDateString() : date('Y-m-d');
$year = date('Y', strtotime($date));
$week = date('W', strtotime($date));
$str_pad = str_pad($week, 2, '0', STR_PAD_LEFT);
//使用 sprintf 拼接表名
return sprintf('%s_%s%s', $this->tableName(), $year, $str_pad);
}
public function getShardingModel(?Carbon $date = null, bool $isCreate = false): Model
{
$tableName = $this->getTableName($date);
if ($isCreate){
// CreateLogTableJob::class 是判断表是否存在否则创建分表任务
$job = make(CreateLogTableJob::class);
$job->execute($tableName);
}
//设置模型名称
return $this->setTable($tableName);
}
}
class Log extends MineModel
{
use ShardingTrait;
public function getTable(): string
{
return $this->table ?? $this->getTableName();
}
//设置分表名称
public function tableName():string
{
return 'log';
}
}
定义模型关联关系
class Api extends MineModel
{
/**
* The table associated with the model.
*/
protected ?string $table = 'system_api';
/**
* 定义 Log 关联,因为 Log 模型使用时间分表,所以通过 created_at 来获取分表表名
* Log 定义的关联关系不支持模型预加载,因为此时 $this->created_at 为 null 无法获取分表表名
* @return hasMany
*/
public function log(): hasMany
{
var_dump(3);
var_dump($this->created_at?->toDateString());
if (!$this->created_at){
throw RelationNotFoundException::make($this->getModel(), 'Log');
}
$instance = (new Log)->getShardingModel($this->created_at);
return $this->newHasMany(
$instance->newQuery(),
$this,
"{$instance->getTable()}.api_id",
$this->getKeyName()
);
}
}
4. 实际得到的结果?
使用 with 测试模型预加载
var_dump(1);
$api = Api::with(['log'])->find(10882);
var_dump(2);
var_dump($api->created_at->toDateString());
var_dump($api->log->toArray());
//输出
int(1)
int(3)
NULL
Call to undefined relationship [Log] on model [App\Model\Api].
使用 load 测试模型预加载
var_dump(1);
$api = Api::find(10882);
var_dump(2);
var_dump($api->created_at->toDateString());
$api->load('log');
var_dump($api->log->toArray());
//输出
int(1)
int(2)
string(10) "2024-03-26"
int(3)
NULL
Call to undefined relationship [Log] on model [App\Model\Api].
在 log 方法中打印 Api 模型
class Api extends MineModel {
....
public function log(){
var_dump($this);
//之前的代码不变
....
}
}
//输出
object(App\Model\Api)#3509 (33) {
["incrementing"]=>
bool(true)
["exists"]=>
bool(false)
["wasRecentlyCreated"]=>
bool(false)
["guarded":protected]=>
array(1) {
[0]=>
string(1) "*"
}
["connection":protected]=>
string(7) "default"
["table":protected]=>
string(21) "Api"
["primaryKey":protected]=>
string(2) "id"
["keyType":protected]=>
string(3) "int"
["with":protected]=>
array(0) {
}
["withCount":protected]=>
array(0) {
}
["perPage":protected]=>
int(15)
["traitInitializers":protected]=>
array(0) {
}
["attributes":protected]=>
array(0) {
}
["original":protected]=>
array(0) {
}
["changes":protected]=>
array(0) {
}
["casts":protected]=>
array(5) {
....
}
["classCastCache":protected]=>
array(0) {
}
["dates":protected]=>
array(0) {
}
["dateFormat":protected]=>
NULL
["appends":protected]=>
array(0) {
}
["events":protected]=>
array(0) {
}
["relations":protected]=>
array(0) {
}
["touches":protected]=>
array(0) {
}
["timestamps"]=>
bool(true)
["hidden":protected]=>
array(1) {
[0]=>
string(10) "deleted_at"
}
["repository":protected]=>
NULL
["dataScopeField":protected]=>
string(10) "created_by"
["useCacheBuilder":protected]=>
bool(false)
}
从打印出的 Api 模型信息可以看出,在 log 方法中访问 $this->created_at 时,模型的 attributes 属性是一个空数组,因此无法获取到 created_at 的值。
在使用 load 方法时候,此时主模型的属性值已经获取,但是在 log 方法中访问 $this->created_at 时,它的值为 null。
「麻烦看下我这还差点什么?
]