środa, 6 czerwca 2007

Zagnieżdżane kategorie

Zagnieżdżone kategorie w sklepie/koszyku czy innej stronie mogą sprawić lekki problem początkującemu programiście PHP. Poniżej postaram się przedstawić szybką metodę na stworzenie warstwy danych i warstwy logicznej (zgodnie z modelem MVC, który polecam) prostego systemu kategorii, który można łatwo wprowadzić w swój projekt.

Założenia
Do stworzenia kategorii użyjemy struktury drzewa, która idealnie się tutaj nadaje. Podstawowym problemem jest jak zaznaczyć zależności dzieci i pobierać to z jednopoziomowej bazy danych. Istnieje wiele podejść, jednak osobiście preferuje strukturę drzewa. Algorytm polega na oznaczaniu kolejnym numerkiem liścia/korzenia począwszy od lewej w dół. Myślę ze obrazek dobrze ilustruje założenia.


Warstwa danych
Podejrzewam, że najpopularniejszą bazą danych jest wszędobylski MySQL - darmowy, szybki i ogólnodostępny. Czego chcieć więcej. Przyjmuję założenie, że posiadasz już skonfigurowaną bazę danych i potrafisz wykonywać polecenia SQL. A więc do dzieła!

Pierwszą rzeczą, którą musimy zrobić jest oczywiście utworzenie tabeli zawierającej rekordy z poszczególnymi kategoriami. Czego potrzebujemy? W sumie niewiele. Wystarczy nam nazwa kategorii, opis i oczywiście pola na oznaczenie dwóch stron rekordu - lewej i prawej.
CREATE TABLE `cart_categories` (
`cac_id` int(11) NOT NULL auto_increment,
`cac_left` int(11) NOT NULL,
`cac_right` int(11) NOT NULL,
`cac_name` varchar(200) collate latin1_general_ci NOT NULL,
`cac_desc` longtext collate latin1_general_ci NOT NULL,
PRIMARY KEY (`cac_id`),
KEY `sides` (`cac_left`,`cac_right`)
) ENGINE=MyISAM;

Tabela jest niezwykle prosta, mamy wszystkie pola o których mówiliśmy wcześniej i oczywiście pole auto_increment oznaczające id danego rekordu. Nic prostszego nie mogliśmy wymyślić.

Warstwa logiki
No cóż, jednak to wszystko trzeba gdzieś przetwarzać. Posiadając prosty model danych musimy nadrobić wszystko w modelu logiki aplikacji. Ale bez paniki! Wszystko jest dla ludzi :)

Jeżeli chcesz pobrać drzewo za pomocą wartości left i right, musisz najpierw zidentyfikować wartości jakie ma element będący korzeniem. Przykładowo dla elementu 'Auto' powinieneś pobrać elementy o wartości left pomiędzy 2 i 11 (jako iż takie wartości posiadają jego pola left i right).
SELECT * FROM cart_categories WHERE cac_left BETWEEN 2 AND 11 ORDERY BY cac_left;
W wyniku tego otrzymasz wszystkie rekordy będące dziećmi rekordu samochód. Jedynym problemem jest brak poprawnej struktury drzewa, którą chcielibyśmy wyświetlić lub przetworzyć. Otrzymamy to za pomocą prostego algorytmu w PHP.
function fillDepths($sqlResults) {
$stack = array();

// przeprowadzamy iterację dla każdego elementu
foreach($sqlResults as $k => $v) {
// sprawdzamy stos
if (count($stack)>0) {
// sprawdzamy czy powinniśmy ściągnąć coś ze stosu
while ($stack[count($stack)-1]<$v['cac_right']) { array_pop($stack); } } // zapisanie glebokosci $sqlResults[$k]['depth'] = count($stack); // dodajemy do stosu $stack[] = $v['cac_right']; } }
Funkcja ta za pomocą stosu wyznacza głębokości (wcięcia) poszczególnych elementów drzewa, co pozwala na swobodne wyświetlanie drzewa w warstwie prezentacji. Funkcja jest pozbawiona części mniej istotnych funkcjonalności dla uproszczenia zapisu.

Bread Crumbs
Częstym problemem jest wyświetlenie ścieżki gdzie aktualnie się znajdujemy (pomocne przy tworzeniu tzw. bread crumbs). W przypadku takiej struktury jest to niezwykle proste. Wszystko załatwia się jednym zapytaniem. Możemy zauważyć, że wszyscy rodzice elementu o wartościach left 4 i right 5 mają wartość left mniejszą, natomiast wartość right większą. Wykorzystajmy to.

SELECT cac_name FROM cart_categories WHERE cac_left <> 5 ORDER BY cac_left ASC;W ten sposób otrzymujemy posortowaną listę rodziców zaczynającą się od korzenia.

Liczenie potomków
Równie prosto określić ile dzieci posiada dany element. Określa się to wzorem.
potomkowie = (cac_right - cac_left - 1) / 2
Dodawanie kategorii
Aby dodać kategorię, musimy przesunąć wszystkie elementy znajdujące się po prawej stronie od miejsca gdzie chcemy dodać element. Jest to niezwykle proste, wystarczy zwiększyć wartość left i right o 2 danym elementom. Jedyne co musimy wiedzieć to miejsce w którym chcemy dodać element.

Już niedługo opublikuję w tym miejscu w pełni działającą klasę implementującą powyższy kod.

0 komentarzy: