使用 Laravel 将两个模型合并到一个分页查询中,并带有急切加载的关系

Posted

技术标签:

【中文标题】使用 Laravel 将两个模型合并到一个分页查询中,并带有急切加载的关系【英文标题】:Two models into one paginated query with eager loaded relations using Laravel 【发布时间】:2016-01-26 01:58:05 【问题描述】:

我想将两个模型合并到一个时间轴中。我已经能够通过在 mysql 中创建一个视图来规范化和合并表来做到这一点。我为此视图创建了一个模型,NewsFeed。如果我不想要相关的Comment 模型,这很有效。通过覆盖模型上的getMorphClass 方法,我已经接近了这一点。这使我可以获取图片的相关 cmets,但不能获取帖子,因为当调用 getMorphClass 时,模型没有任何数据。

我对如何解决这个问题的任何方法持开放态度,而不仅仅是我提议的方式,但我不想从数据库中提取更多数据。

新闻源

    <?php

    namespace App\Users;

    use App\Pictures\Picture;
    use App\Social\Comments\CommentableTrait;
    use App\Posts\Post;
    use App\Users\User;
    use Illuminate\Database\Eloquent\Model;

    class UserFeed extends Model
    
        use CommentableTrait;

        public function user()
        
            return $this->belongsTo(User::class);
        

        public function getMorphClass()
            if ($this->type == 'post')
                return Post::class;
            
            return Picture::class;
        
    

MySQL 视图

CREATE VIEW 
   `user_feeds`
AS SELECT
   `posts`.`id` AS `id`,
   `posts`.`user_id` AS `user_id`,
   'post' AS `type`,
   NULL AS `name`,
   NULL AS `thumbnail`,
   `posts`.`body` AS `body`,
   `posts`.`updated_at` AS `updated_at`,
   `posts`.`created_at` AS `created_at`
FROM 
    `posts` 
UNION SELECT
    `pictures`.`id` AS `id`,
    `pictures`.`user_id` AS `user_id`,
    'picture' AS `type`,
    `pictures`.`name` AS `name`,
    `pictures`.`thumbnail` AS `thumbnail`,
    `pictures`.`description` AS `body`,
    `pictures`.`updated_at` AS `updated_at`,
    `pictures`.`created_at` AS `created_at` 
FROM
    `pictures`;

图片表

    id
    user_id
    title
    img
    img_width
    img_height
    img_other
    description
    created_at
    updated_at

帖子

    id
    user_id
    title
    body
    created_at
    updated_at

【问题讨论】:

你不能只使用急切加载吗? $var = Picture::with('posts','cmets')... 最接近的是获取 User::with(['posts','pictures'])->get();这不会让你分页内容。我想过做一些类似$user-&gt;posts()-&gt;paginate(15);$user-&gt;pictures()-&gt;paginate(15); 然后合并集合的事情,但是一旦你想要更多的记录集就会出现问题。 为什么不分页 User::with(['posts','pictures'])-&gt;paginate(15); 甚至 User::with(['posts','posts.comments','pictures'])-&gt;paginate(15); 并在帖子模型上拉一个 cmets 方法呢? 因为这可以让您获得 15 个用户,而不是一个用户根据 created_at 日期拥有 15 张图片和帖子的混合集合。 您可以collect 所有结果并从那里分页。 collect([$user-&gt;posts, $user-&gt;pictures])-&gt;sortBy('created_date')-&gt;forPage($page, 15); 【参考方案1】:

您非常接近构建视图的想法。实际上,如果您创建一个实际的表而不是视图,则解决方案变得非常简单。

使用指向 Post 类或 Picture 类的“FeedItem”多态对象,您可以通过 hasMany 关系将 cmets 直接附加到 FeedItem。

class FeedItem extends Model 
    use CommentableTrait;
    public function feedable()
    
        return $this->morphTo();
    


class Post extends Model 
    public function feeditem()
    
        return $this->morphOne('FeedItem', 'feedable');
    


class Picture extends Model 
    public function feeditem()
    
        return $this->morphOne('FeedItem', 'feedable');
    

此解决方案可能需要对表单进行一些重构,因为您需要为每个帖子条目和图片条目创建一个 FeedItem 条目。 Picture::created 和 Post::created 的事件监听器应该可以解决问题 (http://laravel.com/docs/5.1/eloquent#events)。

设置完成后,您可以使用:

FeedItem::with('comments')->orderBy('created_at','desc')->paginate(15);

【讨论】:

【参考方案2】:

虽然我对 Laravel 和 Eloquent 都不熟悉,但这是我对此的看法。

假设您将该 SQL 视图的输出放入 $Entries

据我了解,Eloquent 允许您为自己设置值,因此类似这样的方法可能对您有用(我不确定这方面的语法或用法)。

$Collection = [];
foreach( $Entries as $Entry ) 
    if( $Entry->type === 'post' ) 
        $Collection[] = new Post($Entry->toArray())->with('comments');
    else
        $Collection[] = new Picture($Entry->toArray())->with('comments');
    

return $Collection;

【讨论】:

以上是关于使用 Laravel 将两个模型合并到一个分页查询中,并带有急切加载的关系的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Laravel 5 中对合并的集合进行分页?

如何合并集合和查询构建器,然后在其上使用分页 Laravel 8

分页 Laravel 集合

如何使用 Laravel 将查询字符串附加到分页链接

如何自动将查询字符串附加到 laravel 分页链接?

合并两个查询并根据 Laravel 中的 created_at 对它们进行排序