MySQL / Многоуровневая структура категорий

Я создаю веб-сайт, который содержит базу данных продуктов. Каждый товар относится к категории. Структура категорий является многоуровневой и может содержать любое количество уровней, например:

  • Electronics > Games Consoles > Xbox > Xbox One > Games > etc..
  • Fashion > Mens > Shirts > Long Sleeved

Я всегда отношу товар к «последней» категории на уровне.

Вот структура моей таблицы категорий:

id       name            parent_id
================================
1        Fashion         NULL
2        Mens            1
3        Shirts          2
4        Long Sleeved    3
5        Short Sleeved   3

Я использую Yii2 в качестве своей прикладной среды, но те же концепции должны применяться к большинству MVC Frameworks или, по крайней мере, к тем, которые реализуют ORM, например ActiveRecord.

Что я хочу сделать, это:

  1. Для любого уровня категории, получить «мастер» родителя. То есть за Shirts это было бы Fashion
  2. Для любого уровня категории, получить все категории «последнего» уровня в уровне. То есть за Mens это было бы Long Sleeved а также Short Sleeved,
  3. (более продвинутый) Для любого уровня категории выясните, сколько у него детей / родителей.

У меня есть следующие стандартные отношения в моей модели:

public function getParent()
{
return $this->hasOne(Category::className(), ['id' => 'parent_id']);
}

public function getParent()
{
return $this->hasMany(Category::className(), ['parent_id' => 'id']);
}

Следующее является функцией, которую я создал, которая выводит «дерево» для любой данной категории:

public function getParentTree()
{
$array = [];

// $this->parent refers to the 'getParent()' relation above
if(!empty($this->parent))
{
$array[] = $this->parent->name;

if(!empty($this->parent->parent))
$array[] = $this->parent->parent->name;

if(!empty($this->parent->parent->parent))
$array[] = $this->parent->parent->parent->name;
}
else
$array[] = "(none)";

$output = implode(" --> ", array_reverse($array));

return $output;
}

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

2

Решение

Билл Я думаю, что я решил эту проблему в YII2 -> Модели.

Ниже мой код.

public static function getSubCategories($parent_id = NULL, $level = 0)
{
// Get the Category from table
// Here you can use caching Yii::$app->cache->get to avoid multiple queries
$categories = Category::find()->select(['id', 'parent_id', 'name'])->where(['parent_id' => $parent_id])->asArray()->all();

// Logic of Nth level to return
self::$max_down_level += 1;
if($level != 0 && self::$max_down_level > $level) return $categories;

// Now for each sub categories find and return chidren as Array
foreach($categories as $key => $category)
{
$categories[$key]['children'] = self::getSubCategories($category['id'], $level);
}

return $categories;
}

И не забудьте объявить public static $max_down_level = 0; переменная в вашем классе модели. и теперь вызовите функцию, как показано ниже.

  1. Получить всех детей из родительской категории self::getSubCategories(NULL)
  2. Чтобы все дети до 2-го уровня self::getSubCategories(NULL, 2)

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

public static function getParentCategories($parent_id, $level = 0)
{
// Get the Category from table
// Here you can use caching Yii::$app->cache->get to avoid multiple queries
$categories = Category::find()->select(['id', 'parent_id', 'name'])->where(['id' => $parent_id])->asArray()->all();

// Logic of Nth level to return
self::$max_up_level += 1;
if($level != 0 && self::$max_up_level > $level) return $categories;

foreach($categories as $key => $category)
{
$categories[$key]['parent'] = self::getParentCategories($category['parent_id'], $level);
}

return $categories;
}

И не забудьте объявить public static $max_up_level = 0; переменная в вашем классе модели. и теперь вызовите функцию, как показано ниже.

  1. Чтобы получить все дети родительской категории self :: getParentCategories (16, 0)
  2. Чтобы получить всех детей до 2-го уровня self :: getParentCategories (16, 2)

Вы можете использовать собственное имя класса вместо self

Надеюсь это поможет.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]