Laravel,雄辩的兄弟姐妹(下一个,前一个元素)在给定键的hasMany关系中

时间:2016-08-06 18:22:31

标签: php mysql eloquent laravel-5.2

假设我的模型Post有多个Comments

我也发现了一些评论:

$comment = Comment::find(12);

接下来,我想根据updated_at列找到下一个和之前的评论。

例如:

文章:

╔════╦══════════════╦══════╗
║ ID ║       Name   ║ Smtn ║
╠════╬══════════════╬══════╣
║  1 ║ First Post   ║ 5636 ║
║  2 ║ Second Post  ║  148 ║
╚════╩══════════════╩══════╝

评论:

╔════╦══════════════╦═════════╦════════════╗
║ ID ║   Comment    ║ post_id ║ updated_at ║
╠════╬══════════════╬═════════╣════════════╣
║  1 ║ First Comm   ║    1    ║  123       ║
║  2 ║ Second Post  ║    2    ║  124       ║
║  3 ║ Third Post   ║    1    ║  126       ║
║  4 ║ Fourth Post  ║    2    ║  125       ║
║  5 ║ Fifth Post   ║    1    ║  128       ║
║  6 ║ Sixsth Post  ║    1    ║  127       ║
╚════╩══════════════╩═════════╩════════════╝

我的评论是id为3的那个:

$comment = Comment::find(3);

如何定义previous()next()以获取updated_at列的评论并获取上一个和下一个的评论?在这种情况下:

一个:

║  1 ║ First Comm   ║    1    ║  123       ║

下一步:

║  6 ║ Sixsth Post  ║    1    ║  127       ║

updated_at是数据库中的DATETIME(带时区)字段。我懒得写出确切的代码。

3 个答案:

答案 0 :(得分:1)

第一个问题

在答案中看到这样做会得到正确的结果,但不建议这样做。

代码将获得与帖子相关的所有注释,应用顺序以及通过PHP数组操作的位置,然后为您提供过滤的注释。实际上在->comments之后,一切都是PHP。

如果评论的编号从数百到数千或更多,取决于您的硬件,这将非常糟糕。

要解决这个问题,你应该这样做;

$subjectComment = Comment::whereId($commentId)->with('post')->first();

$post = $subjectComment->post; 

$previousComment = $post->comments()
    ->where('updated_at', '<=', $subjectComment->updated_at)
    ->where($subjectComment->getKeyName(), '!=', $subjectComment->getKey())
    ->latest('updated_at')
    ->first();

$nextComment = $post->comments()
    ->where('updated_at', '>', $subjectComment->updated_at)
    ->oldest('updated_at')
    ->first();

注意区别?没有?这是一个简短的解释;

  

将关系用作模型的属性,例如$ post-&gt; comments

此语法在某些情况下具有某些优点,在​​其他情况下具有缺点。这就是Eloquent在幕后所做的事情;

  • 检查关系是否已加载
  • 如果没有,请加载它。这意味着它会加载所有相关模型
  • 这很好,如果你有逻辑可能会加载或不加载这种关系,具体取决于你正在处理的数据。
  • 虽然它绝对可怕,但如果您只需要查询将返回的模型的一小部分。
  

将关系用作模型的函数,例如$ post-&gt; comments()

此语法返回查询而不是模型,您可以进一步自定义以满足您的需要。该查询类似于;

Comment::whereHas('post', function ($q) use ($postId) {
    $instance = new Post;
    $q->where($instance->getQualifiedKeyName(), $postId);
});

第二个问题

两种陈述都没有区别。两者都会在相同的时间内完成同样的事情。唯一的区别是可读性。

答案 1 :(得分:0)

我看起来并不高兴,但我没有看到更好的方法来做到这一点,这对我来说很有意义。如果您有任何想法,请回复。

可能存在拼写错误,因为我的代码有点复杂,而且我的模型有不同的名称。如果您发现任何错误,请报告。

    //get current comment with id 12
    $comment = Comment::->find(12);
    //get parent post
    $post = $comment->post;

    //get all comments of this post
    $comments = $post->comments->orderBy('updated_at',ASC)->get();
    //some helper variables because I don't know the better way
    $prev = null; // the previous comment
    $next  = null; // the next comment
    $counter=0; //helper counter
    //iterate through all comments
    foreach ($comments as $commentIterated) {
        //if this is my comment increase the counter
        if($commentIterated->id == $comment->id) {
            $counter++;
        } elseif ($counter == 1) { //if counter is increased only once increase it again and save current comment as the next one (since in previous iteration we concluded that that was our comment and hence increased the counter first time)
            $next = $commentIterated;
            $counter++;
        } elseif ($counter == 0){ //if counter is not increased current comment is previous one, but this will be rewritten in next iteration if we don't find our comment again
            $prev = $commentIterated;
        }
        $comments->shift(); // Laravel helper method to remove the current element () from the collection so we are left with rest of collection and current instance is again first object in collection which is the next object in this iteration
    }

    return view('my_view', compact('comment','prev','next'));

专家的快速提问:

将替换此

    //get parent post
    $post = $comment->post;

    //get all comments of this post
    $comments = $post->comments->orderBy('updated_at',ASC)->get();

用这个

    //get all comments of this post
    $comments = $comment->post->comments->orderBy('updated_at',ASC)->get();

让事情变得更快?我不确定此方法链接在后台如何工作。有没有一种简单的方法可以在Laravel / PHP中对这些查询进行基准测试?

答案 2 :(得分:0)

这应该比你现在所做的更干净。只需选择1个相关评论,其中第一个updated_at低于或高于$ comment

$previous = $post->comments->orderBy('updated_at', 'DESC')->take(1)->where('updated_at', '<', $comment->updated_at)->first();

$next = $post->comments->orderBy('updated_at', 'ASC')->where('updated_at', '>', $comment->updated_at)->first();