2017-01-28 39 views
0

我在這個問題上掙扎了一段時間,找不到解決方案,所以我希望得到一些幫助。Laravel - 得到樹狀結構的計數,其中父數是來自子女的總數

我有兩個相關的模型,ModelOne也與自己有關,可以有無限數量的子女,孫輩等,並與ModelTwo有關。

class ModelOne extends Model 
{ 
    ... 

    public function model_two() 
    { 
     return $this->hasMany(ModelTwo::class, 'foreign_key'); 
    } 

    //recursively get all children, grandchildren etc. 
    public function children() { 
     return $this->hasMany(ModelOne::class, 'foreign_key')->with('children'); 
    } 

    public function parent() { 
     return $this->belongsTo(ModelOne::class, 'foreign_key'); 
    } 
} 

class ModelTwo extends Model 
{ 
    ... 

    public function model_one() 
    { 
     return $this->belongsTo(ModelOne::class, 'foreign_key'); 
    } 
} 

在我get函數從ModelOne我:

public function get($id = null) { 
    return is_null($id) ? 
     ModelOne::withCount('model_two')->get() : 
     ModelOne::withCount('model_two')->where('id', $id)->first(); 
} 

也練就了我的對象好看的一維數組(或一個對象),與相關ModelTwo計數形式,這是我需要的,但每個相關對象都有個別計數。

我需要的是,每個ModelOne對象在返回數組中有子女,孫輩等,在該對象中計數是來自其所有子女,孫輩等的計數的總和。 因此,例如,如果我有一個ModelOne的對象,id = 2,parent_id = 1,計數爲3,另一個對象的id = 3,parent_id = 1,計數爲2,對於id = 1的對象,計數將是5.這個邏輯在所有節點的樹中繼續。

[ 
    { 
    "id":1 
    "parent_id":"NULL", 
    "model_two_count":5 
    }, 
    { 
    "id":2 
    "parent_id":1, 
    "model_two_count":3 
    }, 
    { 
    "id":3 
    "parent_id":1, 
    "model_two_count":2 
} 

... 

] 

回答

1

所以,我想出瞭解決方案,在這裏,它適用於所有人可能正在努力解決同樣的問題。

首先,在我的ModelOne中,我添加了withCount('model_two'),這樣我就立即擁有從ModelTwo的關係計數。我還增加了一個財產$return_count,將持有所有子孫後代的總和等。

class ModelOne extends Model 
{ 

    private $return_count = 0; 

    ... 

    public function model_two() 
    { 
     return $this->hasMany(ModelTwo::class, 'foreign_key'); 
    } 

    //recursively get all children, grandchildren etc. 
    public function children() { 
     return $this->hasMany(ModelOne::class, 'foreign_key')->withCount('model_two')->with('children'); 
    } 

    public function parent() { 
     return $this->belongsTo(ModelOne::class, 'foreign_key'); 
    } 
} 

然後,我添加了兩個輔助函數,一個遞歸獲取所有的子孫孫等。對於給定父ID,或者還給從表中的所有記錄:從這個函數

public function get_recursive($parentId = null){ 
    return is_null($parentId) ? 
     ModelOne::with('children')->get() : 
     ModelOne::where('id', $parentId)->with('children')->first(); 
} 

輸出是這樣的:

[ 
    { 
    "id":1 
    "parent_id":"NULL", 
    "model_two_count":0. 
    "children": [ 
     { 
      "id":2 
      "parent_id":1, 
      "model_two_count":3 
     }, 
     { 
      "id":3 
      "parent_id":1, 
      "model_two_count":2 
     } 
     ] 
    } 

... 

] 

這個例子是二維嵌套,但它可以去無限深。 在此函數中創建的返回對象成爲第二個函數中的輸入參數。第二個功能實際上是對來自所有子孫的計數進行遞歸求和。對於給定的父對象。當調用withCount('model_two')時,可以訪問屬性model_two_count,因爲它被創建。另外,調用with('children')時,將創建屬性children

private function count_sum($parentChildren) { 
    foreach ($parentChildren->children as $child) { 
     $this->return_count += $child->model_two_count; 
     $this->count_sum($child); 
    } 
    return $this->return_count; 
} 

在我把我的get函數結束:

public function get($id = null) { 
    if(is_null($id = null)) { 
     $data = ModelOne::withCount('model_two')->get();     
     return $data->map(function ($i) {    

      //get all children-grandchildren-etc. for specfic object with id = $i->id 
      $children = $this->get_recursive($i->id); 

      //if object with id = $i->id have children then model_two_count is sum of all model_two_count from all children-grandchildren-etc.      
      if (!empty(array_filter((array)$children->children))) { 
       $i->model_two_count = $this->count_sum($children); 
       //reset the return_count variable for next iteration. If this is not done, then sum from previous object will be added to the next object count 
       $this->return_count = 0; 
      } 
      return $i; 
     }); 
    } else { 
     $data = ModelOne::withCount('model_two')->where('id', $id)->get();     
     return $data->map(function ($i) { 
      $children = $this->get_recursive($i->id); 
      if (!empty(array_filter((array)$children->children))) { 
       $i->model_two_count = $this->count_sum($children);       
       $this->return_count = 0; 
      } 
      return $i; 
     })->first(); 
    } 
} 

這將讓你期望的漂亮的輸出:

[ 
    { 
    "id":1 
    "parent_id":"NULL", 
    "model_two_count":5 
    }, 
    { 
    "id":2 
    "parent_id":1, 
    "model_two_count":3 
    }, 
    { 
    "id":3 
    "parent_id":1, 
    "model_two_count":2 
} 

... 

]