Laravel 4 - Красноречивый. Бесконечные дети в полезный массив?
У меня есть таблица категорий. Каждая категория может иметь необязательный родительский (по умолчанию 0, если нет родителя).
Что я хочу сделать, это построить простое дерево списка html с уровнями категорий.
Пример даты:
Foods
-- Fruit
---- Apple
---- Banana
---- Orange
-- Veg
---- Cucumber
---- Lettuce
Drinks
-- Alcoholic
---- Beer
---- Vodka
Misc
-- Household Objects
---- Kitchen
------ Electrical
-------- Cooking
---------- Stove
---------- Toaster
---------- Microwave
Обратите внимание, что это должно работать около 10 уровней. Я бы хотел, чтобы это было бесконечно, но я действительно не хочу идти по пути использования вложенной модели набора, поскольку это вызовет огромные задержки в этом проекте.
Документы по этому поводу для laravel ужасны, без реальной ссылки на то, где даже начать. Я играю с ним в течение нескольких дней, пытаясь понять, что делать, и, кажется, никуда не денется без огромного беспорядочного блока для каждой петли внутри друг друга 10 раз.
У меня есть мое дерево данных, используя в моей модели следующее:
<?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();
}
}
Это дает всем родителям и детям до уровня 10. Это отлично работает, но вы не можете делать что-либо с дочерними данными, не имея вручную 10 петель для каждого из них.
Что я здесь делаю неправильно? Неужели это не должно быть так сложно/плохо выполнено? Все, что я хочу сделать, это получить простой список html с элементами в древовидной структуре.
Я собрал быстрый пример SQLFiddle из фиктивных данных, используемых выше: http://sqlfiddle.com/#!2/e6d18/1
Ответы
Ответ 1
Это было намного веселее, чем моя обычная утренняя кроссворд.:)
Вот класс ItemsHelper, который будет делать то, что вы ищете, и еще лучше будет записаться так далеко, как вы хотите.
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;
}
}
Я только что использовал новую установку Laravel 4 и основного представления hello.php
.
Вот мой маршрут в app/routes.php
:
Route::get('/', function()
{
$items = Item::all();
$itemsHelper = new ItemsHelper($items);
return View::make('hello',compact('items','itemsHelper'));
});
Хотя мое представление не использует переменную items, я передаю ее здесь, потому что вы, вероятно, захотите сделать что-то еще с ними.
И наконец, мой app/views/hello.php
имеет только одну строку:
<?= $itemsHelper->htmlList(); ?>
Результат выглядит следующим образом:
Примечание. у вашего SQL Fiddle было 5 ( "Оранжевый" ) в качестве parent_id для огурца и латука, мне пришлось изменить его на 6 ( "Veg" ).
Ответ 2
Я использую эти функции, чтобы заставить его работать.
//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;
}
}
Сначала я получаю коллекцию корневых элементов, которые я передаю своим представлениям, где я хочу отобразить дерево...
Тогда я...
<ul class="bg-info cat-list">
@foreach($categories as $category)
<?php
$array = $category->traverse();
list_view($array);
?>
@endforeach
</ul>
С помощью этой функции...
//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]()
Надеюсь, что это поможет
Ответ 3
Я расширил принятый ответ Марка Смита, чтобы разрешить сгенерированные списки ссылаться на данные сложения, которые передаются в класс.
Класс работает практически так же, но я упаковал его так, надеюсь, его можно легко использовать.
Просто укажите вспомогательный класс в контроллере:
use App\Helpers\CategoryHierarchy;
Затем вы можете либо создать экземпляр класса вручную, либо использовать метод Laravel 5 для инъекций:
$products = $product->getAllProducts();
$hierarchy->setupItems($products);
return $hierarchy->render();
Это может выводить следующее:
<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>
Доступно репо: https://github.com/drawmyattention/CategoryHierarchy, что объясняется более подробно.