Dziś jest piątek, 9 stycznia 2009 roku (z kalendarza...)

Przesuwanie węzłów wewnątrz drzew

Icon

12.04.2006, 21:39

PHP

Komentarze (4)

Powrót

Swego czasu napisałem artykuł przybliżający sposób efektywnego wykonania hierarchicznych struktur z użyciem PHP i MySQL. Opiera się on na połączeniu wszystkich elementów dwoma parametrami left i right, tworzących tak jakby ścieżkę oplatającą całą strukturę. Parametry te mają taką właściwość, że między left i right węzła A zawarte są wartości left i right wszystkich dzieci, ich poddzieci itd. W liściu right jest o 1 większy od left. Struktura daje duże możliwości, ale zarządzanie węzłami jest dość skomplikowane. Pisząc artykuł, nie miałem pojęcia, jak wykonać przesuwanie elementów w górę i w dół. Miałem się tym zająć, ale zapomniałem, gdyż nie było mi to potrzebne.

Sprawa powróciła paręnaście dni temu w związku z pewnym projektem, do którego musiałem właśnie zaprojektować drzewo z możliwością przesuwania. Tak narodziły się dwa poniższe kody. Najpierw przesuwanie elementu w górę w obrębie tego samego rodzica:

<?php
 
function moveUp($currId)
{
	global $sql;
	$sql -> query('LOCK TABLES data d1 READ, data d2 READ, data WRITE');
	$nextId = $sql -> get('SELECT d2.id FROM data d1, data d2 WHERE d1.parent_id = d2.parent_id AND d2.right = (d1.left - 1) AND d1.id=\''.$currId.'\'');
			
	if($nextId > 0)
	{
		$currDifference = 0;
		$currSubnodes = selectNodesId($currId, $currDifference);
		$nextDifference = 0;
		$nextSubnodes = selectNodesId($nextId, $nextDifference);
		$sql -> query('UPDATE data SET `left` = (`left` + '.$currDifference.'), `right` = (`right` + '.$currDifference.') WHERE
			id IN(\''.implode('\', \'', $nextSubnodes).'\')');
		$sql -> query('UPDATE data SET `left` = (`left` - '.$nextDifference.'), `right` = (`right` - '.$nextDifference.') WHERE
			id IN(\''.implode('\', \'', $currSubnodes).'\')');			
	}
	$sql -> query('UNLOCK TABLES');	
} // end moveUp();
 
?>

A oto przesuwanie w dół:

<?php
 
function moveDown($currId)
{
	global $sql;
	$sql -> query('LOCK TABLES data d1 READ, data d2 READ, data WRITE');
	$nextId = $sql -> get('SELECT d2.id FROM data d1, data d2 WHERE d1.parent_id = d2.parent_id AND d2.left = (d1.right + 1) AND d1.id=\''.$currId.'\'');
	
	if($nextId > 0)
	{
		$currDifference = 0;
		$currSubnodes = selectNodesId($currId, $currDifference);
		$nextDifference = 0;
		$nextSubnodes = selectNodesId($nextId, $nextDifference);
		$sql -> query('UPDATE data SET `left` = `left` - '.$currDifference.'), `right` = (`right` - '.$currDifference.') WHERE
			id IN(\''.implode('\', \'', $nextSubnodes).'\')');
		$sql -> query('UPDATE data SET `left` = (`left` + '.$nextDifference.'), `right` = (`right` + '.$nextDifference.') WHERE
			id IN(\''.implode('\', \'', $currSubnodes).'\')');			
	}
	$sql -> query('UNLOCK TABLES');		
} // end moveUp();
 
?>

Funkcja selectNodesId powinna zwracać ID wszystkich węzłów zawartych wewnątrz węzła o podanym ID, wraz z nim samym. Do drugiego parametru, za pomocą referencji, wpisywana jest różnica między parametrami right i left powiększona o 1: RIGHT - LEFT + 1. Funkcje posiadają blokadę przed wysunięciem się poza drzewo - jest to realizowane przez zawartą w nich instrukcję if, która sprawdza, czy na pewno znaleziono ID sąsiedniego węzła.

Powrót

Komentarze

Napisał kkkkkkk w środę, 25 kwietnia 2007 o 12:43

Witam,

czytalem Twoj poprzedni art i wlasnie jego dotyczy moje pytanie i problem.


chcialbym wynik zapytania zapisac do tablicy w postaci :
node1
submnodes
subnode_1
subnode_2
subnode_21
subnode_22
..................subnode_n
.
.
.
.
.
nodem
submnodes...


chyli drzewo zapisane do tablicy.

Jakis pomysl ?

Dzieki za odpowiedz.

Napisał Zyx w środę, 25 kwietnia 2007 o 13:03

Aaa, czyli zagnieżdżone tablice? Sprawa jest prosta. Pętla z rekurencją:

<?php
	function buildSub(&$list, &$i, &$result, $level)
	{
		$j = 0;
		while(isset($list[$i]))
		{
			if($list[$i]['depth'] == $level)
			{
				$result[$j] = array('title' => $list[$i]['title']);
				$j++;
			}
			if($list[$i]['depth'] > $level)
			{
				$result[$j-1]['subitems'] = array();
				buildSub($list, $i, $result[$j-1]['subitems'], $level+1);
				if(!isset($list[$i]))
				{
					return;
				}
			}
			if($list[$i]['depth'] < $level)
			{
				$i--;
				return;
			}
			$i++;
		}
	} // end buildSub();
 
	$tree = array(0 =>
			array('title' => 'Main category 1', 'depth' => 0),
			array('title' => 'Main category 2', 'depth' => 0),
			array('title' => 'Subcategory 2.1', 'depth' => 1),
			array('title' => 'Main category 3', 'depth' => 0),
			array('title' => 'Subcategory 3.1', 'depth' => 1),
			array('title' => 'Item 3.1.1', 'depth' => 2),
			array('title' => 'Item 3.1.2', 'depth' => 2),
			array('title' => 'Item 3.1.3', 'depth' => 2),
			array('title' => 'Subcategory 3.2', 'depth' => 1),
			array('title' => 'Subcategory 3.3', 'depth' => 1),
			array('title' => 'Item 3.3.1', 'depth' => 2),
			array('title' => 'Main category 4', 'depth' => 0),
			array('title' => 'Subcategory 4.1', 'depth' => 1),
			array('title' => 'Item 4.1.1', 'depth' => 2)
		);
	$result = array();
	$i = 0;
	buildSub($tree, $i, $result, 0);
	
	var_dump($result);
 
?>

Za parametry podajesz: listę wejściową, zmienną $i ustawioną na 0, tablicę, do której trzeba wpisać wynik i 0.

Napisał buczer w środę, 19 grudnia 2007 o 12:59

witam
czy przy pomocy tej metody przesowania wezłow
jest mozliwe przesuwanie z dowolną ilością węzłow??
mi sie udało to dla 2 poziomów czyli
dzial 1
dzial 2
kategoria 1
kategoria 2
kategoria 3
dział 3
moge zmienic kategorie i działy miejsami ale juz przy podakatgoriach moja funkcja sie gubi przy zmianie kolejnoisci dzialow

Napisał Zyx w środę, 19 grudnia 2007 o 18:22

Mój kod pozwala zamienić ze sobą miejscami dwa dowolne elementy niezależnie od głębokości, na jakiej się znajdują - muszą jedynie być w tym samym węźle. Można to rozszerzyć, aby przesuwanie było o więcej, niż jedną pozycję - główna zasada jest ta sama. Trzeba tylko pobrać trochę więcej danych, aby przedziały określić prawidłowo.

Strona 1 z 1 :: 1

Skomentuj

NickInformacja
E-mailTylko do użytku wewnętrznego.
WWWNie zapomnij o http://
LayoutNapisz tu, czy widzisz dzienny czy nocny layout.
WpisFormatowanie wiki
Internauto, pamiętaj! Wolność to nie samowola - dbaj o kulturę wypowiedzi oraz dyskusji w sieci.

Na Zyxist.com panuje swoboda wyrażania opinii oraz krytyki pod dowolnym adresem. Jedyny warunek: musi być ona kulturalna i rzeczowa. Na chamstwo, prostactwo lub jawne obrażanie kogokolwiek nie ma tu miejsca i takie komentarze są bardzo szybko usuwane. Jeśli zamierzasz polemizować z treścią wpisu, wpierw uważnie ją przeczytaj.

© Tomasz "Zyx" Jędrzejewski 2005 - 2009 | Wykonanych zapytań: 2 | Serwer wirtualny zapewnia