2014-02-25 63 views
4

所以我有結構化這樣的數據:Laravel查詢生成器的遞歸結果?例如。 ID,PARENT_ID

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

您不能使用每次調用數據庫時要求查詢一行的遞歸(php)函數嗎?當然有一個開銷,它可能不那麼幹淨,然後有你在查詢所有的內容,但你有沒有考慮它? – Gregory

+0

我想我可以寫這樣的東西,但我想使用Laravel的超棒功能,並儘可能保持乾淨。 – tiffanyhwang

+0

說實話,我不確定這是可能的(但?)。但是你對我感興趣......如果你碰巧找到任何東西,讓我們知道 – Gregory

回答

2

另一種方式可能是使用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']); 
+0

這是一個很棒的包裝。我會盡力在本地解決方案上工作,然後再看看這個。謝謝。 – tiffanyhwang

+1

雖然這是一個很好的包,但它自己說:缺點是插入/移動/刪除需要複雜的SQL。它快速閱讀它,但在改變結構方面非常緩慢...... – Gregory

+0

查看http://en.wikipedia.org/wiki/Nested_set_model以獲得更好的理解。還有一個關於性能和缺點的部分。 – Gregory

6

所以摸索之後,與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; 
} 

這將產生我所需要的,但我相信它可以更清潔,所以請隨時編輯它!

4

我修改了tiffanyhwang解決方案,並將其轉換爲非靜態方法,幷包含一個屬性accessor以便更容易獲得結果。

public function ancestors() 
{ 
    $ancestors = $this->where('id', '=', $this->parent_id)->get(); 

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

    return $ancestors; 
} 

和存取從模型屬性

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',', '); 
+0

這真棒! –