Drupal tutorial #1: Budowa atrakcyjnego menu w oparciu o moduł Taxonomy
Podczas budowy serwisu Ryneczek.net w którym w, którym umiejscowienie i wyświetlanie poszczególnych produktów bazuje na kategoriach i zawartych w nich terminach (moduł Taxonomy) natknąłem się na problem generowania funkcjonalnego i atrakcyjnego menu. Wprawdzie moduł Views dostarcza nam gotowy "widok" terminów dla danej kategorii w postaci bloku z listą tych terminów niemniej jednak kontrola generowanej w ten sposób treści jest bardzo ograniczona. Wystarczy, że nasze terminy tworzą hierarchiczną strukturę np. Pojazd -> Samochód -> 3-drzwiowy aby standardowy sposób (prezentujący elementy jeden za drugim) na niewiele się przydał...
Z pomocą przychodzą nam tzw. snippety - czyli fragmenty kodu mogące być bezpośrednio umieszczone treści bloku. Dużą ich kolekcję można znaleźć na drupal.org/node/21867. Snippety mogą być użyte tylko w konkretnym miejscu (bloku) w z góry określony sposób (kod snippetu) tak więc nie oferują nam elastyczności znanej z modułów. Niemniej jednak do zadań takich jak wygenerowanie menu wydają się być trafnym rozwiązaniem jak najbardziej właściwym :)
Ok, więc do dzieła...
A więc wchodzimy do Zarządzaj > Budowa witryny > Bloki > Przeglądaj > Ustawienia 'nazwa szablonu'

Następnie dodajemy nowy blok klikając na "Dodaj blok", wpisujemy jego nazwę oraz (co ważne) ustawiamy format danych na kod PHP:

Oczywiście w widoku ogólnym musimy blok przypisać do jakiegoś regionu w którym pojawić sie będzie miało nasze menu:

Po umiejscowieniu naszego bloku wracamy z powrotem do jego konfiguracji. Oczywiście treścią bloku będzie kod php generujący menu wg naszych potrzeb. Prezentowane przeze mnie rozwiązanie działa na zasadzie rekurencyjnego wywołania funkcji (dla każdego elementu spełniającego warunki z zapytania SQL) generującej strukturę zagnieżdżonych list. Szczegółowy opis jest w komentarzach kodu...
<?php
//Id kategorii dla której generujemy menu
$voc = 1;
//pokazywanie przy nazwie kategorii liczby powiązanych z nią treści
$ile = true;
/* 0 czyli 'root' taksonomii, wszystkie terminy na pierwszym poziomie mają 0 jako id ojca (w bazie tabela term_hierarchy, pole parent) */
return generateMenu(0);
// 'kat_menu' to klasa dla wykorzystana w CSS dla głównego poziomu menu
function generateMenu($parent, $child = 'kat_menu'){
/* dla danego ojca zwaraca dane terminów wraz z ilością powiązanych z nimi treści 'ile', jeżeli 'nodes' jest równe null tzn. że termin ma terminy potomne powiązane z treścią ale sam powiązań nie ma */
$sql = "SELECT term_data.name, term_data.tid, term_hierarchy.parent, COUNT( term_data.tid ) AS ile, term_node.tid AS nodes
FROM term_data
INNER JOIN term_hierarchy ON term_hierarchy.tid = term_data.tid
LEFT JOIN term_node ON term_node.tid = term_hierarchy.tid
WHERE term_data.vid = ".$voc."
AND term_hierarchy.parent = ".$parent."
GROUP BY (term_data.tid)
ORDER BY term_data.name";
$result = db_query($sql);
if(db_num_rows($result)){
$temp = '<ul class="'.$child.'">';
while ($data = db_fetch_object($result)) {
/* jezeli są bezośrednie powiązania z treścią */
if($data->nodes){
$temp .= "<li>".l($data->name. (($ile && $data->nodes) ? "(".$data->ile.")" : "") , "taxonomy/term/".$data->tid);
$temp .= generateMenu($data->tid, 'child');
$temp .= "</li>";
/* jezeli powiązańnie ma */
}elseif (generateMenu($data->tid, 'child') != ''){
$temp .= '<li><a href="#" title= "'.$data->name.'" >'.$data->name.'</a>';
$temp .= generateMenu($data->tid, 'child');
$temp .= "</li>";
}
}
$temp .= "</ul>";
return $temp;
}else{
return '';
}
}
?>
Zaprezentowany powyżej kod pozwoli nam na dynamiczne generowanie wielopoziomowego, bazującego na terminach taksonomii menu. Przy użyciu CSS-ów (elementy typu 'child', 'kat_menu' oraz 'active') możemy uzyskać taki efekt:

Niestety menu będzie zawsze "rozwinięte" tzn. będzie prezentowało wszystkie elementy niezależnie od miejsca serwisu w którym jesteśmy. Ukrywanie i rozwijanie w odpowiednim momencie elementów "podmenu" np. "Obrazy haftowane" i "obrazy malowane" możemy zapewnić poprzez odpowiedni skrypt jQuery np.:
function initMenu() {
$('#block-block-3 .kat_menu ul').hide();
$('#block-block-3 .active').next().show();
$('#block-block-3 .active').parents().show();
$('#block-block-3 .kat_menu li a').click(
function() {
$(this).next().slideDown('slow');
$(this).fadeIn(2000);
}
);
}
$(document).ready(function() {initMenu();});
Końcowy efekt zobaczyć będziemy mogli w akcji na stronach serwisu ryneczek.net. W razie problemów z implementacją/dostosowaniem do własnych potrzeb proszę o pytania... najlepiej w komentarzach pod artykułem :)

Komentarze
niezłe, zapewne wykorzystam na swoich stronkach, dzięki :)
A u mnie wyskakuje taki błąd:
Fatal error: Call to undefined function db_num_rows() in /home/www/domains/testowa.pl/public_html/drupal/includes/common.inc(1655) : eval()'d code on line 31
W Drupalu 6 funkcji db_num_rows() już nie ma, zamiast tego możesz użyć db_result().
jaki Snippet mam zaintalować, i dlaczego u mnie przy o dodawaniu bloku nie ma czegoś takiego jak Format Danych? Korzystam z skórki Garland. Pozdrawiam.
Po zasugerowanej zmianie w skrypcie drupal 6 zgłasza nieprawidłowość w zapytaniu której nie zauważam:
[code]
user warning: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AND term_hierarchy.parent = 0 GROUP BY (term_data.tid) ORDER BY term_data.' at line 6 query: SELECT term_data.name, term_data.tid, term_hierarchy.parent, COUNT( term_data.tid ) AS ile, term_node.tid AS nodes FROM term_data INNER JOIN term_hierarchy ON term_hierarchy.tid = term_data.tid LEFT JOIN term_node ON term_node.tid = term_hierarchy.tid WHERE term_data.vid = AND term_hierarchy.parent = 0 GROUP BY (term_data.tid) ORDER BY term_data.name in /home/dawid/public_html/drupal/includes/common.inc(1685) : eval()'d code on line 29.[/code]
błąd jest tu "WHERE term_data.vid = AND ", brakuje id słownika taksonomii...
W przykładzie jest:
WHERE term_data.vid =".$voc."więc pewnie brakuje deklaracji zmiennej $voc
Dodaj nowy komentarz