用于递归结果的Laravel查询构建器?例如。 id,parent_id

时间:2014-02-25 12:56:27

标签: php mysql recursion laravel-4

所以我的数据结构如下:

id|parent_id|name
1 |null     |foo
2 |1        |bar
3 |2        |baz

基本上foo->bar->baz。我很难理解如何使用laravel的查询构建器来获取子行的行,然后是它的祖先(直到parent_id == null)。这可以用laravel完成吗?我做了一点研究,Postgres有RECURSIVE而MySQL没有(Postgres recursive query to update values of a field while traversing parent_id)。

我相信MySQL有类似的东西:How to do the Recursive SELECT query in MySQL?

但是我如何在Laravel中实现它呢?

我的开始代码基本上是使用查询范围,但我只是没有做对:

Model::select('name')->getParent(3); //get baz and the ancestors of baz
protected function scopeGetParent($id) {
  $parent = Model::where('id', '=', $id);
  return $query->getParent($parent->parent_id);
}

我想要的结果是:

name
baz
bar
foo

有什么想法吗?

5 个答案:

答案 0 :(得分:7)

所以在摆弄了merge()类的Collections方法之后:

public static function ancestors($id)
{
    $ancestors = Model::where('id', '=', $id)->get();

    while ($ancestors->last()->parent_id !== null)
    {
      $parent = Model::where('id', '=', $ancestors->last()->parent_id)->get();
      $ancestors = $ancestors->merge($parent);
    }

    return $ancestors;
}

这将产生我需要的东西,但我相信它可以更清洁,所以请随时编辑它!

答案 1 :(得分:5)

我修改了tiffanyhwang解决方案,并将其转换为非静态方法,并添加了一个属性accessor,以便更轻松地获得结果。

public function getAncestorsAttribute()
{
    return $this->ancestors();
    // or like this, if you want it the other way around
    // return $this->ancestors()->reverse();
}

和访问器从模型属性

中检索祖先的集合
$ancestors = $model->ancestors;

所以现在你可以得到这样的祖先:

echo $model->ancestors->implode('title',', ');

并且由于它是一个集合,你现在可以轻松地做到这一点:

  Dim myRecSet As New ADODB.Recordset
        Dim strSql As String
        strSql = "select * from RentBalances where KeyTcyIdSubAcDate = '" & sKeyTcyIdSubAcDate & "'"
        'Display "SQL: " & strSql
        myRecSet.Open strSql, SQLSVSExtractConnection, adOpenKeyset, adLockOptimistic
        'Display "Total no of records = " & myRecSet.RecordCount
        If myRecSet.RecordCount < 1 Then
            'Display ("There are no RentBalances record for this ID. ID = " & sKeyTcyIdSubAcDate)
        Else
            ' delete the record

            myRecSet.Delete
            myRecSet.UpdateBatch
        End If

        myRecSet.AddNew
        myRecSet!KeyTcyIdSubAcDate = rsLocal.Fields("KeyTcyIdSubAcDate")
        myRecSet!KeyTcyId = rsLocal.Fields("KeyTcyId")
        myRecSet!SubAc = rsLocal.Fields("SubAc")
        myRecSet!PeriodEndDate = rsLocal.Fields("PeriodEndDate")

        myRecSet!Amount = rsLocal.Fields("Amount")
        myRecSet!RentAmount = rsLocal.Fields("RentAmount")
        myRecSet!ChargesAmount = rsLocal.Fields("ChargesAmount")
        myRecSet!AdjustmentAmount = rsLocal.Fields("AdjustmentAmount")
        myRecSet!BenefitAmount = rsLocal.Fields("BenefitAmount")
        myRecSet!BenefitBalance = rsLocal.Fields("BenefitBalance")
        myRecSet!TenantBalance = rsLocal.Fields("TenantBalance")
        myRecSet!PayAmount = rsLocal.Fields("PayAmount")
        myRecSet!TimeStamp = rsLocal.Fields("TimeStamp")
        myRecSet!UpdateFlag = rsLocal.Fields("UpdateFlag")
        myRecSet.Update
        myRecCount = myRecCount + 1
        myRecSet.Close  

答案 2 :(得分:3)

另一种方法可能是使用etrepat/baum包,它是Nested set model的Laravel实现。它使用更快的有序树并使用非递归查询。 虽然您的数据结构如下:

root
  |_ Child 1
    |_ Child 1.1
    |_ Child 1.2
  |_ Child 2
    |_ Child 2.1
    |_ Child 2.2

在嵌套集模型中有这样的结构:

 ___________________________________________________________________
|  Root                                                             |
|    ____________________________    ____________________________   |
|   |  Child 1                  |   |  Child 2                  |   |
|   |   __________   _________  |   |   __________   _________  |   |
|   |  |  C 1.1  |  |  C 1.2 |  |   |  |  C 2.1  |  |  C 2.2 |  |   |
1   2  3_________4  5________6  7   8  9_________10 11_______12 13  14
|   |___________________________|   |___________________________|   |
|___________________________________________________________________|

插入节点非常简单:

$child1 = $root->children()->create(['name' => 'Child 1']);

答案 3 :(得分:0)

我创建了一个使用通用表表达式(CTE)来实现递归关系的包:https://github.com/staudenmeir/laravel-adjacency-list

您可以使用ancestors关系来递归获取模型的所有父代:

class YourModel extends Model
{
    use \Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;
}

$allParents = YourModel::find($id)->ancestors;

答案 4 :(得分:0)

我修改了ruuter答案以使用关系。 如果您在模型上具有parent()的归属关系,则可以使用该关系来删除where子句,请参见下文:

public function parents()
{
        $parents = $this->parent()->get();

        while ($parents->last() && $parents->last()->parent_id !== null) {
                $parent = $parents->last()->parent()->get();
                $parents = $parents->merge($parent);
        }

        return $parents;
}

然后您可以访问它:

public function allParents(): Collection 
{
        return $this->parents();
}