2014-06-29 5 views
1

Я не привык к рекурсивным функциям, и я не понимаю, как правильно выйти из функции.PHP рекурсивный перерыв

Я пытаюсь определить список детей (grand-children и т. Д.) Из родительского элемента в базе данных (с Laravel).

public function getChildren($parentId, $allChildrenArray = Array()){ 
    $children = DB::table('myTable')->where('parent',$parentId)->get(); 
    if (sizeof($children)>0){ 
     foreach ($children as $child) { 
      array_push($allChildrenArray,array($child->slug, $child->id)); 
      MyController::getChildren($child->id, $allChildrenArray); 
     } 
    }else{ 
     // var_dump($allChildrenArray); displays a proper array 
     return $allChildrenArray; 
    } 
} 

У меня есть два вопроса по этому вопросу.

  1. Как я понимаю, мой сценарий прекратил распространение, как только встретится тупик. Но что, если есть другие пути для изучения?
  2. Если отображается var_dump($allChildrenArray), отображается массив, который выглядит нормально. Однако, если я пытаюсь вывести его из другой функции, я получаю null ...

    public function doStuff($itemId){ 
        $allChildrenArray = MyController::getChildren($itemId); 
        var_dump($allChildrenArray); // displays null 
    } 
    
+0

Самый простой способ понять это - пройти все шаги вручную на листе бумаги. «Я получаю« null », потому что вы ничего не возвращаете в случае, если' sizeof ($ children)> 0'. Вы также не используете результат вызова MyController :: getChildren. – zerkms

+0

Но если 'sizeof ($ children)> 0', если есть дети, я обновляю значение' $ allChildrenArray'. И затем я просто перезапускаю то же самое с обновленным значением массива children и обновленным эталоном. Что я должен вернуть? – Yako

+0

«Я обновляю значение $ allChildrenArray» и ничего не делаю с ним. Вы изменили переменную, и что? Это не волшебное изменение в том месте, откуда вы его передали. – zerkms

ответ

2

Во-первых, вы должны положить ваши помощники за пределами ваших контроллеров :)

больше к point: чтобы понять рекурсию, она помогает пройти шаг за шагом, и когда вы доберетесь до рекурсии, скажем, предположим, что это делает то, что должно потом вернуться туда позже.

Начнем так:

/** 
    * Returns the children for a given parent 
    * @param int $parentId 
    * @return array 
    */ 
    public function getChildren($parentId){ 
     $allChildrenArray = array(); 
    $children = DB::table('myTable')->where('parent',$parentId)->get(); 
     foreach ($children as $child) { 
     array_push($allChildrenArray, array($child->slug, $child->id)); 
     // Here should be the recursion later 
     } 
     // var_dump($allChildrenArray); displays a proper array 
     return $allChildrenArray; 
    } 

Теперь, если вы посмотрите на эту функцию, вы увидите, что она работает на первом уровне. Легко сказать, что при просмотре детей данного родителя вам нужно будет получить этих потомков, если вы захотите добавить рекурсию.

/** 
    * Returns the children recursively for a given parent 
    * @param int $parentId 
    * @return array 
    */ 
    public function getChildren($parentId){ 
     $allChildrenArray = array(); 
     $children = DB::table('myTable')->where('parent',$parentId)->get(); 
     foreach ($children as $child) { 
     array_push($allChildrenArray, array($child->slug, $child->id)); 
     // get descendants of child 
     $furtherDescendants = $this->getChildren($child->id); 
     // add them to current list 
     foreach ($furtherDescendants as $desc) { 
      $allChildrenArray[] = $desc; // or use arraypush or merge or whatever 
     } 
     } 
     // var_dump($allChildrenArray); displays a proper array 
     return $allChildrenArray; 
    } 

Теперь то, что происходит в том, что, когда вы достигнете первого ребенка новый запуск функции GetChildren начнется Ид этого ребенка в качестве родительского идентификатора. Если их нет, тогда он вернет пустой массив, в противном случае он добавит информацию для этого ребенка, а затем запустит новый прогон для идентификатора внука в качестве родительского идентификатора и ...

Вы можете вероятно, сохраните некоторую память, если вы пройдете массив вдоль, но в этом случае вам нужно сделать это как ссылку. Также тогда, когда вы вызовете этот метод в первую очередь, вам нужно передать переменную в качестве ввода, который будет заполнен.

/** 
    * Returns the children recursively for a given parent 
    * @param int $parentId 
    * @param &array $children 
    */ 
    public function getChildren($parentId, &$allChildrenArray) { 
     $children = DB::table('myTable')->where('parent',$parentId)->get(); 
     foreach ($children as $child) { 
     array_push($allChildrenArray, array($child->slug, $child->id)); 
     // get descendants of child 
     $this->getChildren($child->id, $allChildrenArray); 
     } 
     // var_dump($allChildrenArray); displays a proper array 
     return; // nothing to return, children info added to variable passed by reference 
    } 

    ... 

     $kids=array(); 
     $this->getChildren($parentId, $kids); 
     var_dump($kids) 

Просто убедитесь, что вы не смешиваете два разных решения.

Условие выхода будет иметь место, если у данного родителя нет детей. В этом случае foreach не запускается, поэтому дальнейших рекурсивных вызовов не будет. Однако это означает только выход из этой ветви.

+0

Большое вам спасибо за этот очень полный ответ! – Yako

Смежные вопросы