【问题标题】:Laravel Eloquent Relationships for SiblingsLaravel 兄弟姐妹的雄辩关系
【发布时间】:2014-11-07 10:30:46
【问题描述】:

兄弟姐妹的关系是多对多的自我关系。所以,对于多对多自我关系,我们可以在模型中定义两个函数:

public function siblings()
{
    return $this->belongsToMany('Student', 'student_sibling', 'student_id', 'sibling_id');
}

public function siblingOf()
{
    return $this->belongsToMany('Student', 'student_sibling', 'sibling_id', 'student_id');
}

第一个返回学生的兄弟姐妹。反之亦然,对于兄弟姐妹也是如此。因此,第二个返回学生是兄弟姐妹的学生。

因此,我们可以合并这两个集合以获取作为学生兄弟姐妹的学生列表。这是我在控制器方法中的代码:

    $siblingOf = $student->siblingOf;

    $siblings = $student->siblings;

    $siblings = $siblings->merge($siblingOf);

但还有更多。兄弟姐妹关系不同于朋友关系,是一种连锁关系。这意味着,如果 X 是 Y 的兄弟,Y 是 Z 的兄弟,则 Z 是 X 的兄弟。

那么,如何获取一个学生的兄弟姐妹的所有学生的集合?

【问题讨论】:

  • 所以更像是relative 而不是sibling,对吧?你希望它有多深?请注意,它可能会变成each student is everyone else's relative in a way

标签: laravel model eloquent relationships


【解决方案1】:

我假设您有一个 Student 模型,其中 student_id 作为您的主键,并且有一个如下所示的数据透视表:

+---------+------------+------------+
| id (PK) | student_id | sibling_id |
+---------+------------+------------+
| 1       | 1          | 2          |
| 2       | 2          | 1          |
| 3       | 3          | 4          |
| 4       | 4          | 2          |
| 5       | 6          | 5          |
+---------+------------+------------+

在这个例子中,我们希望能够辨别 1、2、3 和 4 都是彼此的兄弟姐妹。 5 和 6 也是兄弟姐妹。

解决此问题的一种方法是收集模型中两个 belongsToMany() 关系的结果(就像您在问题中所做的那样),然后递归迭代结果,继续检查 belongsToMany() 函数,直到我们运行没有兄弟姐妹。

在 Student 模型中,定义两个函数:

public function getAllSiblings(){

    // create a collection containing just the first student
    $this->siblings = $this->newCollection()->make($this);

    // add siblings (recursively) to the collection
    $this->gatherSiblings($this->student_id);

    // OPTIONAL: remove the first student, if desired
    $this->siblings->shift();

    return($this->siblings);
}

private function gatherSiblings($student_id){

    $checkStudent = Student::find($student_id);

    if ($checkStudent) {
        // get related siblings from model, combine them into one collection
        $siblingOf = $checkStudent->siblingOf()->get();
        $siblings = $checkStudent->siblings()->get();
        $siblings = $siblings->merge($siblingOf);

        // iterate over the related siblings
        $siblings->each(function($sibling)
        {
            // if we've found a new sibling who's 
            // not already in the collection, add it
            if(!$this->siblings->contains($sibling->student_id)) {
                $this->siblings->push($sibling);

                // look for more siblings (recurse)
                $this->gatherSiblings($sibling->student_id);
            };
        });
        return;
    }
}

在您的控制器中,找到初始学生,然后从 Student 调用 getAllSiblings()

$student = Student::find($id);
$siblings = $student->getAllSiblings();

结果是一个包含原始学生所有兄弟姐妹的集合。因此,如果您为学生 1 运行此命令,您将获得一个包含学生 2、3 和 4 的集合。(如果您希望将原始学生保留为兄弟姐妹集合的一部分,那么为 1 运行此命令将返回 1, 2、3和4,只需删除getAllSiblings()中的optional步骤即可。)

从那里,您可以根据需要将集合转换为数组或排序等。

【讨论】:

  • 你不需要这段代码,只需在关系上调用with 使其递归即可。然而,这不是要走的路,因为它可能很容易超出函数嵌套限制。
  • @JarekTkaczyk 你能用with演示解决这个问题吗?除了第一组兄弟姐妹之外,我看不到它如何检索任何东西。 with 将如何遍历整个兄弟姐妹链? (现实世界的限制会阻止这种情况反复出现十几次。)
  • 你去吧,伙计。告诉我,real world limitations would prevent.. 是什么意思?
  • 很酷,但导致无限循环(使用递归with 确实如此)并不能解决问题。 (根据问题的具体情况,现实世界的限制意味着一个学生不可能有超过十几个兄弟姐妹。也许有 20 个。当然少于无限。)
  • 你误会了。导致错误的不是方法本身,而是关系。想象一下这些 id 是相关的:1-2, 2-3, 3-1 - 这将使其无限。对于现实世界的限制 - 正如你所看到的,我问 OP 他是否指的是亲戚,因为很明显,你不能有一个兄弟姐妹,他的另一个兄弟姐妹也不是你的兄弟姐妹。因此,对于亲戚来说,再次调用无限循环非常容易,除非您明确将其限制在给定的嵌套级别。
【解决方案2】:

递归关系可以做到这一点,但它可能会导致无限循环(函数嵌套限制错误)

无论如何,这是建立这种关系的方法:

public function siblingsRecursive()
{
    return $this->siblings()->with('siblingsRecursive');
}

public function siblings()
{
    return $this->belongsToMany('Student', 'siblings');
}

然后你调用,就这么简单:

$students = Student::with('siblingsRecursive');

【讨论】:

  • 我认为递归使用 Eager Loading 在我正在构建的应用程序中可能是危险的。因为,没有办法知道递归中当前学生的兄弟姐妹已经找到了。所以,在某些情况下,会造成死循环。无论如何,谢谢你的回答。
【解决方案3】:

我想你可能想要的是这样的:

 public function siblings() {
    return $this->hasMany(self::class, 'parent_id', 'parent_id');
 }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-31
    • 1970-01-01
    • 2019-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-29
    • 1970-01-01
    相关资源
    最近更新 更多