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。

「麻烦看下我这还差点什么?:pray: :pray:]

讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!