MySQL / PHP - 多层类别结构

Posted

技术标签:

【中文标题】MySQL / PHP - 多层类别结构【英文标题】:MySQL / PHP - Multi tier category structure 【发布时间】:2016-04-04 20:22:39 【问题描述】:

我正在构建一个包含产品数据库的网站。每个产品都属于一个类别。类别的结构是多层的,可以包含任意数量的层,例如:

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 框架,或者至少是那些实现像 ActiveRecord 这样的 ORM 的框架。

我想做的是:

    对于任何类别级别,获取“主”父级。 IE。对于Shirts,它将是Fashion 对于任何类别级别,获取该层中所有“最后”级别的类别。 IE。对于Mens,它将是Long SleevedShort 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;

但是这里有很多重复,看起来很难看。但这也让我相信也许我采取了错误的方法并且需要重组数据库本身?

【问题讨论】:

你在parent_id 的正确轨道上,你可能会在网上找到一个树生成器,或者你可以让你的函数递归。即,检查当前元素是否有父元素,将父元素添加到数组中并在父元素上运行相同的函数(这将一直迭代到树上)。 mikehillyer.com/articles/managing-hierarchical-data-in-mysql parent_id 是将数据存储在类别表中的理想方式,但如果您需要避免递归,您将希望索引类别路径以执行您正在执行的操作。您可以将此信息存储在 Yii 的缓存中,或者,如果您需要 SQL 查询中可用的路径索引,您可以创建一个存储类别路径的表,并随着树结构的变化更新此表。 【参考方案1】:

Bill 我想我已经在 YII2 -> Models 中解决了这个问题。

下面是我的代码。

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::getParentCategories(16, 0)的所有子类 让所有孩子升到第二级 self::getParentCategories(16, 2)

你可以使用自己的类名代替self

希望这会有所帮助。

【讨论】:

以上是关于MySQL / PHP - 多层类别结构的主要内容,如果未能解决你的问题,请参考以下文章

使用php在单个mysql数据库表中的表中显示子类别及其父类别

如何在 MySQL 和 PHP 中显示所有产品的所有类别?

在 PHP 和 MySQL 中使用联结表来分类和包含和排除类别

PHP MySQL搜索/类别过滤器

PHP Wordpress类别层次结构

选择每个类别的最后 3 条新闻 - 两个表 - (MySQL - PHP)