Jakiś czas temu (blisko rok wstecz) zapragnąłem posiadać komentarze w formie drzewka (umieszczanie odpowiedzi na konkretny wpis hierarchicznie) czyli coś na zasadzie:
- pierwszy wpis
- odpowiedź na pierwszy wpis
- odpowiedź do odpowiedzi z pierwszego wpisu
- odpowiedź na odpowiedź odpowiedzi z pierwszego wpisu
- odpowiedź do odpowiedzi z pierwszego wpisu
- inna odpowiedź na pierwszy wpis
- odpowiedź na pierwszy wpis
- drugi wpis
- odpowiedź na drugi wpis
Zwie się to fachowo tablicą rekurencyjną, gdzie istnieją zależności na linii rodzic->dzieci. Na ówczesne potrzeby znalazłem jakieś gotowe rozwiązanie wykorzystujące dodatkowe pola (poziomy: lewa-prawa), i korzystam z tego do dziś. Wczoraj jednak zapragnąłem pozbyć się ręcznego definiowania podkategorii wiadomości i… rozpocząłem zabawę z wynalezieniem sensownego rozwiązania.
W tabeli utworzyłem dodatkowe pole zawierającą id kategorii, do której należy podkategoria. Jeśli kategoria nie należy do żadnej – przypisuję jej wartość 0 (zero). Teraz trzeba było odpowiednio zapętlić funkcję aby wywoływała samą siebie jeśli natrafi na podkategorię należącą do podkategorii, która jest podkategorią jeszcze innej podkategorii. Jak widać przesuwanie się na linii rodzic->dziecko może sięgać bardzo głęboko. Pierwszy z brzegu przykład zaczerpnięty z życia:
- sport
- piłka nożna
- liga polska
- ekstraklasa
- puchar ekstraklasy
- ekstraklasa
- reprezentacja
- puchar polski
- liga polska
- siatkówka
- piłka nożna
Jak widać podkategorii może być sporo i drzewko odpowiednio się rozrośnie. Na swoje potrzeby napisałem cztery funkcje, ale tylko dwie z nich odgrywają kluczową rolę, dwie pozostałe odnoszą się jedynie do pobierania danych z bazy. Najważniejszą z tych dwóch – kluczowych – funkcji jest ta, która zapętla się wywołując samą siebie tak długo, dopóki istnieją w bazie podkategorie przypisane do kategorii, która jest wywoływana jako parametr tej funkcji. Prawda, że proste? Co robi ta druga funkcja? Przypisuje strukturę drzewka do zmiennej na podstawie danych, które otrzymuje z pozostałych funkcji (w tym od tej kluczowej – zapętlającej się). W efekcie zwracany jest wynik, który można przypisać do jakiejś zmiennej, którą można z powodzeniem wykorzystać w szablonie (np. Smarty).
Dlaczego na to rozwiązanie wpadłem dopiero teraz, a nie rok temu, kiedy zacząłem zabawę nad tablicami rekurencyjnymi?
Z bardzo prostego powodu. Zakładałem bowiem, że potrzebuję X zagłębień w strukturę drzewka, gdzie X było liczbą rzędu 4-6 powtórzeń funkcji. I kiedy tak bawiąc się dochodziłem do wyników powyżej określonego z początku (X>6), uznawałem, że moje zabawy z tym są beznadziejne, a efekty dalekie od zadowalających. Stąd wykorzystanie czyjegoś pomysłu z dwoma dodatkowymi polami (lewa-prawa) i zaprzestanie dalszych prac nad poszukiwaniem rozwiązania prostszego acz skuteczniejszego.
Po co to wszystko?
Pomijając fakt, że aktualnie może to być wykorzystane przy budowie rozwijanego menu, popełniłem to z nieco innego powodu. Chodziło mi o to, aby wchodząc np. na wiadomości sportowe wyświetlały się nie tylko te, które są przypisane wyłącznie do tej kategorii, ale także te, które znajdują się w hierarchicznie ułożonym drzewku podkategorii i obejmują piłkę nożną, siatkówkę, ligę polską, ekstraklasę, reprezentację itp. Uzyskane w ten sposób wiadomości można przypisać do zmiennej i wstawić w szablon, tam gdzie się tego zapragnie. To zadanie załatwia mi jedna funkcja, która wyciąga podkategorie danej kategorii, i osiągnięte w ten sposób wyniki przekazuję do funkcji, która wydobywa z bazy konkretne wiadomości.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | function subcat( $cid ) { if ( $cid ){ for($j=0;$j<count($cid);$j++){ $q = 'SELECT * FROM tabela WHERE id_podkategorii = ''.(int)$cid.'';'; $r = mysql_query($q) or die(mysql_error()); if ( mysql_num_rows( $r ) > 0 ){ while( $row = mysql_fetch_assoc( $r )) { $tabela[] = $row; } } } if ($tabela) { for($i=0;$i<count($tabela);$i++){ $wyniki[] = $tabela[$i]; $cos = subcat($tabela[$i]['id_podkategorii']); if ($cos) { for ($a=0;$a<count($cos);$a++){ $wyniki[] = $cos[$a]; } } } } return $wyniki; } return null; } |
Żeby jednak tak słodko nie było, wykorzystując krótkie adresy urli, gdzie nie pojawia się id kategorii, a jedynie nazwa, natknąłem się na inny problem dotyczący zawartości pliku .htaccess. Otóż aby działało wszystko prawidłowo link powinien wyglądać mniej więcej tak:
news/kategoria/podkategoria/podkategoria/podkategoria/podkategoria/tytul,id_artykulu
Oczywiście podkategorii może być znacznie więcej, ale może być i mniej. Pytanie tylko czy adres ów jest jeszcze faktycznie krótki, czy już zaliczany powinien być do tych z rodziny dłuższych? A co jeśli dojdą jeszcze kolejne strony artykułu? Pojawi się kolejna zmienna. Stąd pomysł na drastyczne skrócenie do postaci:
news/kategoria/ostatnia_podkategoria/tytul,id_artykulu
Teraz czas na przebudowanie skryptu wyciągającego wiadomości z bazy… Tak jak wcześniej pisałem, mimo iż zbliżam się do końca, co jakiś czas wyskakuje coś nowego. Tak jak w tym przypadku…


