【问题标题】:How to access to sub related model when the related model returns a collection相关模型返回集合时如何访问子相关模型
【发布时间】:2020-11-14 14:31:54
【问题描述】:

我有 3 个模型:用户、付款和日志。一个用户有很多付款,用户和付款都有很多日志。

用户模型

class User
{

    public function payments()
    {
        return $this->hasMany('Payment', 'user_id');
    }

    public function logs()
    {
        return $this->morphMany(Log::class, 'loggable');
    }

}

用户表

CREATE TABLE `users` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
    `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
    `email_verified_at` timestamp NULL DEFAULT NULL,
    `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
    `remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
    `created_at` timestamp NULL DEFAULT NULL,
    `updated_at` timestamp NULL DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

支付模式

class Payment
{

    public function user()
    {
        return $this->belongsTo('User', 'user_id');
    }

    public function logs()
    {
        return $this->morphMany(Log::class, 'loggable');
    }

}

付款表

CREATE TABLE `payments` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `status` varchar(50),
    `amount` int(11) NOT NULL,
    `collection_date` date NOT NULL,
    `created_at` timestamp NULL DEFAULT NULL,
    `updated_at` timestamp NULL DEFAULT NULL,
    `user_id` bigint(20) unsigned NOT NULL,
    PRIMARY KEY (`id`),
    CONSTRAINT `fk_payments_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

日志模型

class Log
{
    public function loggable()
    {
        return $this->morphTo();
    }

}

日志表

CREATE TABLE `logs` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `loggable_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
    `loggable_id` bigint(20) unsigned NOT NULL,
    `old_values` text COLLATE utf8mb4_unicode_ci,
    `new_values` text COLLATE utf8mb4_unicode_ci,
    `user_id` bigint(20) unsigned DEFAULT NULL, /* the user that made the change, if any */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Log 模型存储对任何其他模型所做的所有更改(这是一种多态关系),因此如果用户更改其名称,Log 模型将存储旧名称和新名称。这同样适用于支付:如果支付状态发生变化,Log 模型将有一条新记录,其中包含旧状态和新状态。

我需要显示按日期排序的特定用户的所有日志记录的分页列表。所以我的代码是:

$user = App\User::find($id);
$allLogs = $user->logs();

// Now I need to join (I'm using union) both sets of logs
$allLogs->union($user->payments->logs());

但是,由于一个用户可以有多个付款,$user->payments 返回一个集合,因此不再是一个查询构建器/雄辩对象,当我尝试调用 ->logs() 时它失败了。

$user->payments()->logs() 也不起作用,因为$user->payments() 返回一个HasMany 对象,而->logs() 方法不存在。

我试图避免单独获取Log 的每个集合,然后使用 php 处理它们(最好将该任务委托给 MySql)。

我相信可以做到,因为我可以在MySql上写查询:

select l.*
from payments p
join logs l on p.id = l.loggable_id and l.loggable_type = 'App\\Payments'
where p.user_id = SOMEUSERID

提前致谢

【问题讨论】:

  • 您能否添加这些表的结构,以便我们可以看到键以及关系是如何设置的
  • @lagbox,感谢您的回复。我刚刚添加了 sql 代码和模型

标签: php laravel eloquent


【解决方案1】:

渴望加载关系(减少查询次数)

$user = User::with(['payments.logs', 'logs'])->find($id);

使用 Log 模型进行查询。

$logs = Log::where([
       'loggable_id' =>  $user->id,
       'loggable_type' => 'User',
     ])
     ->orWhere(function($query){
           $query->whereIn('loggable_id', 
                     $user->payments()->pluck('id'))
                 ->where('loggable_type', 'Payment');
     })->get();

单独获取它们,然后将它们组合起来。

$all_logs = collect([]);

$all_logs->push($user->logs);

foreach($user->payments as $p){
    $all_logs->push($p->logs);
}


$final_logs = $all_logs->collapse();

只需使用关系,无需迭代付款。您可以根据需要组合结果(如前面的方法所示)。

$user_logs = $user->logs;

$payment_logs = $user->payments->pluck('logs')->collapse();

【讨论】:

  • 感谢您的回复@Aashish gaba。有没有办法使用关系来完成同样的事情?谢谢
  • 是的,但是您必须遍历付款并从每次付款中获取日志。
  • 我已经更新了答案,现在它包含两种方式:)
  • 但在第二种方式中,您将获取所有记录,然后使用 php 将它们组合起来。有没有办法通过 SQL 获取付款日志? (我想避免在 php 中处理日志)
  • 如果你想使用关系,那么你将不得不迭代支付。否则,该解决方案包含使用日志模型的方式,您可以使用该方式获得两者的组合日志,这不是您真正需要的吗?
猜你喜欢
  • 1970-01-01
  • 2017-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-05
相关资源
最近更新 更多