Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP/MYSQL] Struktura drzewa - wielokrotne zagniezdzenie
Forum PHP.pl > Forum > PHP
xajart
Witam. 


Opracować musze drzewo o wielokrotnym zagniezdzeniu, jednak mi to nie wychodzi - pewnie z braku wiedzy w dziedzinie php. 

Mam tabele w takiej formie 

Id | sub | poziom | pozycja | name
3. | 0 | 0 | 1   | A 
2. | 1 | 1 |  1  | B
1. | 2 | 2 |   2 | C
4. | 2 | 2 |   1 | D
5. | 1 | 1 |  1  | E
6. | 0 | 0 | 0   | F


Struktura powinna wobec tego wyglądać tak:
A.
- B
-- D
-- C
- E
FRozpisałem sobie taki algorytm

Kod
for ($i=1; $i<=$rows; $i++){
  echo $id[$i];
 $t[] = NULL; 
 for ($j=1; $j<=rows; $j++){
  if ($sub[$j] == $id[$i] AND $sub[$j] != t[$j]){
   for ($k=0; $k<=count($t);$k++){
    echo $id[$j];
    $t[$j] == $id[$j]
   }
  }
 }
}


Niestety kod nie działa tak jak zamierzałem, z racji że nie wiem jak zdefiniować te tabele, bo mają pełnić one role taką że jeżeli znajdzie element w pętli J i wyświetli dany element, to ID tego elementu powinno zostać zapisane w BD, by algorytm mógł je pominąc przy kolejnym sprawdzaniu sprawdzaniu. Tylko. Zaś jak przejdzie do inkrementacji I to tabela powinna zostać wyczyszczona.



Nie wiem czy dobrze rozumuje, prosiłbym was o pomoc z rozwiązaniem tego, bo sobie nie potrafie z tym dać rady, co do powyższych tabel, to opcjonalnie powinno wystarczyć id i sub alternatywnie poziom określa poziom zagłębienia 0 - główne, 1 -pz pierwszy, 2 - pz drugi itd.

W sumie to zależy mi do zagłębienia do poziomu 3, ale jeżeli by realizowało to więcej zagłębień było by bardziej przyszłościowe, bym za pare miesięcy nie musiał się zastanawiać jak 4 poziom czy 5 zrealizować.
_olo_1984
może ten tekst coś Ci pomoże: http://artykuly.zyxist.com/czytaj.php/drzewa_w_php_i_mysql
xajart
niestety zabardzo mi nie pomogła ta wskazówka, musiałbym przebudowywać baze danych a to by było związane również z przebudową serwisu który ją obsługuje. Mogłbym dodać ewentualnie dwie takie kolumny ale wtedy sensu by nie miały te które są dodane. 

Poza tym napewno w jakiś sposób da się to rozwiązać, skoro mamy 1 tabele z numerem ID(unikalnym) do którego z drugiej tabeli jest przypisane 0 - co oznacza że nie jest potomkiem i ją wyświetlamy, bierzemy kolejny element jeżeli ma wartość inną niż 0 tzn że jest potomkiem numeru na który wskazuje i go wyświetlić. Do tego momentu mi działą wszystko, tylko kwestia raczej tego jak wyświetlić kolejne pokolenia. skoro mamy tak

id | sub | pozycja |name
1 | 0 | 0 | A 
2 | 1 | 1 | B
3 | 2 | 2 | C

to analogicznie wiemy że:
id 1 - jest rodzicem bo nie jest przyporządkowana do id, sub=0  oraz pozycja=0 co znaczy że jest rodzicem
id 2 - jest potomkiem id=1 (bo sub=1) oraz wiemy że jest potomkiem 1 poziomu bo pozycja=1
id 3 - jest potomkem id =2 (bo sub=2) oraz wiemy że jest potomkiem 2 poziomu bo pozycja=2

tylko kwestia tego jak to oprogramować by wyświetlić całą strukturę. Wprowadzanie nowych elementów nie stanowi problemu łatwo to określić. Ale wyświetlenie tej struktury, powinno się dać bez problemu (tylko jeszcze do tego etapu nie doszedłem).
EarthCitizen
A ile masz tych rekordów? W Twoim przypadku musisz sobie napisać np funkcję:
  1. <?php
  2. function drzewo($id = 0) {
  3.    if ($id == 0) {
  4.        $sql = 'SELECT * FROM tabela WHERE sub = 0';
  5.    } else {
  6.        $sql = 'SELECT * FROM tabela WHERE sub = ' . (int)$id;
  7.    }
  8.  
  9.    $result = mysql_query($sql);
  10.  
  11.    if (mysql_num_rows($result)) {
  12.        while ($row = mysql_fetch_array($result)) {
  13.  
  14.            echo str_repeat('---', $row['pozycja']) . $row['name'] . '<br />';
  15.            drzewo($row['id']);
  16.  
  17.        }
  18.    }
  19. }
  20. ?>


Lecz już przy kilkuset rekordach będzie to mało wydajne...

Jeżeli potrzebujesz rozwiązania na kilka tysięcy i więcej i ma to chodzić stabilnie to wróć do artykułu, który przesłał Ci _olo_1984
xajart
Maksymalnie tych zagłebień będzie 10, Co do ilości rekordów to zaledwie kilkanaście.

Co do skryptu, to jest mniejwięcej to tylko że wyświetla poziomy wg pozycji w stylu:
A
-- B
-- C
---- D

Jednak jeżeli wg sub dla D jest ustawione podpięcie do B to powinno wyglądać tak

A
-- B
---- D
-- C

Niestety ten skrypt tego nie zrobi bo nie odnosi się w żaden sposób do kolumny SUB

A mi własnie o to chodzi jak zrobić wyświetlanie hierarchi zagniezdzeń wg kolumn ID i SUB
gdzie
ID (identyfikator unikalny), 
SUB 0 - rodzic
- Sub 1 - potomek rodzica
-- Sub 2 - potomek Sub1
--- Sub 3 - potomek Sub2 
itd.


Wiem że lepiej by było rozwiązać to metodą LEFT RIGHT, ale wiem też że w jakiś sposób napewno się da to wykorzystać bo skoro ja to widze, to w programie też powinno dać się to wyświetlić w jakiś sposób, dotego jeszcze kolumna pozycja wskazuje na którym poziomie zagnierzdzenia jest potomek.

Wymyśliłem taki algorytm, tylko byłbym wdzięczny jak by mi go teraz ktoś pomógł przenieść na kod php.

Tabele ID i SUB odczytuje jako dwie zmienne tablicowe A i B

zm. $i  = 1 | 2 | 3 |  4 |  5 |  6  |  7
ID,   A [  2 | 7 | 8 | 20| 21 | 23 | 26 ] 
Sub, B [  0 | 0 | 2 | 7  | 8  | 20 | 2   ]

B[1] = 0 TAK wypisz A[1], gdzie A[1]=2
- szukaj w B[] elementu 2 (pomijając B[1]) JEST wypisz A[3], gdzie A[3] = 8
-- szukaj w B[] elementu 8 (pomijając B[1,3]) JEST wypisz A[5], gdzie A[5] = 21
--- szukaj w B[] elementu 21 (pomijając B[1,3,5]) NIEMA
-- szukaj w B[] elemenu 8 (pomijajac B[1,3,5]) NIEMA
- szukaj w B[] elementu 2 (pomijając B[1,3,5]) JEST wypisz A[7], gdzie A[7] = 26
-- szukaj w B[] elementu 26 (pomijając B[1,3,5,7]) NIEMA
- szukaj w B[] elementu 2 (pomijając B[1,3,5,7]) NIEMA
szukaj w B[] elementu 0 (pomijajac B[1,3,5,7]) JEST wypisz A[2], gdzie A[2] = 7
- szukaj w B[] elementu 7 (pomijając B[1,3,5,7,2]) JEST wypisz A[4], gdzie A[4] = 20
-- szukaj w B[] elementu 20 (pomijając B[1,3,5,7,2,4]) JEST wypisz A[6], gdzie A[6]=23
--- szukaj w B[] elementu 23 (pomijając B[1,3,5,7,2,4,6]) NIEMA
-- szukaj w B[] elementu 20 NIEMA
- szukaj w B[] elementu 7 NIEMA
szukaj w B[] elementu 0 NIEMA - koniec skryptu.

Nie udało mi się napisać własnego skryptu, ale znalazłem coś takiego w sieci:
Kod
function drzewoKategorii ($kategorie = null, $rodzicId = 0, $zaglebienie = 0, $wynik = null)
{
   if ($wynik == null)
      $wynik = array();
   if ($kategorie == null) {
      $query = "select * from kategorie order by rodzic_id, nazwa";
      $kategorie = DB::getAll($query);   
   }
   foreach ($kategorie as $kat) {
      if ($r["rodzic_id"] == $rodzicId) {
         $r["zaglebienie"] = $zaglebienie;
         $wynik[] = $kat;
         $wynik = drzewoKategorii ($kategorie, $kat["id"], ($zaglebienie+1), $wynik);
      }
   }
   return $wynik;
}
$drzewo = drzewoKategorii();
foreach ($drzewo as $galaz) {
   for ($i=0; $i<=$galaz["zaglebienie"]; $i++) {
      echo '-';
   }   
   echo $galaz["nazwa"].'<br/>';
}


Tylko mam z tym problem bo nieobsługuje mi klasy DB::getAll() - bo nie ma PEAR
funkcje foreach() też mi nie obsługuje. Macie może jakieś pomysły jak to przerobić questionmark.gif
Bo musze to skończyć do poniedziałku, bo zaczynaja mi się sesja i bedę z tym w plecy sad.gif
EarthCitizen
Cytat
Niestety ten skrypt tego nie zrobi bo nie odnosi się w żaden sposób do kolumny SUB

Chyba się mylisz? Przecież masz w zapytaniu warunek WHERE sub...

Zrobiłem sobie tabelę, w której umieściłem rekordy tak:

Kod
ID    SUB    POZYCJA   NAME
1    0    1        A
2     1     2         B
3     1     2         C
4     2     3         D


i wynik funkcji drzewo() otrzymałem:

Kod
---A
------B
---------D
------C


Dla mnie oznaczenie A,B,C jest zupełnie niepotrzebne... Tak samo jak POZYCJA, ponieważ funkcja może sama określić pozycję poprzez inkrementację dodatkowego atrybutu, no ale to w tym konkretnym założeniu...
xajart
Cytat(EarthCitizen @ 25.01.2009, 06:28:52 ) *
Dla mnie oznaczenie A,B,C jest zupełnie niepotrzebne... Tak samo jak POZYCJA, ponieważ funkcja może sama określić pozycję poprzez inkrementację dodatkowego atrybutu, no ale to w tym konkretnym założeniu...


Masz rację ten skrypt działa, musiałem coś wcześniej zamieszać że mi niedziałało.

Mam pytanie jak zrobić na tej funkcji rekurencyjnej by zapisać pokolei do zmiennej tablicowej wartości $row['name'] a następnie zwrócić je tak by poza funkcją mógł sobie wyświetlić te elementy pokolei 


Pomogłem sobie sam smile.gif - przynajmniej się czegoś nauczyłem hehe.
Rozwiazanie (gdyby ktoś potrzebował):
Kod
function drzewo($id, $i) {

..... // to co wyżej - odwolania do BD

global $list;


   global $i; 
   
   $result = mysql_query($sql) or die(mysql_error());
 if (mysql_num_rows($result)) {
       while ($row = mysql_fetch_array($result)) {
    $i++;
    $list[$i] = $row['name']; // lub $row['id'] jeżeli chcemy mieć posortowane indeksy
       drzewo($row['id'], $i);
       }
 }
 return $list;
}


//odwolanie ze skryptu
$sort = drzewo (0, 0);


//i otrzymujemy listę $sort[] :)

Wiem że Ameryki nie odkryłem. Mimo wszystko dziękuje wszystkim za ogromna pomoc smile.gif

harbii
rozwiązanie podane w linku _olo_1984 jest ciekawe, ale jak bezpiecznie przenieść np Kościół do Przemysłowych aby reszta się nie rozsypała?

a może macie jeszcze inne ciekawe rozwiązanie zastosowania drzewek?
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.