Laravel Eloquent查询意外结果

时间:2016-11-17 20:46:43

标签: php mysql laravel laravel-5 eloquent

我发现一些查询结果真的出乎意料。

Laravel 5.2

我们有以下实体:

User方法:

public function roles() : BelongsToMany
{
    return $this->belongsToMany(Role::class)->withPivot('timestamp');
}

每个User可以有多个角色,因此我们还有Role个实体(但在我的问题中并不重要)和pivot表格user_role使用timestamp字段(当然还有ID),因为我们保存有关时间的信息,User何时达到特定角色。

我希望最后分配Users

的所有Role

当我创建查询时(在某个存储库的User上下文中):

$this->with(['roles' => function($query) {
    $query->orderBy('timestamp', 'desc');
}])->all();

结果将包含Users,其中Roles个实体按时间戳排序 - 它没问题。但我想在每个User实体中仅检索最后一个角色,而不是所有订购。

因此...

$this->with(['roles' => function($query) {
    $query->orderBy('timestamp', 'desc')->limit(1);
}])->all();

然后我检索Users,但只有User最后一次获得Role包含它!所有其他Users的{​​{1}}字段都包含空数组

为什么单独对每个roles关系执行排序,但是当我添加Users时,它的行为就像全局限制一样。

这让我发疯了......

感谢您的建议。

修改

我已创建limit方法以获取所有lastRoles()有序的desc。但是所有,检索一个是不可能的。

Roles

进行测试:

public function lastRoles() : BelongsToMany
{
    return $this->BelongsToMany(Roles::class)->withPivot('timestamp')->latest('timestamp');
}

但是现在我必须遍历$users = (new User())->with('lastRoles')->get(); 并在每一个上调用Users

lastRoles()

然后,我会检索分配给每个foreach ($users as $user) { var_dump($user->lastRoles()->get()->first()->name); } 的最新Roles的名称。

所以......在一个查询中无法做到这一点?这是唯一的方法吗?

2 个答案:

答案 0 :(得分:0)

当您急切地加载与查询约束的关系时,查询将运行一次以加载所有关系,而不是单独加载每个关系。这是预期的行为。考虑一下,存在急切加载以将许多查询转换为一个查询以优化性能。只执行了一个查询,因此您的 def self.find_all_user_comments(user) @comment_hash = [] @user_comments = Comment.where(user_id: user.id) @user_comments.all.each do |co| post = Post.find(co.commentable_id) @comment_hash.push( { comment: co, post: post} ) end @comment_hash end 约束将限制整个结果集,而不是基于每个模型。

为避免这种情况,您可以尝试创建另一个添加所需限制约束的<div class="col-md-10 col-md-offset-1"> <h3>Recent comments:</h3> <% if @comments.any? %> <table class="table"> <thead> <tr> <th>Comment</th> <th>Post</th> <th>Actions</th> </tr> </thead> <tbody> <% @comments.each do |co| %> <tr class="comment-<%= co[:comment].id %>"> <td><%= truncate(co[:comment].body, length: 50) %></td> <td><%= truncate(co[:post].title, length: 50) %></td> <td><%= link_to "destroy", comment_path(co[:comment]), remote: true, method: :delete, data: {confirm: "Are you sure you want to delete this comment?"} %></td> </tr> <% end %> </tbody> </table> <% end %> </div> 方法。以下代码未经测试:

limit

假设这有效,您可以简单地将关系方法从belongsToMany更改为public function lastRole() : BelongstoMany { return $this->belongsToMany(Role::class) ->withPivot('timestamp') ->orderBy('timestamp', 'desc') ->limit(1); } 并删除您的查询约束:

roles

答案 1 :(得分:0)

为此,您需要一个帮助函数:

public function latestRole()
{
 return $this->hasOne(Role::class)->withPivot('timestamp')->orderBy('timestamp', 'DESC');
}

然后:

$this->with('latestRole')->get();

对此awesome article的信任。