所以我的数据结构如下:
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
有什么想法吗?
答案 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();
}