Drzewka w PHP
Transkrypt
Drzewka w PHP
Drzewka w PHP
Artur 'rzSeattle' Kmera
Kolejna próba stworzenia prostego interfejsu do obsługi drzewek.
1. Wprowadzenie
1. Wstęp
2. Struktura bazy oraz podstawowe założenia.
2. Operacje na danych drzewa
1. Podstawowe operacje - czego potrzebujemy?
2. Zapytania SQL
2.1. Pobieranie danych o rodzicu
2.2. Pobieranie struktury całego drzewa
2.3. Pobieranie struktury gałęzi drzewa
2.4. Dodanie nowego drzewa
2.5. Dodanie nowego dziecka do danego rodzica
2.6. Usuwanie gałęzi
3. Uzupełnienie kodu, czyli ostateczny wygląd klasy
3. Podsumowanie
http://www.php.pl
Strona 1 z 13
1. Wprowadzenie
Rozdział zawiera opis podstawowych założeń użytych do stworzenia klasy
obsługującej proste drzewo.
1. Wstęp
Każdemu programiście przychodzi kiedyś zmierzyć się z problemem drzewek.
Jeśli nie pojawiają się one w kategoriach produktów, artykułów to są potrzebne
do stworzenia systemu uprawnień opartego na grupach, stworzenia drzewa
tematycznego czy nawigacyjnego. Większość ludzi zabierając się do tego
problemu myśli "trochę kodowania i na pewno poradzę sobie z tym zadaniem
raczej bez większych problemów". Muszę przyznać ze zaliczałem się do tej grupy.
Zostałem jednak bardzo szybko sprowadzony na ziemie.
Na początku wydawało mi się ze każdej wartości wystarczy przypisać id rodzica a
wszystko pójdzie później jak po maśle. Pomysł tylko złudnie kusił przejrzystością
oraz brakiem redundancji danych. W rzeczywistości był koszmarem
programistycznym. Aby uzyskać jakiekolwiek dane o strukturze drzewa trzeba
było stosować pętle wciąż pytające o nowy poziom zagnieżdżenia. Pętle te proste
przy uzyskiwaniu informacji o tym, kto jest rodzicem, dla kogo i kto jest czyim
dzieckiem praktycznie nie nadawały się do wyświetlania danych. Dopisywały one
do tablicy znalezione pokrewne wartości jednak nie były w stanie ich
uporządkować. Wtedy zrozumiałem, że tworzenie systemu obsługującego
drzewka wymaga starannego planowania od podstaw. Jeśli próbujesz
rozwiązywać problemy na bieżąco, w miarę pojawiania się, to jesteś skazany na
niepowodzenie. Zacząłem więc od początku. Nie mogąc ustalić jasnych założeń
jak miałaby wyglądać implementacja drzewek na bazie danych zacząłem szukać.
Przejrzałem wiele różnych sposobów na uzyskanie struktury drzewiastej.
Niektóre były podobne do moich wcześniejszych prób, niektóre zupełnie inne. W
końcu ustawiłem priorytet mojego "wyszukiwania" na łatwe porządkowanie
drzewa. Podpowiedz uzyskałem jak zwykle na forum (thx. orson). Podpowiedź
obejmowała segregowanie drzewek dzięki kolumnie decimal w bazie danych oraz
wskazanie formatu dla tej kolumny. Dalej poszedłem sam. Chciałem stworzyć
klasę, która ma swoje ograniczenia, ale w zamian za to jest szybka i banalnie
prosta w użyciu.
Co do samego artykułu to są dwie zasady o których warto wspomnieć. Pierwszą z
nich jest to, że nazywam kolejne dzieci i rodziców "wartościami", jest to
powodowane tym, że tak naprawdę zastosowań drzewek jest wiele i jest to
najbardziej ogólna nazwa dla rodzica i dziecka jednocześnie jaka udało mi się
wymyślić. Drugą z nich jest oznaczanie wartości które musimy uzupełnić w
zapytaniach SQL danymi zewnętrznymi przez [*zmienna*].
A wiec bierzemy się do roboty.
http://www.php.pl
Strona 2 z 13
2. Struktura bazy oraz podstawowe założenia.
Do analizy wykorzystałem następujące drzewko:
0
1
2
/
SYSTEMY OPERACYJNE
LINUX
/
\
SLACK
DEBIAN
3
\
WINDOWS
/
NT
/
\
WIN 2000 WIN XP
\
NO NT
|
\
WIN 95 WIN 98 WIN MILENIUM
/
Postanowiłem umieścić cala strukturę w jednej tabeli. Tabela zawierała id
elementu, nazwę elementu oraz opis zajmowanej pozycji w kolumnie level.
Wyglądało to tak:
+----+--------------+---------+
| id | name
| level
|
+----+--------------+---------+
| 55 | Win 2000
| 1210000 |
| 54 | Win Milenium | 1130000 |
| 53 | Win 98
| 1120000 |
| 51 | NT
| 1200000 |
| 52 | Win 95
| 1110000 |
| 49 | NO NT
| 1100000 |
| 48 | linuks
| 2000000 |
| 47 | windows
| 1000000 |
| 46 | systems
| 0000000 |
| 57 | Win 2003
| 1230000 |
| 56 | Win XP
| 1220000 |
| 61 | Slack
| 2100000 |
| 62 | Debian
| 2200000 |
+----+--------------+---------+
O ile zawartości dwóch pierwszych kolumn nie musze tłumaczyć to należy
zrozumieć działanie kolumny level. Ma ona za zadanie opisać pozycje danej
grupy w drzewie. Składa się ona z góry ustalonej liczbie cyfr. Ilości cyfr zależy od
maksymalnego poziomu zagnieżdżenia, jaki chcemy uzyskać. Zmienia się ona
zawsze w zależności [liczba cyfr] = [poziom zagnieżdżenia] + 2.
Format levelu można opisać w sposób następujący. Ciąg cyfr 1 - 9 i reszta zer
("/^[1-9]+0+0{1}$/"). Taki format jest bardzo wygodny, kiedy dochodzi do
operacji na danych, o czym przekonacie się później. Ciąg cyfr określa pozycję
danej wartości w hierarchii drzewa. Każda następna cyfra różna od zera od lewej,
reprezentuje ścieżkę, jaka trzeba przebyć, aby dojść do wartości. Ostatnia cyfra
różna od zera reprezentuje to, którym dzieckiem z kolei jest dana wartość.
Weźmy na przykład wartość "WIN 98" ma ona, level, 112 czyli aby dojść do tej
wartości należy przejść przez wartość 1000000 (Windows) oraz 1100000 (NO
http://www.php.pl
Strona 3 z 13
NT). Dodatkowo na początku zawsze dodajemy korzeń 0000000 ("SYSTEMY
OPERACYJNE"). W ten sposób uzyskaliśmy prosta ścieżkę:
SYSTEMY OPERACYJNE -> WINDOWS -> NO NT -> WIN 98.
Wiec, po co jeszcze następna wartość liczby przypisana do levelu? Otóż jest to
informacja dla ewentualnych dzieci "WIN 98". Jeśli chcieli byśmy do grupy "WIN
98" dodać dziecko "SECOND EDICTION" to nie wystarczy nam level 1100000
ponieważ po wartości "NO NT" zgubilibyśmy ścieżkę i nie odnaleźlibyśmy wartości
"WIN 98". Dzięki dodatkowej informacji jesteśmy w stanie stworzyć ścieżkę dla,
WINDOWS SE, która wyglądałaby tak: 112+1(numer dla grupy SE)+000. W ten
sposób można opisać zależności dla maksymalnie 9 dzieci danego rodzica.
Ujawnia się tu pierwsze ograniczenie tej metody - dany rodzic może posiadać
tylko dziewięcioro dzieci. Można ten problem ominąć, ale o tym później.
Wspomniałem, że przed uzyskaną ścieżką dopisujemy jeszcze jedna wartość
hierarchii - 0000000. Jest to powodowane tym, iż level = 0000000 odpowiada
głównemu rodzicowi drzewa, który reprezentuje cala resztę jako korzeń. Ta
wartość jest wyjątkiem od zasad, które się tyczą już jej wszystkich dzieci.
Zauważmy, że w ten sposób "zajmujemy" całą tabelę na potrzeby tylko jednego
drzewka. Nie jest to zbyt wygodne jeśli chcemy tworzyć więcej drzew
niezależnych od siebie. Dodałem wiec kolumnę cluster ( grupa ) która oddziela
poszczególne drzewka przez nadanie każdemu indywidualnego numeru. Nie jest
to konieczne jeśli chcemy użyć tylko jednego drzewa do obsługi np. systemu
nawigacyjnego strony.
Ostatecznie więc struktura bazy wygląda następująco:
#
# Table structure for table `groups`
#
CREATE TABLE `groups` (
`id` int(11) NOT NULL auto_increment,
`cluster` int(11) NOT NULL default '0',
`name` varchar(100) NOT NULL default '',
`level` decimal(10,0) NOT NULL default '0',
UNIQUE KEY `id` (`id`)
) TYPE=MyISAM;
#
# Dane do ćwiczeń
#
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO
http://www.php.pl
`groups`
`groups`
`groups`
`groups`
`groups`
`groups`
`groups`
`groups`
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
VALUES
(55,
(54,
(53,
(51,
(52,
(49,
(48,
(47,
1,
1,
1,
1,
1,
1,
1,
1,
'Win 2000', '1210000');
'Win Milenium', '1130000');
'Win 98', '1120000');
'NT', '1200000');
'Win 95', '1110000');
'NO NT', '1100000');
'linuks', '2000000');
'windows', '1000000');
Strona 4 z 13
INSERT
INSERT
INSERT
INSERT
INSERT
INTO
INTO
INTO
INTO
INTO
`groups`
`groups`
`groups`
`groups`
`groups`
VALUES
VALUES
VALUES
VALUES
VALUES
(46,
(57,
(56,
(61,
(62,
1,
1,
1,
1,
1,
'systems', '0000000');
'Win 2003', '1230000');
'Win XP', '1220000');
'Slack', '2100000');
'Debian', '2200000');
#
# select * from groups
#
+----+---------+--------------+---------+
| id | cluster | name
| level
|
+----+---------+--------------+---------+
| 64 |
1 | Windows Ce
| 1300000 |
| 55 |
1 | Win 2000
| 1210000 |
| 54 |
1 | Win Milenium | 1130000 |
| 53 |
1 | Win 98
| 1120000 |
| 51 |
1 | NT
| 1200000 |
| 52 |
1 | Win 95
| 1110000 |
| 49 |
1 | NO NT
| 1100000 |
| 48 |
1 | linuks
| 2000000 |
| 47 |
1 | windows
| 1000000 |
| 46 |
1 | systems
| 0000000 |
| 57 |
1 | Win 2003
| 1230000 |
| 56 |
1 | Win XP
| 1220000 |
| 61 |
1 | Slack
| 2100000 |
| 62 |
1 | Debian
| 2200000 |
+----+---------+--------------+---------+
2. Operacje na danych drzewa
Praktyczne porady odnośnie tego jak korzystać ze zgromadzonych danych.
1. Podstawowe operacje - czego potrzebujemy?
Zaczynamy wiec operować na naszym drzewku. Tworzymy klasę Trees. Czego
wymagamy od naszej klasy? Podstawowym jej zastosowaniem jest pobieranie
struktury drzewa do odpowiednio sformatowanej tablicy. Nie możemy zapomnieć
o tym że możemy zechcieć pobrać dzieci tylko do określonej głębokości, lub tylko
jedną z gałęzi drzewa, a czasami nawet i to i to. Następnie chcielibyśmy tworzyć
nowe drzewa oraz dodawać do nich nowe dzieci. Przydałaby się również ręczna
kontrola nad tym ile poziomów zagnieżdżenia jest możliwych. Kierując się tymi
potrzebami można wstępnie ustalić metody i własności klasy obsługującej
drzewka.
http://www.php.pl
Strona 5 z 13
class trees{
//maksymalny poziom zagnieżdżenia
var $maxNest //liczone od zera
//do
obsługi
bazy
danych
wykorzystuje
zewnętrzny
kompatybilny z ADoDB
//trzeba go więc załadować do naszego obiektu
function trees(){
obiekt
$this->db =& new db;
return true;
}
//pobieranie całego drzewa o określonym numerze grupy ( cluster
) i określonej głębokości
function getAll( $cluster , $depth = null ){
return $arrTree;
}
//pobieranie gałęzi drzewa o określonym numerze grupy ( cluster
) i określonej głębokości zaczynającej się w wartości której id
podaliśmy
function getPart( $parent_id, $depth = null ){
return $arrTree;
}
//nowe drzewo (grupa wartości), z korzeniem o podanej nazwie
function newGroup( $name ){
return TRUE;
}
//nowa wartość o podanej nazwie będąca
podanym id
function newChild( $name, $parentId ){
dzieckiem
wartości
return TRUE;
}
//skasuj gałąż drzewa od wartości której id podaliśmy
function delete( $id ){
return TRUE;
}
}
http://www.php.pl
Strona 6 z 13
o
2. Zapytania SQL
Do realizacji powyższych zadań będą nam potrzebne czasem dość
skomplikowane zapytania do bazy danych. Stanowią one właściwie o wszystkim,
ponieważ nie lubię używać php do obrabiania danych z bazy i dlatego zapytania
przejmują cały ciężar formatowania i pozyskiwania danych. Postaram się
dokładnie opisać oraz podać przykłady prezentowanych zapytań tak, aby ich
działanie było jasne.
2.1. Pobieranie danych o rodzicu
Zapytaniem, które będzie powtarzać się kilkakrotnie jest zapytanie o dane
rodzica. Wygląda ono następująco:
SELECT cluster,SUBSTRING(level, 1, INSTR(level,'0')-1) AS cutLevel,
INSTR(level,'0')-1 AS depth
FROM groups
WHERE id=[*parent_id*]
#
#Czyli dla rodzica "WINDOWS":
#
SELECT cluster,SUBSTRING(level, 1, INSTR(level,'0')-1) AS cutLevel,
INSTR(level,'0')-1 AS depth
FROM groups
WHERE id=47
#
#Uzyskany wynik to:
#
+---------+----------+-------+
| cluster | cutLevel | depth |
+---------+----------+-------+
|
1 | 1
|
1 |
+---------+----------+-------+
Dzięki temu zapytaniu pobieramy z bazy numer grupy rodzica (cluster), jego
level z którego od razu usuwamy końcowe zera (cutLevel) oraz głębokość
(depth). Możecie zauważyć na przykładzie tego zapytania, że dzięki levelowi
możemy wyznaczyć również głębokość zagnieżdżenia danej wartości.
Odczytywanie głębokości polega na odczycie, na którym miejscu pojawia się w
levelu pierwsze zero i odjęciu od tej wartości jedynki. Czyli dla "WINDOWS" level
wynosi 1000000, zero znajduje się na drugim miejscu, odejmujemy od dwójki
jedynkę i już nam wychodzi że "WINDOWS" jest wartością o pierwszym stopniu
zagnieżdżenia
http://www.php.pl
Strona 7 z 13
2.2. Pobieranie struktury całego drzewa
SELECT *, INSTR(level,'0')-1 AS depth
FROM groups
WHERE cluster = [*cluster*] AND
INSTR(level,'0')-1 <= [*depth*]
ORDER BY level
#
#Przykład dla naszej grupy
#
SELECT *, INSTR(level,'0')-1 AS depth
FROM groups
WHERE cluster = 1 AND
INSTR(level,'0')-1 <= 5
ORDER BY level
#
#A jego wynik:
#
+----+---------+--------------+---------+-------+
| id | cluster | name
| level
| depth |
+----+---------+--------------+---------+-------+
| 46 |
1 | systems
| 0000000 |
0 |
| 47 |
1 | windows
| 1000000 |
1 |
| 49 |
1 | NO NT
| 1100000 |
2 |
| 52 |
1 | Win 95
| 1110000 |
3 |
| 53 |
1 | Win 98
| 1120000 |
3 |
| 54 |
1 | Win Milenium | 1130000 |
3 |
| 51 |
1 | NT
| 1200000 |
2 |
| 55 |
1 | Win 2000
| 1210000 |
3 |
| 56 |
1 | Win XP
| 1220000 |
3 |
| 57 |
1 | Win 2003
| 1230000 |
3 |
| 48 |
1 | linuks
| 2000000 |
1 |
| 61 |
1 | Slack
| 2100000 |
2 |
| 62 |
1 | Debian
| 2200000 |
2 |
+----+---------+--------------+---------+-------+
Zapytanie jest raczej proste. Proste dzięki kolumnie level która w łatwy sposób
uporządkowuje wartość w odpowiedniej kolejności. Zauważmy również, że jeśli
nie podamy opcjonalnego parametru $depth to głębokość jest on równa
$maxNest.
2.3. Pobieranie struktury gałęzi drzewa
Następne zapytanie ma za zadanie zwrócić strukturę określonej gałęzi drzewa.
Początkiem tej gałęzi jest wartość, której id podajemy. Jeśli chodzi o poziom
zagnieżdżenia to działa analogicznie jak w poprzednim przykładzie. Zanim
wykonamy to zapytanie jesteśmy zmuszeni spytać o dane rodzica.
http://www.php.pl
Strona 8 z 13
SELECT
*,
INSTR(level,'0')-1
AS
depth,
INSTR(level,'0')-1
[*parent_depth*] AS relativeDepth
FROM groups
WHERE cluster = [*parent_cluster*] AND
level LIKE '[*parent_cutLevel]%' AND
INSTR(level,'0')-1 <= ([*$depth*]+[*parent_depth*])
ORDER BY level
-
#
#Czyli w naszym przypadku:
#
SELECT *, INSTR(level,'0')-1 AS depth, INSTR(level,'0')-1 - 1 AS
relativeDepth
FROM groups
WHERE cluster = 1 AND
level LIKE '1%' AND
INSTR(level,'0')-1 <= (5+1)
ORDER BY level
+----+---------+--------------+---------+-------+---------------+
| id | cluster | name
| level
| depth | relativeDepth |
+----+---------+--------------+---------+-------+---------------+
| 47 |
1 | windows
| 1000000 |
1 |
0 |
| 49 |
1 | NO NT
| 1100000 |
2 |
1 |
| 52 |
1 | Win 95
| 1110000 |
3 |
2 |
| 53 |
1 | Win 98
| 1120000 |
3 |
2 |
| 54 |
1 | Win Milenium | 1130000 |
3 |
2 |
| 51 |
1 | NT
| 1200000 |
2 |
1 |
| 55 |
1 | Win 2000
| 1210000 |
3 |
2 |
| 56 |
1 | Win XP
| 1220000 |
3 |
2 |
| 57 |
1 | Win 2003
| 1230000 |
3 |
2 |
+----+---------+--------------+---------+-------+---------------+
Zapytanie znajduje wszystkie dzieci z danej grupy (cluster) 1, które maja level
'[obcięty level rodzica]%' z głębokością podaną przez użytkownika lub
maksymalną ($maxNest) oraz zwraca wszystkie dane z tabeli plus głębokość
bezwzględną (liczoną od korzenia drzewa) oraz głębokość względna liczoną od
pierwszego elementu rodzica.
2.4. Dodanie nowego drzewa
Zapytanie realizujące dodanie nowego drzewa (korzenia). Już wcześniej
wspominałem, że elementem nadrzędnym i niepodlegającym tym reguła jest
element o levelu 0000000. Jest to rodzic dla całego drzewa. Stworzenie nowego
drzewa polega, więc na umieszczeniu nowego rodzica o danej nazwie, unikalnym
numerze cluster oraz levelu 0000000.
INSERT INTO groups ( cluster, name, level )
SELECT MAX(cluster)+1,'[*name*]', RPAD('0',".[*levelNum*],'0') FROM
groups
http://www.php.pl
Strona 9 z 13
2.5. Dodanie nowego dziecka do danego rodzica
Zapytanie mające za zadanie dodać nowe dziecko do rodzica. Zauważmy pewna
zależność. Dziecko dla danego rodzica posiada zawsze jedna cyfrę więcej w
levelu. W tej metodzie niestety cyfry te są ograniczone (1-9) dlatego wiec nie
możemy sobie pozwolić na marnotrawstwo przestrzeni i nie możemy po prostu
pobierać najwyższego dostępnego levelu i dodawać do niego jedynki. Zauważ ze
ta metoda sprawdza się dopóki nie usuniemy jakiejś grupy. Np. jeśli chcemy
dodać dziecko do rodzica, o levelu 1100000 i posiada on dziecko 1120000
(dziecko o levelu 1110000 zostało kiedyś usunięte) to w takim przypadku już
nigdy nie wykorzystalibyśmy levelu usuniętego. Zapytanie, sql musi wiec
przewidzieć ewentualność usunięcia grupy i umożliwić wypełnienie tego miejsca
inną.
INSERT into groups (cluster, name, level)
SELECT
[*parent_cluster*],
'[*name*]',p.level,RPAD(SUBSTRING(g.level,
1,
[*parent_depth*]
+
1)+1 ,[*levelNum*],'0')
FROM groups AS g
LEFT
JOIN
groups
AS
p
ON
CONCAT(g.cluster,'|',RPAD(SUBSTRING(g.level, 1, [*$parent_depth*]+
1)+1 ,[*$levelNum*],'0')) = CONCAT(p.cluster,'|',p.level)
WHERE g.cluster = [*parent_cluster*] AND
g.level LIKE CONCAT( '[*parent_ cutLevel *]','%') AND
[*parent_depth*]+ 1 <= [*levelNum*]-1 AND
p.level IS NULL
LIMIT 1
#
# Przykład dodania wartości "WINDOWS CE" do rodzica "WINDOWS"
#
INSERT INTO groups (cluster, name, level)
SELECT 1, 'Windows Ce',RPAD(SUBSTRING(g.level, 1, 1 + 1)+1 ,7,'0')
FROM groups AS g
LEFT
JOIN
groups
AS
p
ON
CONCAT(g.cluster,'|',RPAD(SUBSTRING(g.level, 1, 1 + 1)+1 ,7,'0')) =
CONCAT(p.cluster,'|',p.level)
WHERE g.cluster = 1 AND
g.level LIKE CONCAT( '1','%') AND
1 + 1 <= 7-1 AND
p.level IS NULL
LIMIT 1
Skomplikowane? Nie! Zapytanie porostu kopiuje tabele groups na tabele g i p
pobiera z tabeli g wszystkie levele oraz dodaje do nich jeden, następnie
sprawdza czy istnieje taki level w tabeli p. Jeśli nie to dodaje grupę z takim
właśnie levelem do określonego rodzica. Równocześnie zapytanie pilnuje, aby nie
dodać grupy zagnieżdżonej bardziej niż wymagamy to od $maxNest (poprzez
$levelNum).
http://www.php.pl
Strona 10 z 13
2.6. Usuwanie gałęzi
Ostatnim zapytaniem Jest zapytanie realizujące usuwanie gałęzi drzewa od
wartości której id zostało podane:
DELETE
FROM groups
WHERE level LIKE '[*parent_cutLevel*]%' AND
cluster =[*parent_cluster*]
3. Uzupełnienie kodu, czyli ostateczny wygląd klasy
Nadszedł czas na wstawienie kodu i zapytań do schematu klasy, który
napisaliśmy wcześniej.
class trees{
var $maxNest = 5;
//liczone od zera()
function trees(){
$this->db =& new db;
return true;
}
function getAll( $cluster , $depth = null ){
$depth = (is_null($depth))?$this->maxNest:$depth;
return $this->db->getArray("
SELECT *, INSTR(level,'0')-1 as depth
FROM groups
WHERE cluster = ".$cluster." AND
INSTR(level,'0')-1 <= ".$depth."
ORDER BY level
");
}
function getPart( $parent_id, $depth = null ){
$depth = (is_null($depth))?$this->maxNest:$depth;
$r = $this->db->getRow("
SELECT
cluster,SUBSTRING(level,
INSTR(level,'0')-1) as cutLevel, INSTR(level,'0')-1 as depth
FROM groups
WHERE id=".$parent_id
);
1,
if(!$r){return false;}
http://www.php.pl
Strona 11 z 13
return $this->db->getArray("
SELECT
*,
INSTR(level,'0')-1
as
INSTR(level,'0')-1 - ".$r['depth']." as relativeDepth
FROM groups
WHERE cluster = ".$r['cluster']." AND
level like '".$r['cutLevel']."%' AND
INSTR(level,'0')-1
(".$depth."+".$r['depth'].")
ORDER BY level
");
depth,
<=
}
function newGroup( $name ){
$levelNum = $this->maxNest+2;
return $this->db->execute("
INSERT INTO groups ( cluster, name, level )
SELECT
MAX(cluster)+1,'".$name."',
RPAD('0',".$levelNum.",'0') FROM groups
");
}
function newChild( $name, $parentId ){
$levelNum = $this->maxNest+2;
$parent = $this->db->getRow("
SELECT
cluster,
SUBSTRING(level,
1,
INSTR(level,'0')-1) as cutLevel, INSTR(level,'0') - 1 as depth FROM
groups
WHERE id=".$parentId
);
if(!$parent){return false;}
return $this->db->execute("
INSERT INTO groups (cluster, name, level)
select ".$parent['cluster'].", '".$name."',RPAD(SUBSTRING(g.level,
1, ".$parent['depth']." + 1)+1 ,".$levelNum.",'0')
FROM groups AS g
LEFT
JOIN
groups
AS
p
ON
CONCAT(g.cluster,'|',RPAD(SUBSTRING(g.level, 1, ".$parent['depth']."
+ 1)+1 ,".$levelNum.",'0')) = CONCAT(p.cluster,'|',p.level)
WHERE g.cluster = ".$parent['cluster']." and
g.level
like
CONCAT(
'".$parent['cutLevel']."','%')
and
".$parent['depth']." + 1 <= ".$levelNum."-1 and p.level is null
limit 1
");
}
function delete( $id ){
$r = $this->db->getRow("
http://www.php.pl
Strona 12 z 13
SELECT
cluster,
INSTR(level,'0')-1) as cutLevel
FROM groups
WHERE id=".$id
);
SUBSTRING(level,
1,
if(!$r){return false;}
return $this->db->execute("
DELETE
FROM groups
WHERE level like '".$r['cutLevel']."%' AND
cluster =".$r['cluster']);
}
}
Przykład wykorzystania klasy:
$t =& new trees;
$arr = $t->getPart(47);
foreach($arr as $val){
print
str_repeat("____",
'.$val['name']."<br />";
$val['depth']).'
'.$val['id'].'
}
3. Podsumowanie
Przedstawiona tu metoda tworzenia drzewek nie jest idealna. Posiada wiele wad,
z których największa jest to ze rodzic może posiadać tylko dziewięcioro dzieci.
Jednak moim celem nie było podanie gotowego rozwiązania na tacy, lecz
wskazanie pewnego kierunku. Kierunku pominiętego w artykułach traktujących o
drzewkach obecnych na dzień dzisiejszy w Internecie. Jeśli chcesz uzyskać cos
więcej możesz posiedzieć nad ta koncepcja i ją zmodyfikować. Myślę, że bez
trudu ją rozbudujesz tak, aby była dostosowana do o wiele większych wymagań.
Moc należy do was ("Kapitan Planeta").
http://www.php.pl
Strona 13 z 13