Laravel 4-Elokwentny. Nieskończone dzieci do użytecznej tablicy?

Mam tabelę kategorii. Każda kategoria może mieć opcjonalny rodzic (Domyślnie 0, jeśli nie ma rodzica).

Chcę zbudować proste drzewo listy html z poziomami kategorii.

Przykładowa data:

Foods
-- Fruit
---- Apple
---- Banana
---- Orange
-- Veg
---- Cucumber
---- Lettuce
Drinks
-- Alcoholic
---- Beer
---- Vodka
Misc
-- Household Objects
---- Kitchen
------ Electrical
-------- Cooking
---------- Stove
---------- Toaster
---------- Microwave

Zauważ, że to musi działać przez około 10 'poziomów'. Chciałbym, aby był nieskończony, ale naprawdę nie chcę iść w dół trasy korzystania z zagnieżdżonego modelu zestawu, ponieważ spowoduje to ogromne opóźnienia w tym projekcie.

Dokumenty na ten temat dla laravel są straszne, bez żadnego odniesienia, od czego zacząć. Bawiłem się nim od kilku dni, próbując wymyślić, co robić i wydaje się, że nigdzie nie dociera bez ogromnego niechlujnego bloku dla każdej pętli w sobie 10 razy.

Mam swoje drzewo danych przy użyciu następującego modelu:

<?php
class Item extends Eloquent {
    public function parent()
    {
        return $this->hasOne('Item', 'id', 'parent_id');
    }

    public function children()
    {
        return $this->hasMany('Item', 'parent_id', 'id');
    }

    public function tree()
    {
        return static::with(implode('.', array_fill(0,10, 'children')))->where('parent_id', '=', '0')->get();
    }
}
Dzięki temu wszyscy rodzice i dzieci osiągają poziom 10. To działa dobrze, ale naprawdę nie można zrobić nic z danymi potomnymi bez ręcznego posiadania 10 pętli foreach wewnątrz siebie nawzajem. Co ja tu robię źle? Na pewno nie powinno to być tak trudne/źle wykonane? Wszystko, co chcę zrobić, to uzyskać prostą listę html z elementami w strukturze drzewa.

Zebrałem szybki przykład sqlfiddle atrapy danych użytych powyżej: http://sqlfiddle.com/#! 2 / e6d18/1

Author: StackOverflowNewbie, 2013-05-14

3 answers

To było o wiele zabawniejsze niż moja zwykła krzyżówka. :)

Oto Klasa ItemsHelper, która zrobi to, czego szukasz, a jeszcze lepiej będzie rekurencyjnie tak daleko, jak chcesz.

app/models/ItemsHelper.php:

<?php

  class ItemsHelper {

    private $items;

    public function __construct($items) {
      $this->items = $items;
    }

    public function htmlList() {
      return $this->htmlFromArray($this->itemArray());
    }

    private function itemArray() {
      $result = array();
      foreach($this->items as $item) {
        if ($item->parent_id == 0) {
          $result[$item->name] = $this->itemWithChildren($item);
        }
      }
      return $result;
    }

    private function childrenOf($item) {
      $result = array();
      foreach($this->items as $i) {
        if ($i->parent_id == $item->id) {
          $result[] = $i;
        }
      }
      return $result;
    }

    private function itemWithChildren($item) {
      $result = array();
      $children = $this->childrenOf($item);
      foreach ($children as $child) {
        $result[$child->name] = $this->itemWithChildren($child);
      }
      return $result;
    }

    private function htmlFromArray($array) {
      $html = '';
      foreach($array as $k=>$v) {
        $html .= "<ul>";
        $html .= "<li>".$k."</li>";
        if(count($v) > 0) {
          $html .= $this->htmlFromArray($v);
        }
        $html .= "</ul>";
      }
      return $html;
    }
  }

Właśnie użyłem nowej instalacji Laravel 4 i podstawowego widoku hello.php.

Oto moja trasa w app/routes.php:

Route::get('/', function()
{
  $items = Item::all();
  $itemsHelper = new ItemsHelper($items);
  return View::make('hello',compact('items','itemsHelper'));
});

Chociaż mój widok nie używa zmiennej items, przekazuję ją tutaj, ponieważ prawdopodobnie będziesz chciał zrobić z nimi coś innego też.

I wreszcie mój app/views/hello.php ma tylko jedną linijkę:

<?= $itemsHelper->htmlList(); ?>

Wyjście wygląda jak to:

  • Foods
    • owoce
      • Apple
      • Banan
      • Orange
    • warzywa
      • ogórek
      • sałata
  • napoje
    • alkoholik
      • piwo
      • wódka
  • Misc
    • gospodarstwo domowe Obiekty
      • Kuchnia
        • elektryczne
          • gotowanie
            • Piec
            • toster
            • kuchenka mikrofalowa

Uwaga: Twój SQL Fiddle miał 5 ("Orange") jako parent_id dla ogórka i sałaty, musiałem zmienić go na 6 ("Veg").

 39
Author: Mark Smith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-14 16:09:53

Rozszerzyłem akceptowaną przez Marka Smitha odpowiedź, aby umożliwić generowanym listom odwoływanie się do danych dodawania, które są przekazywane do klasy.

Klasa działa prawie tak samo, ale spakowałem ją, więc mam nadzieję, że można z niej łatwo korzystać.

Po prostu odwołaj się do klasy pomocniczej w kontrolerze:

use App\Helpers\CategoryHierarchy;

Możesz wtedy albo utworzyć instancję klasy ręcznie, albo użyć metody Laravela 5 ' s injection rzeczy stają się jeszcze lepsze:

$products = $product->getAllProducts();
$hierarchy->setupItems($products);
return $hierarchy->render();

To może wypisać "po": {]}

<ul class='simple-list'>
   <li><input type="checkbox" name="hierarchy-checkboxes[]" value="1" >Home delivery</li>
      <ul>
        <li><input type="checkbox" name="hierarchy-checkboxes[]" value="2" >Italian</li>
          <ul>
            <li><input type="checkbox" name="hierarchy-checkboxes[]" value="3" >Pizza</li>
            <li><input type="checkbox" name="hierarchy-checkboxes[]" value="4" >Pasta</li>
          </ul>
        <li><input type="checkbox" name="hierarchy-checkboxes[]" value="5" >Burgers</li>
      </ul>
</ul>

Dostępny jest repo: https://github.com/amochohan/CategoryHierarchy co wyjaśnia bardziej szczegółowo.

 2
Author: Amo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-23 15:08:12

Używam tej funkcji, aby to działało.

 //Returns Root elements
 public function scopeRoot($query) {
        $all = $query->whereParent(0)->get();
        $collection = $all->filter(function($single) {
            if ($single->ModelFilter('GET')) {
                return $single;
            }
        });
        return $collection;
    }
    //Recursive call
    public function traverse() {
        self::_traverse($this->Children, $array, $this);
        return $array;
    }

    //This function build a multidimensional array based on a collection of elements
    private static function _traverse($collection, &$array, $object) {
        $new_array = array();
        foreach ($collection as $element) {
            self::_traverse($element->Children, $new_array, $element);
        }
        $array[] = $object;
        if (count($new_array) > 0) {
            $array[] = $new_array;
        }
    }

Najpierw dostaję zbiór pierwiastków, które są tymi, które przekazuję do moich widoków, gdzie chcę wymienić drzewo...

Więc tak...
 <ul class="bg-info cat-list">
        @foreach($categories as $category)
        <?php
        $array = $category->traverse();
        list_view($array);
        ?>
        @endforeach
    </ul>

Za pomocą tej funkcji...

//Prints a multidimensional array as a nested HTML list
function list_view($element, $ul = true) {
    foreach ($element as $value) {
        if (!is_array($value)) {
            echo "<li>";
            echo $value->name;
        } else {
            echo ($ul) ? "<ul>" : "<ol>";
            list_view($valuce, $ul);
            echo "</li>";
            echo ($ul) ? "</ul>" : "</ol>";
        }
    }
}

Wyjście

Hope it helps

 1
Author: Carlos Arturo Alaniz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-24 18:38:18