[英]“Three way” many-to-many relationship using Eloquent
I have a simple database setup: Users, Groups, Pages - each are many to many. 我有一个简单的数据库设置:用户,组,页面-每个都有很多。 See diagram: http://i.imgur.com/oFVsniH.png
参见图: http : //i.imgur.com/oFVsniH.png
Now I have a variable user id ($id), and with this I want to get back a list of the pages the user has access to, distinctly, since it's many-to-many on all tables. 现在,我有了一个可变的用户ID($ id),并希望以此返回用户可以访问的页面的列表,这显然是因为在所有表上都是多对多的。
I've setup my main models like so: 我已经像这样设置了我的主要模型:
class User extends Eloquent {
protected $table = 'ssms_users';
public function groups()
{
return $this->belongsToMany('Group', 'ssms_groups_users', 'user_id','group_id');
}
}
class Group extends Eloquent {
protected $table = 'ssms_groups';
public function users()
{
return $this->belongsToMany('User', 'ssms_groups_users', 'user_id','group_id');
}
public function pages()
{
return $this->belongsToMany('Page', 'ssms_groups_pages', 'group_id','page_id');
}
}
class Page extends Eloquent {
protected $table = 'ssms_pages';
public function groups()
{
return $this->belongsToMany('Group', 'ssms_groups_pages', 'group_id','page_id');
}
}
I can get the groups the user belongs to by simply doing: 我可以通过以下简单操作获得用户所属的组:
User::with('groups')->first(); // just the first user for now
However I'm totally lost on how to get the pages the user has access to (distinctly) with one query? 但是我完全迷失了如何通过一个查询获得用户(明确地)访问的页面?
I believe the SQL would be something like: 我相信SQL将是这样的:
select DISTINCT GP.page_id
from GroupUser GU
join GroupPage GP on GU.group_id = GP.group_id
where GU.user_id = $id
Can anyone help? 有人可以帮忙吗?
Thanks 谢谢
Have you tried something like this before? 您以前尝试过类似的东西吗?
$pages = User::with(array('groups', 'groups.pages'))->get();
Eager loading might be the solution to your problem: eager loading 渴望加载可能是您解决问题的方法: 渴望加载
TL;DR: The fetchAll
method below, in the MyCollection
class, does the work. TL; DR:下面的
MyCollection
类中的fetchAll
方法可以完成工作。 Simply call fetchAll($user->groups, 'pages');
只需调用
fetchAll($user->groups, 'pages');
Ok, assuming you managed to load the data (which should be done by eager-loading it, as mentioned in the other answer), you should loop through the Group
s the User
has, then loop through its Page
s and add it to a new collection. 好的,假设您设法加载了数据(应通过急于加载它来完成,如另一个答案所述),则应遍历
User
拥有的Group
,然后遍历其Page
并将其添加到新系列。 Since I've had this problem already, I figured it would be easier to simply extend Laravel's own Collection
class and add a generic method to do that. 因为我已经遇到了这个问题,所以我认为,简单地扩展Laravel自己的
Collection
类并添加一个通用方法来做到这一点会更容易。
To keep it simple, simply create a app/libraries
folder and add it to your composer.json
, under autoload -> classmap
, which will take care of loading the class for us. 为了保持它的简单,只需创建一个
app/libraries
文件夹,并把它添加到您的composer.json
下autoload -> classmap
,这将需要加载的类对我们的照顾。 Then put your extended Collection
class in the folder. 然后将扩展的
Collection
类放入文件夹中。
app/libraries/MyCollection.php app / libraries / MyCollection.php
use Illuminate\Database\Eloquent\Collection as IlluminateCollection;
class MyCollection extends IlluminateCollection {
public function fetchAll($allProps, &$newCollection = null) {
$allProps = explode('.', $allProps);
$curProp = array_shift($allProps);
// If this is the initial call, $newCollection should most likely be
// null and we'll have to instantiate it here
if ($newCollection === null) {
$newCollection = new self();
}
if (count($allProps) === 0) {
// If this is the last property we want, then do gather it, checking
// for duplicates using the model's key
foreach ($this as $item) {
foreach ($item->$curProp as $prop) {
if (! $newCollection->contains($prop->getKey())) {
$newCollection->push($prop);
}
}
}
} else {
// If we do have nested properties to gather, then pass we do it
// recursively, passing the $newCollection object by reference
foreach ($this as $item) {
foreach ($item->$curProp as $prop) {
static::make($prop)->fetchAll(implode('.', $allProps), $newCollection);
}
}
}
return $newCollection;
}
}
But then, to make sure your models will be using this class, and not the original Illuminate\\Database\\Eloquent\\Collection
, you'll have to create a base model from which you'll extend all your models, and overwrite the newCollection
method. 但是,然后,要确保您的模型将使用此类,而不是原始的
Illuminate\\Database\\Eloquent\\Collection
,则必须创建一个基础模型,从该基础模型扩展所有模型,并覆盖newCollection
方法。
app/models/BaseModel.php app / models / BaseModel.php
abstract class BaseModel extends Eloquent {
public function newCollection(array $models = array()) {
return new MyCollection($models);
}
}
Don't forget that your models should now extend BaseModel
, instead of Eloquent
. 不要忘记,你的模型现在应该
extend BaseModel
,而不是Eloquent
。 After all that is done, to get all your User
's Page
s, having only its ID, do: 完成所有操作后,要获取仅具有其ID的所有
User
's Page
,请执行以下操作:
$user = User::with(array('groups', 'groups.pages'))
->find($id);
$pages = $user->groups->fetchAll('pages');
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.