luq techblog

o tworzeniu słów kilka…

Drzewa – prosta obsługa w PHP 17 marca 2010

Filed under: PHP,Programowanie — Łukasz @ 23:02
Tags: , ,

Dziś przedstawię klasę, którą napisałem do obsługi grafu drzewiastego. Pomysł na wpis zrodził się w chwili modyfikacji pewnego skryptu, w, którym zlecono mi kilka modyfikacji, jedną z nich było dopisanie wyświetlenie struktury drzewiastej kategorii pobranych z bazy… Jak wiadomo, jest mnóstwo sposobów na przedstawienie drzewa z relacyjnych bazach danych, więcej o tym można poczytać tutaj. Ja jednak spotkałem się już z gotową bazą która wykorzystywała drugą metodę opisaną na powyższej stronie, tzn.
Tabelka z kategoriami zbudowana była z kilku pól, ale dla mnie były ważne 3 z nich (nazwy dla ułatwienia pozmieniałem):

id | parent | name

Jak nie trudno się domyśleć, id to unikatowy identyfikator, parent to id rodzica natomiast name to nazwa kategorii.

Przed moją modyfikacją, na stronie były podane tylko kategorie główne, czyli takie które miały parent = 0. Czyli zapytanie wyglądało tak:

SELECT id, name FROM category WHERE parent = 0

A więc ja wyrzuciłem z niego przeszkadzający mi warunek + dodałem sortowanie po id (nie sortowałem już drugi raz wyników, zresztą w tej modyfikacji, nie użyłem tej klasy, wszystko było napisane strukturalnie a więc po co tam tą klasę upychać…) i pobrałem do tablicy pełną tabelkę z kategoriami, natomiast po stronie PHP zadbałem o to aby przekształcić tą tablicę tak aby była prosta możliwość jej wyświetlenia w sposób:

- Kategoria 1
     - Kategoria 1.1
          - Kategoria 1.1.1
          - Kategoria 1.1.2
     - Kategoria 1.2
- Kategoria 2
(...)

Oczywiście rozwiązanie rekurencyjne, którego nie będę tłumaczył krok po kroku bo wiele cierpliwości by mnie to kosztowało… Pokaże jedynie obsługę tego ustrojstwa, kod odpowiedzialny za to, jak i rzucę na końcu całą klasę do ściągnięcia wraz z przykładem.

 

Okej, a więc główne zadanie, można wykonać w ten sposób:

 

Załóżmy, że to nasza tablica z kategoriami, którą mamy w zmiennej $arr:

Array
(
    [0] => Array
        (
            [id] => 1
            [name] => Programowanie
            [parent] => 0
        )

    [1] => Array
        (
            [id] => 2
            [name] => Web
            [parent] => 0
        )

    [2] => Array
        (
            [id] => 3
            [name] => PHP
            [parent] => 1
        )

    [3] => Array
        (
            [id] => 5
            [name] => JavaScript
            [parent] => 1
        )

    [4] => Array
        (
            [id] => 6
            [name] => Ajax
            [parent] => 5
        )

    [5] => Array
        (
            [id] => 7
            [name] => XHR
            [parent] => 6
        )

    [6] => Array
        (
            [id] => 8
            [name] => Technika ukrytej ramki
            [parent] => 6
        )

)

Piszemy:

$oTree = new Tree( $arr );
$oTree->createTree();
	
foreach( $oTree->getTree1D() as $k => $v ){
	echo str_repeat( ' ', $v['level']*5 ).'- '.$v['name']."\n"; 
        // klucz level, jest tworzony wewnątrz klasy, 
        // jego nazwę można zmienić używając metody setLevelRowName()
}

Dzięki czemu otrzymujemy na ekranie taki oto wynik:

- Programowanie
     - PHP
     - JavaScript
          - Ajax
               - XHR
               - Technika ukrytej ramki
- Web

A więc jak widać jest to dość wygodne ;)
Cała klasa prezentuję się tak:

<?php
	/**
     * Klasa tworzonce drzewo 
     *
     * @author luq <luq_10@o2.pl>
     * @version 0.1
     */
	 
	class Tree
	{
		private $array;
		private $tree_ND = false;
		private $tree_1D = false;
		
		private $idRowName 			= 'id';
		private $parentRowName 		= 'parent';
		private $columnRowName 		= 'column';
		private $levelRowName 		= 'level';
		
		public function __construct( $arr ){
			$this->array = $arr;
		}
		
		// SETTERS
		public function setIdRowName( $name ){
			$this->idRowName = $name;
		}
		public function setParentRowName( $name ){
			$this->parentRowName = $name;
		}
		public function setColumnRowName( $name ){
			$this->columnRowName = $name;
		}
		public function setLevelRowName( $name ){
			$this->levelRowName = $name;
		}
		// SETTERS END
		
		// GETTERS
		public function getTreeND(){
			return $this->tree_ND;
		}
		public function getTree1D(){
			return $this->tree_1D;
		}
		// GETTERS END
		
		public function createTree(){
			$this->tree_ND = $this->array;
			usort( $this->tree_ND, array( $this, '_sortArray' ) );
			
			foreach( $this->tree_ND as $k => $v ){
				if( $v[ $this->parentRowName ] != 0 ){
					$this->_reqCreateTree( $this->tree_ND, $v, $k );
				}
			}
			
			$this->tree_1D = array();
			$this->_splashTree( $this->tree_ND, $this->tree_1D );
		}
		
		private function _sortArray( $a, $b ){
			if( $a['id'] == $b['id'] ){
				return 0;
			}
			return ( $a < $b ) ? -1 : 1;
		}
		
		private function _reqCreateTree( &$array, $searchArray, $key, &$parentArr = false ){
			if( !$parentArr ){
				$parentArr = &$array;
			}

			foreach( $array as $k => $v ){
				if( $v[ $this->columnRowName ] ){
					$this->_reqCreateTree( $array[$k][ $this->columnRowName ], $searchArray, $key, $parentArr );
				}
		
				if( $v[ $this->idRowName ] == $searchArray[ $this->parentRowName ] ){
					if( !$array[$k][ $this->columnRowName ] ){
						$array[$k][ $this->columnRowName ] = array();
					}
			
					array_push( $array[$k][ $this->columnRowName ], $searchArray );
					unset( $parentArr[ $key ] );

					return;
				}
			}
		}
		
		private function _splashTree( $array, &$newArray, &$i = 0, $level = 0 ){
			foreach( $array as $k => $v ){
				$newArray[$i] = $v;
				$newArray[$i][ $this->levelRowName ] = $level;
				unset( $newArray[$i][ $this->columnRowName  ] );
				$i++;
		
		
				if( is_array( $v[ $this->columnRowName ] ) ){
					$this->_splashTree( $v[ $this->columnRowName  ], $newArray, $i, $level+1 );
				}
			}
		}
	}
?>

Dodam tylko, że zdaje sobie z tego sprawę, że nie jest to najbardziej optymalne i zapewne dało by się dużo szybciej… :)
Paczka z klasą i przykładem – download

Reklamy
 

One Response to “Drzewa – prosta obsługa w PHP”

  1. aras Says:

    fajna klasa, prosta w obsłudze, gratuluję ;)


Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj / Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj / Zmień )

Zdjęcie na Google+

Komentujesz korzystając z konta Google+. Wyloguj / Zmień )

Connecting to %s