PHP: Категории бесконечного уровня вложенности.

Author Автор: Роман Чернышов    Опубликовано: 9 августа 2012

Приветствую вас мои дорогие читатели, сегодня я хочу поделиться с вами моими небольшими наработками по созданию скрипта(класса) для работы с категориями и подкатегориями бесконечного уровня вложенности. Необходимость применения сие функционала может потребоваться где угодно, например в организации категорий новостей, статей, товаров в интернет магазине или же вообще пользовательских комментариев с ответами, также вложенными друг в друга.

И так для начала опишу с чем мы будем работать и что нам понадобится.
Система: PHP 5 и выше, mySQL 4 и выше
Вспомогательные классы: dbsql.class.php (класс для работы с базой данных)
Класс вложенных категорий: classTreeCategory.php (непосредственно основной класс, ниже приведен его листинг и пояснения.

Создаем таблицу в БД, следующей структуры:

DROP TABLE IF EXISTS `category`;
CREATE TABLE `category` (
   `id` int(11) NOT NULL auto_increment,
   `podcat` int(11) NOT NULL,
   `name` varchar(255) NOT NULL,
    PRIMARY KEY  (`id`),
    KEY `id` (`id`)
   ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

В данной таблице присутствует поле ID — порядковый номер категории, podcat — имеет значение ноль у категорий первого порядка или ID родительской категории, name — название категории.

Пример работы класса, вывод категорий списком с подкатегориями:

include('dbsql.class.php');
include('classTreeCategory.php');
$DB=new DB_Engine('mysql', $settings['dbHost'], $settings['dbUser'], $settings['dbPass'], $settings['dbName']); // подключаемся к БД, с указанием данных доступа
 
$category = new TreeCategory ($DB); // передаем в класс категорий, объект работы с БД
$category->table='category'; // название таблицы в БД с категорийми
$array=$category->getCategory(); // получаем все категории из БД в виде многоуровневого массива, отсортированные и вложенные уже в нужном нам порядке
$category->outCategory($array, 'option'); // подготовка вывода категорий (формируем HTML), передаем массив с категориями
echo $category->html; // вывод категорий в виде HTML <option value="id">name</option>

Как видно из примера выше, все предельно просто, создаем новый объект $category, устанавливаем с какой таблицей БД работаем: ‘category’, далее получаем из таблицы список всех категорий уже оформленный в виде массива и разложенных в иерархичном порядке, с учетом всех подкатегорий. затем передаем массив в метод outCategory() который формирует для нас готовый HTML код, который остается только вывести в браузер.

Метод outCategory(), как мы видим принимает два параметра @array и @string в первом параметре массив со всеми категориями, а во втором строка содержащая значение option или table, это значени указывает какой тип HTML кода требуется сформировать.
Значение option — формирует следующий HTML код:

<option value="1">-категория1</option>
<option value="2">--подкатегория 1</option>
<option value="3">---подподкатегория 1</option>
<option value="4">-категория 2</option>

Для вставки данного HTML кода в поле select какой либо формы.

Значение table— формирует следующий HTML код:

<tr><td>-категория1</td><td>кнопки ред и удл</td></tr>
<tr><td>--подкатегория1</td><td>кнопки ред и удл</td></tr>
<tr><td>---подподкатегория1</td><td>кнопки ред и удл</td></tr>
<tr><td>-категория2</td><td>кнопки ред и удл</td></tr>

Этот HTML код удобен для вставки в таблицу которая отображает все наши категории подкатегории.

Класс имеет также следующие методы:
deleteItem($id); — удаляет одну категорию, не смотря на вложенные
delCategory($array, $id); — удаляет категорию со всеми вложенными подкатегориями, $array — массив со всеми категориями подготовленный методом $category->getCategory(), $id- номер удаляемой категории
addItem(); — данный метод следует вызывать если вы хотите добавить категорию, при этом этот метод считывает значения из данных переданных методом POST, т.е. из массива $_POST.
$name=$this->PHP_slashes(strip_tags($_POST[‘name’])); // имя категории
$podcat=intval($_POST[‘podcat’]); // ID родительской категории, если указан 0 категория будет в корне.
updateItem(); — аналогично предыдущему методу, кроме того что данный метод обновляет категорию, её название и уровень вложенности.

Листинг всего класса по работе с категориями и подкатегориями бесконечного уровня вложенности.

<?php
/**
 *
 * CMS osRealty 2.1.x
 * Autor: Roman Chernyshov
 * E-mail: support@osRealty.ru
 * URL: www.osRealty.ru
 *
 */
 
 
 
/**
 *
 *  Пример использования
 *
 *  $category = new TreeCategory ($DB); // передаем в класс интерфес работы с БД
 *  $category->table='category'; // запрос на выборку списка категорий, название таблицы
 *  $category->outCategory($category->getCategory()); // подготовка вывода категорий(запрос массива категорий)
 *  echo $category->html; // вывод категорий в HTML <option value="id">name</option>
 *
 */
 
 
/**
 *  Дамп таблицы с которой ведется работа
 *
 *  DROP TABLE IF EXISTS `category`;
 *  CREATE TABLE `category` (
 *    `id` int(11) NOT NULL auto_increment,
 *    `podcat` int(11) NOT NULL,
 *    `name` varchar(255) NOT NULL,
 *    PRIMARY KEY  (`id`),
 *    KEY `id` (`id`)
 *  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 *  
 */
 
 
class TreeCategory {
	/**
	*   Строка запроса в БД
	*/
	var $table;
 
	/**
	*   Интерфейс работы с БД
	*/
	var $DB;
 
	/**
	*   Массив категорий с вложенными подкатегориями
	*/
	var $arrayCat;
 
	/**
	*   Авто-подстчет кол-ва прочерков перед названием категории при выводе
	*/
	var $countPodcat;
 
	/**
	*   HTML код для вывода категорий с подкатегориями
	*/
	var $html;
 
 
	/**
	*   Получаем интерфейс для работы с БД и кладем его в локальные переменную
	*/
	function __construct($DB) {
		$this->DB=$DB;
		$this->component=$_GET['component'];
	}
 
 
	/**
	*   Получает список категорий, сортирует и помещает в массив с вложенными массивами и т.д.
	*   @return array category
	*/
	function getCategory () {
 	  $all = $this->DB->getAll("SELECT * FROM `{$this->table}` ORDER BY `id` ASC");
          $path = array();
	  if(count($all)>0) {
	   foreach($all as $item):
		if($item['podcat']==0)$sort[$item['id']]=$item;
		if($item['podcat']>0)
			{
			if(isset($path[$item['podcat']]))
				{
				$str='$sort';
				foreach($path[$item['podcat']] as $pitem):
					$rep=$item['podcat'];
					$str.="[$pitem][sub]";
				endforeach;
				$str.="[{$item['podcat']}][sub]";
 
				$str.="[{$item['id']}]";
                                $str.='=$item;';
 
				eval($str);
 
				foreach($path[$item['podcat']] as $pitem):
					$path[$item['id']][]=$pitem;
				endforeach;
 
				$path[$item['id']][]=$item['podcat'];
				}
				else
				{
				$sort[$item['podcat']]['sub'][$item['id']]=$item;
				$path[$item['id']][]=$item['podcat'];
				}
			}
           endforeach;
           }
           $this->arrayCat=$sort;
	   return $this->arrayCat;
        }
 
 
        /**
        *   Печатает категории, помещает готовый HTML в $this->html
	*   @param array Массив с категориями и вложенными подкатегориями
	*   @param string Тип генерируемого HTML кода для вывода, option или table
        */
        function outCategory(&$arrayCat, $type='option', $idSel=0) {
		foreach($arrayCat as $sub) {
		  $this->countPodcat++;
		  $this->outItem($sub, $type);
		  if(!empty($sub['sub']))$this->outCategory($sub['sub'], $type, $idSel);
		  $this->countPodcat--;
		}
	}
 
	/**
	*   Вспомогательный метод подготовки HTML кода
	*   @param array Массив с категорией
	*   @param string Тип генерируемого HTML кода для вывода, option или table
	*/
        function outItem($sub, $type='option', $idSel=0) {
                for($i=0;$i<=$this->countPodcat;$i++) {
                        $out.='-';
			}
		if($idSel==$sub['id'])$se='selected'; else $se='';
		if($type=='option')$this->html.="<option value=\"{$sub['id']}\" {$se}>{$out} {$sub['name']}</option>";
		if($type=='table')$this->html.= <<<HTML
			<tr><td class="name" width="450">{$out} <img src="images/index.png" width="16" height="16" border="0" alt="{$sub['name']}" />
			{$sub['name']}</td>
			<td align="center"><a href="?component={$this->component}&section=edit&id={$sub['id']}">
				<img src="images/user_edit.png" alt="edit" title="edit" border="0" /></a></td>
			<td align="center"><a href="?component={$this->component}&section=delete&id={$sub['id']}" class="ask">
			<img src="images/trash.png" alt="del" title="del" border="0" /></a></td></tr>
HTML;
 
	}
 
	function delCategory(&$a_tree,&$id=0) {
		foreach($a_tree as $sub) {
			if($sub['id']<>$id and isset($sub['sub']))$this->delCategory($sub['sub'],$id);
			if($sub['id']==$id) {
				$sql="DELETE FROM {$this->table} WHERE id = '$id' LIMIT 1";
				$this->DB->execute($sql);
				if (isset($sub['sub'])) $this->delCategory_process($sub['sub']);
			}
		}
	}
 
	function delCategory_process(&$a_tree) {
		foreach($a_tree as $sub) {
			$sql="DELETE FROM {$this->table} WHERE id = '{$sub['id']}' LIMIT 1";
			$this->DB->execute($sql);
			if(isset($sub['sub']))$this->delCategory_process($sub['sub']);
		}
	}
 
 
	function updateItem() {
		$name=$this->PHP_slashes(strip_tags($_POST['name']));
		$podcat=intval($_POST['podcat']);
		$id=intval($_POST['id']);
		$sql="UPDATE `{$this->table}` SET `name` = '{$name}',`podcat` = '{$podcat}'
			WHERE `id`='{$id}' LIMIT 1; ";
		$this->DB->execute($sql);
 
	}
 
	function addItem() {
		$name=$this->PHP_slashes(strip_tags($_POST['name']));
		$podcat=intval($_POST['podcat']);
		$id=intval($_POST['id']);
		$sql="INSERT INTO `{$this->table}` (`id`,`podcat`,`name`) VALUES ('', '$podcat', '$name');";
		$this->DB->execute($sql);
 
	}
 
	function deleteItem($id) {
		$id=intval($id);
		$sql="DELETE FROM `{$this->table}` WHERE `id` = '{$id}' LIMIT 1";
		$DB->execute($sql);
		header("Location: ?component={$this->component}");
	}
 
	function PHP_slashes($string,$type='add') {
	    if ($type == 'add') {
	        if (get_magic_quotes_gpc()) {
	            return $string;
	            }
	        else {
	            if (function_exists('addslashes')) {
	                return addslashes($string);
		    }
	            else {
	                return mysql_real_escape_string($string);
	            }
	        }
	    }
	    else if ($type == 'strip') {
	        return stripslashes($string);
	    }
	    else {
	        die('error in PHP_slashes (mixed,add | strip)');
	    }
	}
 
}

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

Буду признателен если в комментариях вы предложите собственные варианты решения данной задачи — организации категорий бесконечного уровня вложенности.

Оставить комментарий

Автор блога
Роман Чернышов
Веб-разработчик,
Full Stack
Senior, Architect
PHP, JavaScript, Node.JS, Python, HTML 5, CSS 3, MySQL, Bash, Linux Admin
Заказать работу
предложить оффер

Моя книга
Книга. Веб-разработчик. Легкий вход в профессию
Печатная книга
Веб-разработчик.
Легкий вход в профессию
Купить за 359₽
Популярные записи
Последние вопросы
Список вопросов
Последние комментарии
Меню

Archive

Мои проекты
Insurance CMS Love Crm CMS Совместные покупки Мой PHP Framework Хостинг для моих клиентов Лицензии на мой софт и поддержка