Подтвердить что ты не робот

Получить все предметы категории и ее ребенка

Я собираюсь дать 100 бонусных баллов за ответ на этот вопрос

Итак, у меня очень сложный вопрос о рекурсиях - как получить все элементы, подсчет категории и всех дочерних элементов, которые содержат этот родительский элемент и более глубокие до конца?

У меня есть таблица:

+----+---------------+-----------------+
| id | category name | category_parent |
+----+---------------+-----------------+
| 1  | cars          |        0        |
+----+---------------+-----------------+
| 2  | real estate   |        0        |
+----+---------------+-----------------+
| 3  | clothes       |        0        |
+----+---------------+-----------------+
| 4  | bmw           |        1        |
+----+---------------+-----------------+
| 5  | audi          |        1        |
+----+---------------+-----------------+
| 6  | 100           |        5        |
+----+---------------+-----------------+
| 7  | 80            |        5        |
+----+---------------+-----------------+
| 8  | A4            |        5        |
+----+---------------+-----------------+
| 9  | QUATRO        |        8        |
+----+---------------+-----------------+
| 10 | TDI           |        8        |
+----+---------------+-----------------+
| 11 | Black         |        9        |
+----+---------------+-----------------+
| 12 | White         |        9        |
+----+---------------+-----------------+
| 13 | 2 doors       |        11       |
+----+---------------+-----------------+
| 14 | 5 doors       |        11       |
+----+---------------+-----------------+

а таблица моих продуктов выглядит так:

+----+---------------+-----------------+
| id | category_id   | name            |
+----+---------------+-----------------+

и, например, я хочу считать все элементы, находящиеся в категории cars. Поэтому в основном я должен передать эту категорию id (1) и как-то сделать рекурсию для подсчета всех элементов. Но я понятия не имею, как с этим бороться, потому что дети этой категории могут быть неограниченными.

Поэтому, когда я хочу знать все элементы этого родительского счета, я должен сделать что-то вроде этого:

1++:
  4++
  5++:
    6++
    7++
    8++:
      9++:
        11++:
           13++
           14++
        12++
      10++

Надеюсь, вы поймете, что мне нужно, и дайте мне какое-нибудь предложение, которое могло бы мне помочь.

тоже, это начало, которое я сделал до сих пор - я мог бы его реализовать, но в будущем я застрял в рекурсии... так что ничего не стоит.

public function get_category_tree_id_list($cat_id, $list_array = FALSE)
{
    if ( !$list_array ){
        $items = $this->system->_getCustomTableData('categories', array(array('category_parent' => $cat_id)), 'id DESC');
        $this->__tmp['id_list'] = [];
        foreach ( $items as $key => $value ) {
            $this->__tmp['id_list'][] = $value['id'];
        }
    }        
}
4b9b3361

Ответ 1

Скорее всего, вы захотите сделать вложенные наборы. Они немного сложны в настройке, но делают запросы намного проще. Итак, вместо родительской категории у вас будет два столбца - lft и rgt. Влево и вправо - это в основном границы категории, если между этими значениями находится идентификатор категории предметов, вы знаете, что он является дочерним элементом этой категории.

+----+---------------+-----+------+
| id | category name | lft | rgt  |
+----+---------------+-----+------+
| 1  | cars          |  1  |  24  |
+----+---------------+-----+------+
| 2  | bmw           |  2  |  3   |
+----+---------------+-----+------+
| 5  | audi          |  4  | 23   |
+----+---------------+-----+------+
| 6  | 100           |  5  |  6   |
+----+---------------+-----+------+
| 7  | 80            |  7  |  8   |
+----+---------------+-----+------+
| 8  | A4            |  9  | 22   |
+----+---------------+-----+------+
| 9  | TDI           |  10 | 11   |
+----+---------------+-----+------+
| 10 | Quatro        |  12 | 21   |
+----+---------------+-----+------+
| 11 | Black         |  13 | 18   |
+----+---------------+-----+------+
| 12 | White         |  19 |  20  |
+----+---------------+-----+------+
| 13 | 2 doors       |  14 |  15  |
+----+---------------+-----+------+
| 14 | 5 doors       |  16 | 17   |
+----+---------------+-----+------+

Затем, чтобы получить количество элементов в категории автомобилей, вы можете сделать это просто так:

SELECT categories.name, items.id, items.category_id, items.name 
FROM categories 
LEFT JOIN items 
    ON (items.category_id BETWEEN categories.lft AND categories.rgt)
WHERE categories.category_name = 'cars'

Очевидно, вы можете просто изменить значение category_name и получить элементы в ЛЮБОЙ категории.

Извините, по какой-то причине изображение повернулось, когда я загрузил его здесь, но если вы выделите свои категории в виде кругов и затем пронумеруете строки, вы увидите, какое значение должно быть для левого и правого.

Я только делал автомобили, так как решил, что вы можете экстраполировать, чтобы получить другие категории.

введите описание изображения здесь

Итак, если вы выписываете свои категории следующим образом:

Cars(BMW(), Audi(100(),80(),A4(TDI(),Quatro(Black(2dr(),5dr()), White())))

Затем вы можете пометить свою скобку цифрами:

Cars[1]->(BMW[2]->()<-[3], Audi[4]->(100[5]->()<-[6],80[7]->()<-[8],A4[9]->(TDI[10]->()<-[11],Quatro[12]->(Black[13]->(2dr[14]->()<-[15], 5dr[16]->()<-[17])<-[18], White[19]->()<-[20])<-[21])<-[22])<-[23])<-[24]

Или, если вы нарисуете его как дерево, вы можете пометить его так, как показано на рисунке, где вы помечаете левое большинство node номером и назовите только node, когда вы пометили все его дочерние элементы:

введите описание изображения здесь

Ответ 2

У меня есть новая идея, я думаю, это будет хорошо. Идея такова: в столбце category_parent мы добавим ссылку на всех родителей этого node.

+----+---------------+-----------------+
| id | category name |    hierarchy    |
+----+---------------+-----------------+
| 1  | cars          |        1        |
+----+---------------+-----------------+
| 2  | real estate   |        2        |
+----+---------------+-----------------+
| 3  | clothes       |        3        |
+----+---------------+-----------------+
| 4  | bmw           |       1-4       |
+----+---------------+-----------------+
| 5  | audi          |       1-5       |
+----+---------------+-----------------+
| 6  | 100           |      1-4-6      |
+----+---------------+-----------------+
| 7  | 80            |      1-4-7      |
+----+---------------+-----------------+
| 8  | A4            |      1-4-8      |
+----+---------------+-----------------+
| 9  | QUATRO        |     1-4-8-9     |
+----+---------------+-----------------+
| 10 | TDI           |     1-4-8-10    |
+----+---------------+-----------------+
| 11 | Black         |    1-4-8-9-11   |
+----+---------------+-----------------+
| 12 | White         |   1-4-8-9-12    |
+----+---------------+-----------------+
| 13 | 2 doors       |  1-4-8-9-11-13  |
+----+---------------+-----------------+
| 14 | 5 doors       |  1-4-8-9-11-14  |
+----+---------------+-----------------+

если вы посмотрите на мою обновленную таблицу, вы заметите, что в каждой записи есть ссылка на своих родителей, а не только на прямую, но и на всех родителей. И для этой работы я сделал некоторые изменения для вставки:

Insert into table_name (category_name, hierarchy) values ('new_name', (concat(parent_hierarch, '-', (SELECT Auto_increment FROM information_schema.tables WHERE table_name='table_name'))))

Теперь давайте сделаем желаемые запросы:

1- все подкатегории автомобилей:

select * from table_name where hierarchy like '1-%'

2- если вам нужен все родительский элемент BLACK, просто введите:

select * from table_name where hierarchy = '1-4-8-9' or hierarchy = '1-4-8' or hierarchy = '1-4' or hierarchy = '1'

(вы можете построить этот запрос из php, разбивая поле иерархии на '-' char)

3- Чтобы просмотреть все категории с уровнем и прямым родителем:

select *, SUBSTR(hierarchy, 1, (LENGTH(hierarchy) - LENGTH(id) - 1)) as parent, LENGTH(hierarchy) - LENGTH(REPLACE(hierarchy, '-', '')) as level From table_name
+----+---------------+-----------------+-----------+--------+
| id | category name |    hierarchy    |   parent  |  level |
+----+---------------+-----------------+-----------+--------+
| 1  | cars          |        1        |           |    0   |
+----+---------------+-----------------+-----------+--------+
| 2  | real estate   |        2        |           |    0   |
+----+---------------+-----------------+-----------+--------+
| 3  | clothes       |        3        |           |    0   |
+----+---------------+-----------------+-----------+--------+
| 4  | bmw           |       1-4       |     1     |    1   |
+----+---------------+-----------------+-----------+--------+
| 5  | audi          |       1-5       |     1     |    1   |
+----+---------------+-----------------+-----------+--------+
| 6  | 100           |      1-4-6      |    1-4    |    2   |
+----+---------------+-----------------+-----------+--------+
| 7  | 80            |      1-4-7      |    1-4    |    2   |
+----+---------------+-----------------+-----------+--------+
| 8  | A4            |      1-4-8      |    1-4    |    2   |
+----+---------------+-----------------+-----------+--------+
| 9  | QUATRO        |     1-4-8-9     |   1-4-8   |    3   |
+----+---------------+-----------------+-----------+--------+
| 10 | TDI           |     1-4-8-10    |   1-4-8   |    3   |
+----+---------------+-----------------+-----------+--------+
| 11 | Black         |    1-4-8-9-11   |  1-4-8-9  |    4   |
+----+---------------+-----------------+-----------+--------+
| 12 | White         |   1-4-8-9-12    |  1-4-8-9  |    4   |
+----+---------------+-----------------+-----------+--------+
| 13 | 2 doors       |  1-4-8-9-11-13  |1-4-8-9-11 |    5   |
+----+---------------+-----------------+-----------+--------+
| 14 | 5 doors       |  1-4-8-9-11-14  |1-4-8-9-11 |    5   |
+----+---------------+-----------------+-----------+--------+

Это новая идея и нуждается в улучшении. Надеюсь, вам это удастся.

Ответ 3

Возможно, вы захотите ознакомиться с этой статьей о предложениях по обработке древовидных данных в MySQL.

http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql

Какой подход вы решите предпринять, вероятно, будет зависеть от того, из чего состоят ваши приложения для чтения и записи.

В настоящее время вы используете модель списка смежности, что особенно проблематично при работе с произвольными типами запросов глубины дерева. Если это основной случай использования, вы можете рассмотреть подход вложенного набора, который, хотя, вероятно, не такой интуитивный, гораздо лучше подходит для произвольных запросов дерева глубин.

Задача с помощью подхода с вложенным набором заключается в том, что обновление таблицы обычно затруднено.

Ответ 4

Чтобы вызвать это, просто создайте объект targetId как аргумент:

Вроде:

$counter = new RecursiveCounter(8);
$count = $counter->getCount();

Класс:

class RecursiveCounter {
private $row;
private $targetId;

public function __construct($targetId) {
    //Just setting up the info I need. You need to consider how to get the data from database and replace the constructor
    $this->row = array(
        1 => array("category" => "cars", "parent" => 0),
        2 => array("category" => "realestate", "parent" => 0),
        3 => array("category" => "clothes", "parent" => 0),
        4 => array("category" => "bmw", "parent" => 1),
        5 => array("category" => "audi", "parent" => 1),
        6 => array("category" => "100", "parent" => 5),
        7 => array("category" => "80", "parent" => 5),
        8 => array("category" => "A4", "parent" => 5),
        9 => array("category" => "QUATRO", "parent" => 8),
        10 => array("category" => "TDI", "parent" => 8),
        11 => array("category" => "Black", "parent" => 9),
        12 => array("category" => "White", "parent" => 9),
        13 => array("category" => "doors", "parent" => 11),
        14 => array("category" => "doors", "parent" => 11)
    );
    $this->targetId = $targetId;
}

public function getCount() {
    // Entry point
    $count = 0;
    foreach ($this->row as $id => $row) {
        if ($this->isMatchTarget($id)) {
            $count++;
        }
    }
    return $count;
}
private function getParent($id) {
    $parentId = $this->row[$id]["parent"];
    if (array_key_exists($parentId, $this->row)) {
        return $parentId;
    } else {
        return false;
    }
}

private function isMatchTarget($id) {
    // 1. If the supplied id is the target id, job is done and return true;
    // 2. If not:
    //      Get the parent ud;
    //      If parent id is not 0 (Meaning it has a parent), keep on checking
    //          What to check? Check if the parent id is matching
    //          If the the parent id is still not equal to target or 0, it will check the parent of parent until it they are equal or it is 0
    //      if there is no parent, and the id dont match (Ending condidtion)
    //          return false;
    if ($id == $this->targetId) {
        return true;
    } else {
        $parentId = $this->getParent($id);
        if (0 != $parentId) {
            return $this->isMatchTarget($parentId);
        } else {
            return false;
        }
    }
}

}

Ответ 5

Вы можете использовать рекурсивную функцию в php для получения чисел продукта в категории и его дочернего

public function get_number_of_products_in_category($cat_id)
{
    $qty = 0;

    //get number of product in this category
    $nb_products = $this->system->_getRowsCount('products', array(array('category_id' => $cat_id)));
    $qty += $nb_products;

    //get all child categories
    $items = $this->system->_getCustomTableData('categories', array(array('category_parent' => $cat_id)), 'id DESC');

    //add number of products in the child category
    if(!empty($items)) {
        foreach ( $items as $key => $value ) {
            $qty += get_number_of_products_in_category($value['id']);
        }
    }

    return $qty;
}

чтобы ускорить запрос, вы должны установить ключ для этих столбцов:

  • category_id в таблице продуктов
  • category_parent в таблице категорий

Ответ 6

Использование индексов в MySQL - это то, что почти не использует ресурс, если вы правильно используете индексы. Таким образом, вы можете подсчитать количество продуктов для каждой категории, а затем использовать PHP для выполнения большой работы.

$query="SELECT c.*, count(p.*) total
      FROM category, product p on c.id=p.category_id
      GROUP BY c.id;";

Предположим, что вы сохранили результат предыдущего запроса в массиве $result_categories

 function getMatchingItemsNb($result_categories, $category_id) {
    $sum=0;
    foreach ($result_categories as $row) {
      if ($row['id']==$category_id) {
         $sum+=$row['total'];
      } else if ($row['category_parent']==$category_id) {
         $sum+=getMatchingItemsNb($result_categories, $row['category_parent']);
      }
    }
    return $sum;
 }

Для этого требуется только один запрос SQL, который будет полезен для индексов и кеша. И он может быть использован для получения нескольких категорий, без запроса mysql дважды.

Для повышения производительности вы также можете рассмотреть возможность добавления поля total в таблицу category, даже если это подразумевает некоторую избыточность, но я предполагаю, что изменение структуры данных здесь не является предметом.