我正在使用codeigniter并拥有一个包含3列(id,name,parent_id)的表。类别可以有许多子类别,子类别可以有许多子子类别。
我一直在尝试使用此代码获取所有类别及其子类别:
public function getCategory($id)
{
$categories = array();
while($id != 0)
{
$this->db->from('categories'); //$this->table is a field with the table of categoris
$this->db->where('id', $id);
$this->db->limit(1);
$result = $this->db->get()->row(); //fetching the result
$categories[] = $result;
$id = $result->parent_id;
}
return $categories;
}
public function getAllCategories()
{
$this->db->select('id');
$this->db->from('categories'); //$this->table is a field with the table of categoris
$this->db->where('parent_id', 0);
$mainCategories = $this->db->get()->result(); //fetching the result
$result = array();
foreach($mainCategories as $id)
{
$result[] = $this->getCategory($id->id);
}
return $result;
}
但它只返回1级别。
我的问题是如何完成我的任务:获取每个级别的所有类别和子类别。
答案 0 :(得分:20)
解决问题的最简单方法是添加递归。
public function getCategoryTreeForParentId($parent_id = 0) {
$categories = array();
$this->db->from('categories');
$this->db->where('parent_id', $parent_id);
$result = $this->db->get()->result();
foreach ($result as $mainCategory) {
$category = array();
$category['id'] = $mainCategory->id;
$category['name'] = $mainCategory->name;
$category['parent_id'] = $mainCategory->parent_id;
$category['sub_categories'] = $this->getCategoryTreeForParentId($category['id']);
$categories[$mainCategory->id] = $category;
}
return $categories;
}
通过预加载所有类别并对数组进行操作,可以大大提高此方法,从而跳过每个新parent_id的查询。
我也没有使用codeigniter的对象,因为我不知道它们。如果它们支持魔术设置器/ getter,或者可以说服它们采用一系列子对象,那么应该使用它们来代替我在这里构建的数组。
算法的作用是:使用给定的parent_id加载所有类别,遍历所有这些类别,将iteration-category-id假设为parent_id并为其加载所有内容。 这有效地加载所有类别,只要它们引用现有类别。
涉及到一个轻微的危险:当您构建类别A和B时,其中A将B作为父级,B将A作为父级,您将遇到无限循环,因为它们会一次又一次地相互加载。这会强制您在数据中使用干净的树结构。
<强>更新强>
因为这仍然得到了回报: 有关此实施的另一个建议。 如果您的类别树更大或具有多个级别,则可能会遇到性能问题,因为此实现会使用新的查询参数一次又一次地加载类别。这很容易导致数十,数百或数千个查询,具体取决于您的类别树。
一种更有效的方法是从类别表中加载所有类别(使用一个查询),并在应用程序中使用递归对它们进行排序。这是极少数情况之一,早期评估确实可以提高绩效。
如果在同一个请求中需要多次树,那么甚至可以通过静态变量添加缓存(具有缓存的所有常见危险)。
答案 1 :(得分:3)
这是一个codeigniter库,我在stackoverflow中的答案中找到它,但我不记得它的所有者。我对它进行了一些修改,将嵌套类别作为列表返回。它的作用非常好。
<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class Tree {
var $table = '';
var $CI = '';
var $ul_class = '';
var $iteration_number = 0;
function Tree($config = array()) {
$this -> table = $config['table'];
$this -> CI = &get_instance();
$this -> CI -> load -> database();
$this -> ul_class = $config['ul_class'];
}
function getTree($parent_id = 1) {
$this -> iteration_number++;
$this -> CI -> db -> where('parent_id', $parent_id);
$first_level = $this -> CI -> db -> get($this -> table) -> result();
if($this->iteration_number == 1)
$tree = '<ul id="red" class="' . $this -> ul_class . '">';
else
$tree = '<ul>';
foreach ($first_level as $fl) {
$this -> CI -> db -> where('parent_id', $fl -> category_id);
$count = $this -> CI -> db -> count_all_results($this -> table);
if ($count != 0) {
$tree .= '<li><span>' . $fl -> category_name . '</span>';
$tree .= $this -> getTree($fl -> category_id);
} else {
$tree .= '<li><a href="'.base_url().'categories/sub/'.str_replace(' ', '-', $fl -> category_name).'" cat_id="'.$fl -> category_id.'" class="leaf">' . $fl -> category_name . '</a>';
}
$tree .= '</li>';
}
$tree .= '</ul>';
return $tree;
}
}
?>
您可以在控制器中使用它,如下所示:
$config['ul_class']='treeview';
$config['table']='categories';
$this->load->library('tree',$config);
$tree = $this->tree->getTree();
和DB表的结构如下:
CREATE TABLE IF NOT EXISTS `categories` (
`category_id` int(4) NOT NULL AUTO_INCREMENT,
`category_name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`category_description` varchar(500) COLLATE utf8_unicode_ci NOT NULL,
`parent_id` int(4) NOT NULL,
PRIMARY KEY (`category_id`),
KEY `categories_categories_fk_idx` (`parent_id`)
) ENGINE=InnoDB
您可以通过库中的getTree函数修改返回的结果,以返回您需要的内容。
答案 2 :(得分:2)
我将提供一个不同的解决方案,虽然你需要做一些轻微的重组:
使用沿袭风格的层次结构,可以避免所有的递归问题。有更快的数据拉动。您使用完整的祖先列表标记每个类别,例如:0001-0005-0015 ...等,然后可以使用LIKE运行简单查询以获取子查询。
我在git&amp; amp;上有一个Codeigniter库。博客文章解释这一切是如何运作的:http://codebyjeff.com/blog/2012/10/nested-data-with-mahana-hierarchy-library
答案 3 :(得分:1)
假设您的顶级类别的parent_id = 0。 您可以使用以下代码从DB获取数据,并创建一个二维数组
$this->db->from('categories');
$query = $this->db->get();
$rows = $query->result()) {
// ok, we have an array ($rows). We need it
// transformed into a tree
$categories = array();
foreach ($rows as $category) {
// add the category into parent's array
$categories[$category->parent_id][] = $category;
}
return $categories;
然后你可以遍历$ categories数组,因为$ categories [0]是一个包含顶级类别的数组,$ categories [$ i]持有类别(或子类别)的子类别,其中id = $我。