Я создаю веб-сайт, который содержит базу данных продуктов. Каждый товар относится к категории. Структура категорий является многоуровневой и может содержать любое количество уровней, например:
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.
Что я хочу сделать, это:
Shirts
это было бы Fashion
Mens
это было бы Long Sleeved
а также Short Sleeved
,У меня есть следующие стандартные отношения в моей модели:
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;
}
Но здесь много повторений, и это выглядит ужасно. Но это также заставляет меня поверить, что, возможно, я выбрал неправильный подход и должен реструктурировать саму базу данных?
Билл Я думаю, что я решил эту проблему в 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;
переменная в вашем классе модели. и теперь вызовите функцию, как показано ниже.
self::getSubCategories(NULL)
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;
переменная в вашем классе модели. и теперь вызовите функцию, как показано ниже.
Вы можете использовать собственное имя класса вместо self
Надеюсь это поможет.
Других решений пока нет …