Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [MySQL][PHP]Stworzenie menu kategori w postaci drzewa
Forum PHP.pl > Forum > Przedszkole
Bednarus3
W SQL mam tablicę zawierającą dwie kolumny: id_kategorii oraz id_rodzica. Jeżeli id_rodzica wynosi 0 to jest to kategoria na poziomie 1, a późniejsze podkategorie mają id_rodzica w zależności w której kategorii się znajdują. Chcę to wyświetlić w postaci listy tak, że główne kategorie będą całkiem po lewej, pod nią będą podkategorie przesunięte lekko w prawo i tak dalej. Koncepcja jest prosta. Wyszukuje kategorie z id_rodzica = 0. Potem wyszukuję kategorie, które w id_rodzica mają nr kategorii nadrzędnej i tak w głąb. Gdy dojdziemy do kategorii, która nie ma potomka. Wracamy o poziom wyżej, aż do zera. Nie mam pomysłu jak to zaimplementować.
Turson
Rekurencja ?
nospor
Zacznij od implementacji porządnie bazy. Zainteresuj sie strukturami drzewiastymi, np. drzewka IP. Wowcza zycie bedzie piękniejsze.

Jesli jednak nie zamierzasz tego poprawiac, to pozostaje ci to co napisał Turson: rekurencja i zazynanie bazy bezsensownymi zapytaniami smile.gif
golabow
Napisałem taki skrypt, który może być przykładem rozwiązania z rekurencją:

  1. <?php
  2. class Tree {
  3. private $id_kategorii;
  4. private $id_rodzica;
  5. // tablica z obiektami ktore sa dziecmi danego wezla
  6. private $dzieci;
  7.  
  8. public function __construct( $id_kategorii, $id_rodzica )
  9. {
  10. $this->id_kategorii = $id_kategorii;
  11. $this->id_rodzica = $id_rodzica;
  12. // zmienna $db to uchwyt do twojej bazy danych
  13. $q = "SELECT * FROM tree WHERE id_rodzica=$this->id_kategorii"; // tree to tablica z wezlami
  14. $queryResult = $db->query( $q );
  15.  
  16. while ( $row = $queryResult->fetch_assoc() )
  17. {
  18. $this->dzieci[] = new Tree( $row['id_kategorii'], $row['id_rodzica'] );
  19. }
  20. }
  21.  
  22. public function getHtml( $html = '' )
  23. {
  24. if ( !empty( $this->dzieci ) )
  25. {
  26. $html .= '<li>'.$this->id_kategorii.'<ul>';
  27. }
  28. else
  29. {
  30. $html .= '<li>'.$this->id_kategorii.'</li>';
  31. return $html;
  32. }
  33.  
  34. foreach( $this->dzieci as $dziecko )
  35. {
  36. $html = $dziecko->getHtml( $html );
  37. }
  38.  
  39. $html .= '</ul></li>';
  40. return $html;
  41. }
  42. }
  43.  
  44. $ob = new Tree(1, 0);
  45. $htmlTree = $ob->getHtml();
  46. ?>
  47.  
  48. <ul>
  49. <?php echo $htmlTree; ?>
  50. </ul>


Tworzysz obiekt klasy podając id węzła i jego ojca od którego ma być generowane drzewo. Tutaj założyłem że domyślną id_rodzic każdego obiektu w bazie jest wartość 0 która oznacza że nie ma ojca, możliwe że wartość -1 byłaby bardziej jasna. W każdym bądź razie tutaj podaje id pierwszego elementu zbioru który jest korzeniem drzewa.
Bednarus3
nospor, nie mogę zaimplementować od nowa bazy, bo to co robię jest rozbudowaniem, do własnych potrzeb, innego projektu, a dokładnie OscGold.
Jako, że jestem słabiutki z programowania obiektowego nie wykorzystałem tego co napisał kolega golabow, bo po prostu nie rozumiem jak to działa. Za to, poprzez analogię, stworzyłem własną funkcję rekurencyjną, która robi to o co mi chodziło.
  1.  
  2. db_connect();
  3. echo '<select name="nazwa" multiple="multiple">';
  4. tree(0);
  5. echo '</select>';
  6. db_close();
  7. function tree($category_id)
  8. {
  9. $result = mysql_query("SELECT Count(`categories_id`) FROM `categories` WHERE `parent_id` = '{$category_id}'");
  10. $row = mysql_fetch_assoc($result);
  11. if($row['Count(`categories_id`)'] == 0)
  12. {
  13. }
  14. else
  15. {
  16. $result = mysql_query("SELECT `categories_id` FROM `categories` WHERE `parent_id` = '{$category_id}'");
  17. if((mysql_num_rows($result) > 0))
  18. {
  19. while($row = mysql_fetch_assoc($result))
  20. {
  21. $result_name = mysql_query("SELECT `categories_name` FROM `categories_description` WHERE `categories_id`=".$row['categories_id']);
  22. $row_name = mysql_fetch_assoc($result_name);
  23. echo '<option>'.$row_name['categories_name'].'</option>';
  24. tree($row['categories_id']);
  25. }
  26. }
  27. }
  28. }

Mam jeszcze jeden problem. Chcę zrobić, żeby każdy poziom w menu miał wcięcie od lewej strony. Im niższy poziom, tym większe wcięcie. Kombinowałem z <optgroup>, ale rozjeżdżało mi się.
Turson
Z każdym kolejnym zagłębieniem przesyłaj zmienną jako argument funkcji, a zmienną inkrementuj. Potem ile wynosi zmienna tyle px wcięcia np.
nospor
Pierwsze zapytanie w funkcji, ktore liczy liczbe rekordow jest niepotrzebne, bo tak czy siak potem tę liczbę liczysz.

Co do wcięć, to dodaj drugi parametr dla funkcji
function tree($category_id, $level)

Pierwsze wywolanie: tree(0,0)
Kolejne wywolania: tree($row['categories_id'], $level+1);

Zas tutaj
echo '<option>'.$row_name['categories_name'].'</option>';
dodawaj przed nazwą tyle spacji jaką wartosc ma zmienna $level. Możesz też tę ilosc przemnozyc przez 2 czy 3 dla wiekszego wcięcia.
Bednarus3
Dzięki nospor. Osiągnąłem dokładnie to co chciałem. Tak myślałem, żeby wcięcia zrobić w oparciu o inkrementowaną zmienną, ale bardzo długo bym kombinował, żeby przekazać ją jako argument funkcji.
Edit:
Jeszcze jedna sprawa. Chcę dodawać do bazy nową kategorię. Dane kategorii rozbite są na dwie tabele. W jednej znajduję się ID_kategorii(auto_increment) oraz adres_obrazka, w drugiej ID_kategorii oraz nazwa_kategorii. Czy da się zrobić inaczej, żeby wprowadzić dane odpowiednio do tych dwóch tabel, niż wprowadzenie danych do pierwszej tabeli, pobranie z niej wartości auto_increment, następnie wpisanie tej wartości, do drugiej tabeli, jako ID_kategorii, oraz odpowiedniej dla niej nazwa_kategorii
Turson
Masz trochę dziwnie rozwiązane. Czemu auto increment nie jest druga tabela a pierwsza?
Wstawiasz do pierwszej, bierzesz lastInsertId i wstawiasz do drugiej.
nospor
Cytat
Czy da się zrobić inaczej, żeby wprowadzić dane odpowiednio do tych dwóch tabel, niż wprowadzenie danych do pierwszej tabeli, pobranie z niej wartości auto_increment, następnie wpisanie tej wartości, do drugiej tabeli, jako ID_kategorii, oraz odpowiedniej dla niej nazwa_kategorii
Nie, musisz robić tak jak wlasnie napisales.

@Turson przeciez wyraznie w drugim poscie napisal, ze to nie jest jego struktura. On tylko z niej korzysta
Bednarus3
Piszę skrypt ułatwiający obsługę oscGold do własnych potrzeb. Bazę nie ja tworzyłem tylko jest gotowa dla oscGold. Dlatego muszę operować na tym co mam. W zapytaniu chodzi o to, że jak wprowadzę dane do pierwszej tabeli, następnie użyję lastInsertId lub odczytam wartość auto_increment z tabeli information_schema.TABLES, to może się zdarzyć, że między wprowadzeniem danej, a oczytaniem jej ID ktoś inny wprowadzi dane do tabeli i w drugiej tabeli będę miał ID wyższe i zostaną zapisane błędne dane, które powinny być zapisane dla niższego ID.
nospor
Korzystaj z LAST_INSERT_ID.
A jesli baaaaaaardzo sie boisz, ze w tym ulamku setnej sekundy ktos ci sie wstrzeli ze swoim ID, to ustaw LOCK na tabelach
Turson
Jeżeli mówimy o PHP MySQLowym last insert id, to on przechowuje id tego samego połączenie, wiec jak ktos ma sie wstrzelic?
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.