相关模型返回集合时如何访问子相关模型

时间:2020-07-24 17:15:09

标签: php laravel eloquent

我有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返回了一个Collection,因此不再是查询生成器/雄辩的对象,并且在我尝试调用->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

预先感谢

1 个答案:

答案 0 :(得分:0)

尽早加载关系(减少查询数量)

$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();

OR

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

$all_logs = collect([]);

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

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


$final_logs = $all_logs->collapse();

OR

只需使用关系即可,无需重复付款。您可以根据需要合并结果(如前一种方法所示)。

$user_logs = $user->logs;

$payment_logs = $user->payments->pluck('logs')->collapse();
相关问题