2017-01-28 15 views
0

Я борюсь с этой проблемой в течение некоторого времени, не могу найти решение, поэтому я надеюсь на какую-то помощь.Laravel - получить счет в древовидной структуре, где count в parent - сумма отсчетов от детей

У меня есть две связанные модели, 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'); 
    } 
} 

В моей функции получить от 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 и count 3, а другой объект с id = 3, parent_id = 1 и count 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'); 
    } 
} 

Затем я добавил две вспомогательные функции, одна из которых рекурсивно получает все дети-внуки и т.д.. для данного родительского идентификатора или возвращает все записи из таблицы:

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 
     } 
     ] 
    } 

... 

] 

Этот пример для двумерного вложенности, но он может пойти бесконечно глубокий. Возвращаемый объект, созданный в этой функции, становится входным параметром во второй функции. Вторая функция фактически выполняет рекурсивное суммирование отсчетов от всех детей-внуков и т. Д. для данного родительского объекта. Свойство model_two_count можно получить, потому что оно создается, когда был вызван withCount('model_two'). Кроме того, свойство children создается, когда был вызван with('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; 
} 

В конце концов, я называю моей прибудет функцию:

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 
} 

... 

]