Моделирование N-мерного каталога

15.04.2011

N-мерный каталог продукции, то меж каталог с подкатегориями неограниченной вложенности, Задумка такая, что у товара может быть миниум одна, а максиум N (Ну, для начала, N не менее 5-ти, но задумка сделать НЕ ограниченным) и если в чистой категории (pid=0) можно создать или товар или подкатегорию. Если создаём подкатегорию, то этой новосозданной подкатегории в поле pid записывается id родительской категории (род, кат,), и в род, кат, отныне можно создавать только подкатегории (товары уже НЕльзя), до тех пор, пока из неё не будут удалены все подкатегории. А если же мы в род, кат, создаём товар, то в таблице товаров полю link_id созданного товара присваивается id род,кат,, и в ней мы теперь можем создавать только товары, пока не будут из таблицы удалены все товары.

Пока стандартная схема 3-ёх уровнего каталога, но фишка в том, что поле pid таблицы категорий не INT, а VARCHAR, и когда мы в категории 2-ого уровня (К2) создаём К3, то в pid записываются через запятую id род, кат, начиная с самой верхней, Томеж если в К1 с id=1 pid=0, создали К2 у которой id=2 и pid=1, и в ней создали К3 у которой id=3, то pid у ней будет равен pid='1,2' и т,д,

Вот упрощённая структура таблицы с тестовым дампом:


  1. CREATE TABLE `test`
  2. (
  3. `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  4. `pid` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
  5. `title` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT ''
  6. )
  7. ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;


  1. INSERT INTO `test`
  2. (`id`,`pid`,`title`)
  3. VALUES
  4. (1,'0','Кат 1'),
  5. (2,'0','Кат 2'),
  6. (3,'0','Кат 3'),
  7. (4,'1','Кат 1.1'),
  8. (5,'1','Кат 1.2'),
  9. (6,'1','Кат 1.3'),
  10. (7,'1,4','Кат 1.1.1'),
  11. (8,'1,4','Кат 1.1.2'),
  12. (9,'1,4','Кат 1.1.3'),
  13. (10,'1,5','Кат 1.2.1'),
  14. (11,'1,5','Кат 1.2.2'),
  15. (12,'1,5','Кат 1.2.3'),
  16. (13,'1,6','Кат 1.3.1'),
  17. (14,'1,6','Кат 1.3.2'),
  18. (15,'1,6','Кат 1.3.3'),
  19. (16,'2','Кат 2.1'),
  20. (17,'2','Кат 2.2'),
  21. (18,'2','Кат 2.3'),
  22. (19,'0','Кат 4');

Для того, что бы узнать какая категория последняя (не имеющая детей, следовательно или имеющая товары или пустая), а какая родительская (имеющая детей, следовательно имеющая подкатегории) Нам нужно отделить группу "Последних id" (3,7,8,9,10,11,12,13,14,15,16,17,18,19) которые или пустые или содержат товары, и группу "Промежуточных id" (1,2,4,5,6)

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

Для админской части:


  1. function list_cat($pid)
  2. {
  3. if ((int)$pid <= 0) $pid = 0;
  4. $query = 'SELECT t1.*,COUNT(t2.pid)col FROM '.$this->table.'_cat t1 LEFT JOIN '.$this->table.'_cat t2 ON FIND_IN_SET(t1.id,t2.pid) WHERE t1.pid=\''.addslashes($pid).'\' GROUP BY t1.id ORDER BY t1.order';
  5. $res = $this->db->assoc($query);
  6. return (is_array($res) && count($res) > 0) ?$res :false;
  7. }

Где разделения на "Конечные" и "Промежуточные" категории происходит путём подсчёта дочерних подкатегорий.

Для кат 1 pid='0' (именно '0', а не NULL) А для всех дочерних последующих соответствующему pid-у

И клиентской части:


  1. function list_cat($id)
  2. {
  3. $query = 'SELECT t1.*,COUNT(t2.pid)col FROM '.$this->table.'_cat t1 LEFT JOIN '.$this->table.'_cat t2 ON FIND_IN_SET(t1.id,t2.pid) WHERE (t1.pid=\''.(int)$id.'\' OR t1.pid LIKE \'%,'.(int)$id.'\') AND t1.status>0 GROUP BY t1.id ORDER BY t1.order';
  4. $res = $this->db->assoc($query);
  5. return (is_array($res) && count($res) > 0) ?$res :false;
  6. }

Где, в отличии от админской части, выполняется только просмотр, а следовательно нам не нужно знать полностью pid родителя и можно схитрить с оператором LIKE, что бы делать выборку не по всему pid, а только по его числовой концовке, что даёт возможность применить ЧПУ к сылкам (когда мы делаем выборку не по 1-му id а по связки id через запятую, нам невозможно заменить его через .htaccess)

Последнее в нашем блоге