Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION] Hyperf 框架中使用分表时关联关系查询报错的问题 #6707

Open
jackchang1025 opened this issue Apr 20, 2024 · 3 comments
Labels
question Further information is requested

Comments

@jackchang1025
Copy link

Hyperf 框架中使用分表时关联关系查询报错的问题

求助问题已经发布到社区了,有什么好的解决办法嘛?
https://learnku.com/laravel/t/86535

@jackchang1025 jackchang1025 added the question Further information is requested label Apr 20, 2024
@lazychanger
Copy link
Contributor

你的操作有问题啊。一对多模型在获取“多”的时候是通过原数据数据plunk获取关联ID的,在你的log时候,API模型实际上是空的,不是你的数据模型。

你应该自己plunk所有的api的createdAt并且在format与unique后,使用unionALL进行聚合查询,再重新组合数据。

模型关系处理不了复杂的分表聚合问题。

@jackchang1025
Copy link
Author

jackchang1025 commented Apr 29, 2024

 // 获取分表模型
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($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()
        );
    }
}
$api = Api::with(['log'])->find(10882);

你说的没错,在使用 with 加载关联关系的时候,Api 此时模型数据是空的,就如同我之前测试一样,

$api = Api::find(10882);
var_dump($api->created_at->toDateString());
$api->load('log');
//输出 
string(10) "2024-03-26"
Call to undefined relationship [Log] on model [App\Model\Api].

在使用 load 加载模型的时候,首先查询数据并且能够打印出 模型中 created_at 字段不为空,然后使用 load 加载关联模型,在定义的模型关联方法中获取 created_at 字段却为空

这里使用是正常的,
$api = Api::find(10882);
var_dump($api->log);

主要问题是使用with 和 load 模型预加载时候才会获取 created_at 字段为空,导致报错,

主要是模型的表名和关联的字段名称设置好了,一样可以使用聚合查询

@lazychanger
Copy link
Contributor

其实你可以打印SQL语句查询一下,with与load生成的SQL是什么样子的。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants