如何在laravel中对Eager Loaded关系进行分页

时间:2014-08-04 19:22:07

标签: laravel-4 eloquent

我有一个严重的问题。

我有3个模特

角色模型:

<?php

class Role extends Eloquent{

/**
 * The database table used by the model.
 *
 * @var string
 */
protected $table = 'roles';



public function users()
{
    return $this->belongsToMany('User', 'user_roles');
}

}

用户模型:

public function roles()
{
    return $this->belongsToMany('Role', 'user_roles');
}

现在在我的控制器中,我通过选定的角色获得用户,或者如果没有被选中,我将获得与所有角色相关的所有用户:

$roles = Role::with([
        'users' => function($query) use ($memtype)
        {

            $query->where('users.membertype', $memtype);
            $query->orderBy('users.name');
            $query->paginate(10);
        }
    ])->where('id', $role_id)->get();

如果我不想按角色过滤,我使用以下代码:

$roles = Role::with([
        'users' => function($query) use ($memtype)
        {

            $query->where('users.membertype', $memtype);
            $query->orderBy('users.name');
            $query->paginate(10);
        }
    ])->get();

在我看来

我正在尝试通过以下代码生成分页链接,

@foreach($roles as $role)

@foreach($role->users as $user)
    {{ $user->name }}
    <br>
    {{ $user->email }}

@endforeach
{{ $role->users()->links() }}
@endforeach

但我收到错误:

ErrorException

调用未定义的方法Illuminate \ Database \ Query \ Builder :: links()(查看:C:\ xampp \ htdocs \ waytohouse \ app \ views \ admin.blade.php)

请帮我解决这个问题..

此外,我在以下链接中尝试了Option1:

Laravel 4.1: How to paginate eloquent eager relationship?

但是如何在option1中添加约束。在我的情况下,它是膜类型? 提前谢谢!

2 个答案:

答案 0 :(得分:3)

有一些事情是不可能的,你想要做什么(显然不可能采用雄辩的关系方法)。

Eloquent处理急切加载的方式,而你使用paginate会导致整个事情不正确。

让我们检查没有paginate的示例:

select users.*, user_roles.user_id as pivot_user_id, user_roles.role_id as pivot_role_id
inner join user_roles on user_roles.user_id = users.id
where user_roles.role_id in ( .. some ids here .. );

然后Eloquent会将返回的users与其roles匹配。

现在,通过paginate,我们基本上会将limitoffset添加到查询中。所以这就是:

select users.*, [...]
where user_roles.role_id in ( .. some ids here .. )
limit 10 offset 0;

这意味着您只需将行数切成10行。总共10个,而不是每个角色10个

话虽如此,你最终会得到意想不到的,完全不真实的结果(除非幸运的是,只有10个或更少的用户可以返回,但我不会指望这个用户好运。)


还有另外一件事:

$role->users; // collection of eager loaded users

$role->users(); // BelongsToMany relation object

后者完全独立于先前急切加载的集合,与您在with闭包中调用的任何内容无关。


因此,分页相关模型的唯一方法实际上是使用:

$users = $role->users()->paginate(10);

// for example:
$roles = Role::where(..)->get();
$roles->map(function ($role) {
  $role->users = $role->users()->paginate(10);
});

为每个角色。这意味着每个角色都要查询一次db。

此外,您需要让Laravel了解哪个角色的用户已分页。我想ajax是你用来向用户展示这个的唯一方法,所以好的。如果没有,那就太麻烦了。

另一种方法是加载所有东西和&#39; paginatin&#39;集合$role->users,但我肯定不会。

答案 1 :(得分:0)

实际上,如何使用分区(在PostgreSQL中)为每个用户角色选择10个用户。这是查询:

SELECT * FROM (
  SELECT *, rank() over (partition by role_id order by id) FROM users
) AS partitioned_users
WHERE rank > 10 AND rank <= 20

所以SQL不是问题。唯一的问题是,如何使用laravel Query Builder构建查询。可以像这样在没有分页的情况下急切加载:

    UserRole::with(['users' => function($query) {
        $subQ = DB::table('users AS partitioned_users')->select('*')
            ->addSelect(DB::raw('rank() over (partition by user_role_id order by id desc)'));

        /** @var Builder $query */
        $query->from(DB::raw("(".$subQ->toSql().") AS users"))
            ->where('rank', '<=', 10)
            ->setBindings(array_merge_recursive($subQ->getBindings(), $query->getBindings()));
    })->all()

这样,它将加载所有用户角色,并为每个用户角色预先加载前10个用户。

但我还没有想出分页部分。我们需要的是创建自定义分页,使用此SQL:

WHERE rank > :offset AND rank <= :limit

而不是使用标准:

LIMIT :limit OFFSET :offset

一旦我弄明白,我就会更新你。