6

私は 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 レベルのカテゴリのみを返します。

私の質問は、私のタスクを達成する方法です。すべてのレベルのすべてのカテゴリとサブカテゴリを取得します。

4

4 に答える 4

25

問題の最も簡単な解決策は、再帰を追加することです。

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 のオブジェクトを使用しませんでした。それらが魔法のセッター/ゲッターをサポートしている場合、または子オブジェクトの配列を取得できると確信できる場合は、ここで構築する配列の代わりにそれらを使用する必要があります。

アルゴリズムが行うことは次のとおりです。指定されたparent_idを持つすべてのカテゴリをロードし、それらすべてのカテゴリをループし、iteration-category-idをparent_idと見なして、そのすべてをロードします。これにより、既存のカテゴリを参照している限り、すべてのカテゴリが効果的に読み込まれます。

わずかな危険が伴います。A が B を親として、B が A を親として持つカテゴリ A および B の構成がある場合、互いに何度もロードし合うため、無限ループに陥ります。これにより、データにクリーンなツリー構造が必要になります。

アップデート

これはまだ支持を得ているため: この実装に関する別のアドバイスがあります。カテゴリ ツリーが大きい場合や複数のレベルがある場合、この実装では新しいクエリ パラメータを使用してカテゴリが何度も読み込まれるため、パフォーマンスの問題が発生する可能性があります。これにより、カテゴリ ツリーによっては、数十、数百、または数千のクエリが簡単に発生する可能性があります。

より効率的な方法は、カテゴリ テーブルからすべてのカテゴリを (1 つのクエリで) ロードし、アプリケーション内で再帰的に並べ替えることです。これは、早期評価によってパフォーマンスが向上するまれなケースの 1 つです。

ツリーが同じリクエスト内で複数回必要な場合は、静的変数を介してキャッシュを追加することもできます (キャッシュのすべての通常の危険を伴います)。

于 2013-04-06T03:43:35.370 に答える
3

これは、stackoverflow の回答で見つけた codeigniter ライブラリですが、その所有者を覚えていません。ネストされたカテゴリをリストとして返すように、いくつかの変更を加えました。それは私と非常にうまく機能します。

<?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 関数によって返された結果を変更して、必要なものを返すことができます。

于 2013-04-08T15:35:38.913 に答える
2

少し再構築する必要がありますが、別の解決策を提案します。

リネージ スタイルの階層を使用すると、すべての再帰の問題を回避し、データ プルを大幅に高速化できます。0001-0005-0015 などの祖先の完全なリストで各カテゴリをマークし、LIKE を使用して単純なクエリを実行してサブクエリを取得できます。

git に Codeigniter ライブラリがあり、すべての仕組みを説明するブログ投稿があります。

于 2013-04-06T05:56:51.433 に答える
1

トップレベルのカテゴリにparent_id = 0があると仮定します。次のコードでDBからデータを取得し、2次元配列を作成できます

$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[0] がトップ レベルのカテゴリを保持する配列であり、$categories[$i] が id = $i のカテゴリ (またはサブカテゴリ) のサブカテゴリを保持している場合、$categories 配列をトラバースできます。

于 2013-04-09T18:33:52.750 に答える