0

親と子を持つMySQLデータベースにカテゴリシステムがあります。データベースは、直接の親の ID (またはルートの場合は 0) のみを保存します。システムでは複数のサブカテゴリが許可されているため、複数の子の場合があります。

例えば

[98] Storage
    [1] External
        [3] Pendrives
        [4] Portable hhdds
    [2] Internal
        [5] Sata hhdd
        [6] IDE hhdd
[...]
[99] Clothing

データベースは

id    parent_id    name
1     98           External
2     98           Internal
3     1            Pendrives
4     1            Portable
5     2            Sata
6     2            IDE
98    0            Storage
99    0            Clothing

カテゴリ ID を持つ製品テーブルもあり、カテゴリの最初のレベルにあるすべての製品のリストを取得する必要があります。

例えば:

Product   Category
 A          3
 B          4
 C          5
 D          6
 E          74

98 を返す必要があります: A、B、C、D 99: X、Y、Z...

私は立ち往生しており、その方法でそれを取得するロジックが思いつきません。

最初のレベルにないすべてのカテゴリの ID を取得することから始めました。

while ($row = mysql_fetch_assoc($result)) {
    if ($row['parent_id'] != 0) {
        $level1[$i]['name'] = utf8_encode($row['categories_name']);
        $level1[$i]['id'] = $row['categories_id'];
    }

    $i++;
}

しかし、私は燃え尽きており、それらをネストする方法が思いつきません。しばらく考えましたが、それは無限です:P

アイデアはありますか?

4

4 に答える 4

1

再帰関数を使用して、すべての子カテゴリを取得します。

//fetch category structure data
$childCategories = array();
$result = mysql_query("SELECT id, parent_id FROM categories");
while ($row = mysql_fetch_assoc($result)) {
    if ($row['parent_id']) {
        if (!isset($childCategories[$row['parent_id']])) $childCategories[$row['parent_id']] = array();
        $childCategories[$row['parent_id']][] = $row['id'];
    }
}

function getRecursiveCategories($id, $childCategories) {
    $ret = array();
    if (!isset($childCategories[$id])) return $ret;
    foreach ($childCategories[$id] as $childId) {
        $ret[] = $childId;
        $ret = array_merge($ret, getRecursiveCategories($childId, $childCategories));
    }
    return $ret;
}

$cateoryIds = getRecursiveCategories(98, $childCategories);

//now you can query the products
"SELECT * FROM products WHERE category_id IN (".implode(',', $cateoryIds).")";
于 2012-10-11T19:55:04.490 に答える
1

再帰関数呼び出しはどうですか?例えば:

$allItems = Array();
function getAllItems($category) {
    global $allItems;
    $query = mysql_query("SELECT `id` FROM `categories` WHERE `parent_id` = '".$category."' ");

    if(mysql_num_rows($query)) {
        while($cat = mysql_fetch_assoc($query)) {
            $itemQuery = mysql_query("SELECT * FROM `items` WHERE `category` = '".$cat['id']."' ");
            if(mysql_num_rows($itemQuery)) {
                while($item = mysql_fetch_assoc($itemQuery)) {
                    $allItems[$item['id']] = $item; //using $item['id'] to avoid duplicating items if they are in several categories
                }
            }
            $checkSubCatQuery = "SELECT `id` FROM `categories` WHERE `parent_id` = '".$cat['id']."' LIMIT 1");
            if(mysql_num_rows($checkSubCatQuery)) { //Checking to see if we have a sub category
                getAllItems($cat['id']); //Calling agait to get all items from sub cattegory of the current category
            }
        }
    }
}

もちろん、いつでも最適化できます。

于 2012-10-11T19:56:33.143 に答える
0

以下の手順に従うと、うまくいくはずです (効率のためにアルゴリズムを改善することができます)。

  • 最初に、初期カテゴリ ID に基づいて選択を行います。SELECT id, parent_id FROM categories結果を次のような配列に保存するようなもの:

    $catArr = array(
       array(1,98),
       array(2,98),
       ...
      );
    
  • これを次の関数に対して実行します (注: $start=98)

    function getSubsets($catArr, $start) {
       $rtn = array();
       foreach($catArr as $cat) {
          if($cat[1] == $start) { 
             $rtn[] = $cat[0];
             $subcats = getSubsets($catArr,$cat[0]);
             foreach($subcats as $subcat) { $rtn[] = $subcat; }
          }
       }
       return $rtn;
    }
    
    note: never have a category be its own parent or any other similar 
    conditions that would cause an infinite loop.
    
  • これで、最終的な SQL を次のように生成できます

     $sql = "SELECT * FROM products WHERE category IN (".implode(',', $rtn).")"; 
     Note: this assumes category is a number
    

最後の注意: SQL クエリを 2 つだけ実行します :)

于 2012-10-11T21:34:20.920 に答える
0

これを正しく実行し、mysql で優れたパフォーマンスを発揮する方法についての完璧な記事を1 つ知っています (このアイデアを信用することはできません。チェコ語から翻訳しているだけです)。

データベースのレイアウト

次のようなツリー構造が必要です。

categories (
    id,
    lft,  -- index on "left iteration" - number on the left
    rgt,  -- index on "right iteration" - number on the right
    depth,
)

完全なツリーを表示する方法:

$result = mysql_query("SELECT * FROM categories ORDER BY lft");
while ($row = mysql_fetch_assoc($result)) {
    echo str_repeat("- ", $row["depth"]) . htmlspecialchars($row["data"]) . "<br />";
}
mysql_free_result($result);

ツリー全体を印刷する必要がある場合は、ソートしてlft書き留めるだけで十分です。ツリーの一部だけを表示する必要がある場合は、両方lftを制限してrft、現在のノード値より大きくすることができます。

番号付きリストで表示:

$result = mysql_query("SELECT * FROM categories ORDER BY lft");
$depth = -1;
while ($row = mysql_fetch_assoc($result)) {
    if ($depth < $row["depth"]) {
        echo "<ul>";
    } else {
        echo str_repeat("</li></ul>", $depth - $row["depth"]) . "</li>";
    }
    echo "<li>\n" . htmlspecialchars($row["data"]);
    $depth = $row["depth"];
}
echo str_repeat("</li></ul>", $depth + 1) . "\n";
mysql_free_result($result);

順序付きリスト (複数レベル) で表示する場合、現在のノードdepthと次のアイテムの間の接続を検出する必要があります。

「ブレッドクラム ナビゲーション」用に取得する:

$row = mysql_fetch_assoc(mysql_query("SELECT * FROM categories WHERE id = " . intval($_GET["id"])));
$result1 = mysql_query("SELECT * FROM categories WHERE lft < $row[lft] AND rgt > $row[rgt] ORDER BY lft");
while ($row1 = mysql_fetch_assoc($result1)) {
    echo "<a href='?id=$row1[id]'>" . htmlspecialchars($row1["data"]) . "</a> &gt; ";
}
mysql_free_result($result1);
echo htmlspecialchars($row["data"]);

リストの最後に子を追加します。

mysql_query("INSERT INTO categories (lft, rgt, depth, data)
    SELECT IFNULL(MAX(rgt), 0) + 1, IFNULL(MAX(rgt), 0) + 2, 0, '" .         
    mysql_real_escape_string($_POST["data"]) . "' FROM categories");

必要な場所に子ノードを追加する

mysql_query("START TRANSACTION");
$row = mysql_fetch_assoc(mysql_query("SELECT * FROM categories WHERE id = " . intval($_GET["rodic"]) . " FOR UPDATE"));
mysql_query("UPDATE categories SET lft = lft + 2 WHERE lft > $row[rgt]");
mysql_query("UPDATE categories SET rgt = rgt + 2 WHERE rgt >= $row[rgt]");
mysql_query("INSERT INTO categories (lft, rgt, depth, data) VALUES ($row[rgt], $row[rgt]+1, $row[depth]+1, '" . mysql_real_escape_string($_POST["data"]) . "')");
mysql_query("COMMIT");

完全な子を削除

mysql_query("START TRANSACTION");
$row = mysql_fetch_assoc(mysql_query("SELECT * FROM categories WHERE id = " . intval($_GET["id"]) . " FOR UPDATE"));
mysql_query("DELETE FROM categories WHERE lft >= $row[lft] AND rgt <= $row[rgt]");
$rozdil = $row["rgt"] - $row["lft"] + 1;
mysql_query("UPDATE categories SET lft = lft - $rozdil WHERE lft > $row[rgt]");
mysql_query("UPDATE categories SET rgt = rgt - $rozdil WHERE rgt > $row[rgt]");
mysql_query("COMMIT");

このアルゴリズムの詳細(または名前)を知っている場合は、お知らせください

これは PDO を使用して記述することをお勧めします。

于 2012-10-11T19:47:44.087 に答える