2013-05-14 10 views
10

Ich habe eine Kategorien-Tabelle. Jede Kategorie kann ein optionales Elternelement haben (Standard ist 0, wenn kein Elternteil vorhanden ist).Laravel 4 - beredt. Infinite Kinder in brauchbare Array?

Was ich tun möchte, ist einen einfachen HTML-Listenbaum mit den Ebenen der Kategorien zu erstellen.

Beispiel Datum:

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

Beachten Sie, dass dies für etwa 10 'Ebene' arbeiten muss. Ich würde es lieben, unendlich zu sein, aber ich möchte wirklich nicht den Weg des Verwendens eines geschachtelten Satzmodells gehen, da es große Verzögerungen auf diesem Projekt verursachen wird.

Die Dokumente auf dieser für Laravel sind schrecklich, ohne wirklichen Hinweis, wo man überhaupt anfangen soll. Ich habe tagelang damit gespielt und versucht, herauszufinden, was zu tun ist, und es scheint, als ob ich ohne einen riesigen unordentlichen Block von jeder Schleife innerhalb von 10 Mal nirgendwohin komme.

Ich habe meinen Baum von Daten habe die folgend in meinem Modell:

<?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(); 
    } 
} 

Dies wird all Eltern und Kinder bis zu einem Niveau von 10 Dies funktioniert gut, aber man kann nicht wirklich dann tun alles mit den Kinddaten, ohne manuell 10 foreach Schleifen innerhalb von einander zu haben.

Was mache ich hier falsch? Das sollte doch nicht so hart/schlecht ausgeführt sein? Ich möchte lediglich eine einfache HTML-Liste mit den Elementen in einer Baumstruktur erstellen.

Ich habe zusammen ein schnelles SQLFiddle Beispiel der Dummy-Daten oben verwendet: http://sqlfiddle.com/#!2/e6d18/1

+0

Verschachtelte Sätze sind eigentlich einfacher. Außerdem gibt es einige bereits vorhandene Pakete wie [Cartalysts verschachtelte Sätze] (http://docs.cartalyst.com/nested-sets-2) oder [Baum] (https://github.com/etrepat/baum). –

+0

Jason, was ist der Vorteil von verschachtelten Sets im Vergleich zu der akzeptierten Antwort? Ich muss mich zwischen den beiden entscheiden. Und mit Baum scheint es keinen Weg zu geben, diesen Baum trotzdem wiederzubeschaffen, es sei denn, Sie verwenden die akzeptierte Antwort ebenfalls. – kJamesy

Antwort

38

Das war viel mehr Spaß als meine üblichen Morgenkreuzworträtsel. :)

Hier ist eine ItemsHelper-Klasse, die tun wird, was Sie suchen, und noch besser wird recurse so weit wie Sie wollen.

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

habe ich gerade eine neue Installation von Laravel 4 und die Grund hello.php Ansicht.

Hier ist meine Route in app/routes.php:

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

Obwohl meine Ansicht Variable nicht die Elemente verwenden, ich bin es hier vorbei, weil Sie wahrscheinlich auch etwas anderes mit ihnen tun wollen.

Und schließlich meine app/views/hello.php hat nur eine Zeile:

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

Die Ausgabe sieht wie folgt aus:

  • Foods
    • Obst
      • Apfel
      • Banana
      • orange
    • Veg
      • Cucumber
      • Salat
  • Getränke
    • Alkoholisches
      • Bier
      • Wodka
  • Misc
    • Haushalt Objekte
      • Küche
        • Elektrische
          • Kochen
            • Herd
            • Toaster
            • Mikrowelle

Hinweis: Ihre SQL Fiddle hatte 5 ("Orange") als Parent_id für Gurke und Salat, ich musste es auf 6 ("Veg") ändern.

+1

Fantastisch! Vielen Dank dafür :) – Sk446

+2

Sie sind herzlich willkommen! Froh, dass ich helfen könnte. –

+1

@MarkSmith - große Antwort danke! Sie haben nur Mühe zu verstehen, wie Sie dem Array zusätzliche Werte hinzufügen würden? Oder fügen Sie sogar benannte Werte hinzu? Zum Beispiel: '{ id: '2', Name: 'Elter 2', Kinder: [ { id: '5', Name: 'Sub 2a' }, { id: '6', Name: 'Sub 2b' } ] } ' – Fraccus

1

Ich benutze diese Funktionen, damit es funktioniert.

//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; 
     } 
    } 

Zuerst bekomme ich eine Sammlung der Wurzelelemente sind diejenigen, die ich meine Ansichten passieren, wo ich den Baum auflisten möchten ...

Dann mache ich ...

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

Mit dieser Funktion ...

//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>"; 
     } 
    } 
} 

Output

Ich hoffe, es hilft

1

Ich habe die akzeptierte Antwort von Mark Smith erweitert, um generierte Listen zu ermöglichen, auf Additionsdaten zu verweisen, die in die Klasse übergeben werden.

Die Klasse funktioniert auf die gleiche Weise, aber ich habe es verpackt, also hoffentlich kann es leicht verwendet werden.

Referenz einfach die Hilfsklasse in Ihrem Controller:

use App\Helpers\CategoryHierarchy; 

können Sie dann entweder instanziieren die Klasse manuell oder mit der Methode des Laravel 5 Injektion Dinge noch besser:

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

Dies kann Ausgang das Folgende:

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

Ein Repo ist verfügbar: https://github.com/drawmyattention/CategoryHierarchy, die etwas ausführlicher erklärt.