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(); — аналогично предыдущему методу, кроме того что данный метод обновляет категорию, её название и уровень вложенности.

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

table='category'; // запрос на выборку списка категорий, название таблицы
 *  $category->outCategory($category->getCategory()); // подготовка вывода категорий(запрос массива категорий)
 *  echo $category->html; // вывод категорий в HTML 
 *
 */


/**
 *  Дамп таблицы с которой ведется работа
 *
 *  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.="";
		if($type=='table')$this->html.= <<{$out} {$sub['name']}
			{$sub['name']}
			
				edit
			
			del
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
Заказать работу
предложить оффер

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

Archive

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