Představte si následující scénář. Vytvoříte slovník kategorií, který je do sebe nějak hierarchicky zatříděn. Typicky třeba seznam států, měst a ulic. Nebo tvoříte inzertní server, kde je několik kategorií zanořených vzájemně do sebe. To Drupal samozřejmě zvládne v pohodě pomocí vestavěného modulu Taxonomy, jinými slovy zvládnete to i vy v pohodě pomocí kategorií a termínů v Drupalu.
Co je však problém, je uživatelská stránka (nikoli administrace). Představte si, že hierarchickou strukturu kategorií máte přidánu jako políčko při vkládání nějakého obsahu. To je bez problému - rozbalovací nabídka i výběr více možností se zobrazí s naznačením hierarchie. Jenže co v případě, že kategorie obsahuje tisíce záznamů? Nebo i jen stovky? Dovedete si představit výběr položek v takové „rozbalovačce"? Nebo jinak, co když chcete uživatelům umožnit vkládat další termíny v této hierarchii přímo z editace obsahu? Políčko pro tagování hierarchii nezachovává a jiná možnost se v základním Drupalu nenabízí.
Hierarchické vkládání termínů z editace obsahu pomocí modulu Hierarchical Select
Zkusme následující. V Drupalu 7 a Views 3 vytvořte kategorii nazvanou Lokality. Vložte do ní termíny obsahující názvy několika měst. Poté vložte do stejné kategorie termíny s názvy různých čtvrtí v daných městech a v seznamu termínů je myší popřetahujte tak, abyste vytvořili hierarchii, tedy jednotlivé čtvrtě zařaďte k příslušným městům. Výsledek by měl vypadat následovně:
Hierarchická struktura kategorií
Poté si vytvořte nový typ obsahu nazvaný Místo, případně použijte už některý z existujících typů obsahu ve vaší instalaci Drupalu. V editaci typu obsahu se přepněte na Správu polí a přidejte nové pole nazvané Lokalita. Jako Typ dat vyberte Term reference a jako widget Hierarchical Select. V upřesňujícím nastavení pro Typ widgetu vyberte nastavení Save podmínka lineage, kterým zajistíte, že se při ukládání obsahu uloží i všechny nadřazené termíny, tedy v našem případě jak město, tak i čtvrť. Dále zapněte volbu Force the user to choose a podmínka from a deepest level, kterou zajistíte, že uživatel při vkládání obsahu opravdu vybere tu nejnižší volbu (což je u nás čtvrť). V sekci Editability settings můžete ještě zapnout možnost vytvářet nové termíny přímo z editace obsahu.
Když nyní budete vytvářet nový obsah, objeví se u něj i výběr lokality řízené pomocí Hierarchical Selectu. Jakmile vyberete záznam z první úrovně (město), nabídne se ve vedlejší rozbalovací nabídce seznam termínů podřízených tomu, který jste vybrali v první úrovni (tedy seznam čtvrtí ve vybraném městě). Jestliže jste v nastavení widgetu zapnuli možnost vkládání nových termínů, objeví se v nabídce i možnost vložení nového termínu pro danou úroveň.
Výběr lokalit v obsahu pomocí widgetu Hierarchical Select
Vytvořte tedy nyní několik smyšlených záznamů pomocí typu obsahu Místo. Pro ně záhy doplníme View zobrazující seznam všech míst zadaných do systému s možností je filtrovat podle vybraného místa a čvrti.
Vytvoření Exposed View pro filtrování obsahu uživatelem
Pomocí administrační části Struktura > Views vytvořte nový pohled pro stránku, kterou umístíte na adresu lokality, View dáte stejný název a necháte v něm vyfiltrovat pouze obsah typu Místo, který je už vydaný. Řazení a další věci nebudeme řešit, pro jednoduchost nechejte místa vypisovat jen ve formě perexu, políčka teď řešit nemusíme.
Nyní do tohoto View přidejte nový filtr pracující s kategorií, do které jste vytvářeli hierarchickou strukturu. V našem ukázkovém příkladu to tedy bude Obsah: Lokalita s nastavením Dropdown a naznačenou hierarchií. U tohoto filtru zapněte volbu Expose this filter to visitors, to allow them to change it, popisek nastavte na Lokalita a jako Operátor vyberte volbu Jeden z.
View uložte a načtěte si jeho stránku. Nad výpisem míst se vám zobrazí rozbalovací nabídka se všemi termíny v kategorii Lokalita. Když jeden z nich vyberete a klepnete na tlačítko Použít, zůstanou ve výpisu jen místa s odpovídajícím zatříděním.
Vytvořte si modul pro úpravu hierarchického filtru ve View
U rozbalovací nabídky, kterou ve View nyní máme, ale může nastat stejný problém, jako u rozbalovačky v seznamu článků. Pokud budete mít kategorií stovky a více, bude jejich výběr z rozbalovací nabídky nepřehledný. Navíc není hierarchie příliš dobře patrná. Pokud by už v Drupalu 7 fungoval modul Hierarchical Select správně a podporoval Views (což se jednoho dne stane), pak byste mohli Exposed Filtr upravit tak, aby pracoval právě s hierarchickým výběrem a vše by bylo pro uživatele rázem pohodlnější.
Protože však Hierarchical Select zatím Views 3 v Drupalu 7 nepodporuje, musíme si poradit menší lstí v jednoduchém modulu, který pro tento účel vytvoříme. Následující řešení můžete v podstatě použít pro jakékoli další vztahy ve vystavených (exposed) filtrech ve Views, kdy v jedné rozbalovací nabídce chcete zobrazit seznam hodnot reagující na nastavení nabídky jiné.
Doplňte do svého View ještě jeden filtr pro Obsah: Lokalita, se stejným nastavením, jako u prvního. View by nyní mělo zobrazovat dvě identické rozbalovací nabídky.
Připravte si složku modulu nazvanou view_hierarchy_mod. Doplňte do ní soubor view_hierarchy_mod.info s následujícím obsahem:
name = Views Hierarchy Mod description = This is to show how to do Views Exposed Filter Hierarchy without HS module. Brought to you by <a href="https://www.maxiorel.cz">www.maxiorel.cz</a> core = 7.x files[] = views_hierarchy_mod.module
Kód modulu bude v souboru view_hierarchy_mod.module a bude se skládat z několika částí. Nejprve implementujte funkci hook_form_views_exposed_form_alter(&$form, &$form_state, $form_id). Samozřejmě si ji přejmenujete podle názvu svého (našeho) modulu. Tato funkce je volána vždy předtím, než dojde k vykreslení exposed formuláře nějakého View.
Abyste ovlivnili jen View, které potřebujete, vložte do této funkce po zapnutí modulu Devel příkaz dsm($form);. Načtěte stránku s View a podívejte se do klikacího rozhraní pro prohlížení pole $form na hodnotu $form['#id'].
Prohlížení obsahu pole pomocí modulu Devel
Zde je uložena informace jedinečná pro daný exposed formulář daného View. Na základě této hodnoty sestrojte podmínku zajišťující, že v ní doplněný kód se provede jen pro naše View. Pokud jste všechno pojmenovali dle mého návodu, pak by příslušná funkce měla po doplnění podmínky vypadat následovně:
{syntaxhighlighter brush: php;fontsize: 100; first-line: 1; } <?php function view_hierarchy_mod_form_views_exposed_form_alter(&$form, &$form_state, $form_id){ //dsm($form); if ( $form['#id'] == 'views-exposed-form-lokality-page') { $form['field_lokalita_tid']['#ajax'] = array( 'event' => 'change', 'method' => 'replace', 'wrapper' => 'dropdown-second-replace', 'callback' => 'view_hierarchy_mod_preprocess_views_exposed_form_callback', ); $options_first = _nastavit_mesta(); $selected = isset($form_state['values']['field_lokalita_tid']) ? $form_state['values']['field_lokalita_tid'] : key($options_first); $form['field_lokalita_tid']['#options'] = $options_first; $form['field_lokalita_tid']['#default_value'] = $selected; $form['field_lokalita_tid_1']['#prefix'] = 'Nyní se podívejme, co je obsahem této podmínky. V poli $form si můžete všimnout jednotlivých políček pro vystavené filtry. Prvnímu z nich, které by mělo být pojmenováno $form['field_lokalita_tid'] přiřadíme drupalovskou ajaxovou obsluhu. Konkrétně ji napojíme na změnu výběru v této rozbalovací nabídce - událost change. Řekneme, že výsledkem ajaxového volání má být nahrazení nějakého stávajícího prvku na stránce, ve wrapperu specifikujeme identifikátor nahrazovaného prvku a následně ještě uvedeme funkci, která se při ajaxovém volání aktivuje.
V další části necháme naplnit první rozbalovací nabídku a předáme její aktuální výběr do proměnné $selected.
Okolo druhé rozbalovací nabídky vložíme DIV s id odpovídajícím označení wrapperu, do kterého se bude vkládat výsledek ajaxového volání první rozbalovací nabídky - specifikovali jsme výše.
Následuje trošku složitější konstrukce pro ošetření správného předávání hodnot v drupalovském AJAXu a informace o sestaveném formuláři. Do těla stránky přidáme připravený JavaScriptový kód a ošetříme stav, kdy není vybrána žádná hodnota ve formuláři.
Nyní vytvoříme funkci, kterou jsme označili jako volanou z AJAXové události první rozbalovací nabídky. Tato funkce nebude dělat nic jiného, než že do výše definovaného wraperu vrátí druhou rozbalovací nabídku - mezitím dojde k její úpravě.
{syntaxhighlighter brush: php;fontsize: 100; first-line: 1; } function view_hierarchy_mod_preprocess_views_exposed_form_callback($form, $form_state) { return $form['field_lokalita_tid_1']; } {/syntaxhighlighter}Zbývají funkce sloužící k naplnění rozbalovacích nabídek. Při odchytu vystaveného formuláře jsme specifikovali funkci, která naplní první rozbalovací nabídku. Proč to? Nechceme přeci, aby se v ní vyskytovaly všechny termíny, ale jen ty, které jsou v hierarchii v první úrovni. Funkci jsem pojmenoval jako _nastavit_mesta(), takže si ji také tak vytvoříme.
{syntaxhighlighter brush: php;fontsize: 100; first-line: 1; } function _nastavit_mesta() { $query = db_select('taxonomy_term_data', 't'); $query->join('taxonomy_term_hierarchy', 'h', 't.tid = h.tid'); $select = $query ->fields('t', array('tid','name')) ->condition('t.vid',3) ->condition('h.parent',0) ->orderBy('t.name'); $tids = $select->execute()->fetchAll(); $mesta = array(); $mesta['All'] = 'vyberte město'; foreach ($tids as $tid) { $mesta[$tid->tid] = $tid->name; } return $mesta; } {/syntaxhighlighter}Obsah této funkce je velice jednoduchý. Pomocí databázového rozhraní Drupalu 7 nazvaného DBTNG vybereme z databázových tabulek taxonomy_term_data a taxonomy_term_hierarchy všechny záznamy první úrovně (h.parent = 0). Slovník je specifikován svým číslem u t.vid. Získané záznamy jeden po druhém projdeme a přiřadíme do proměnné s názvem $mesta.
Obdobně vypadá funkce specifikovaná pro naplnění druhé rozbalovací nabídky. Ta je už volána s parametrem obsahujícím hodnotu vybranou v první rozbalovací nabídce. Výběr hodnot z databáze, konkrétně termínů taxonomie z patřičného slovníku je podobný, jiné je zde jen nastavení nadřazeného termínu, kterým je právě parametr funkce obsahující termín vybraný v první rozbalovačce.
V celé funkci _nastavit_ctvrti() je podmínka if, která ověřuje, zda je nastavena číselná hodnota v předávaném parametru, tedy zda je v první rozbalovačce vybrán nějaký termín a ne hodnota vše/All. Pokud máme číslo nadřazeného termínu, omezíme výběr v nabídce, v opačném případě použijeme dotaz do databáze, který vybere všechny termíny z druhé a další úrovně.
{syntaxhighlighter brush: php;fontsize: 100; first-line: 1; } function _nastavit_ctvrti($key = '') { if ($key && is_numeric($key)){ $query = db_select('taxonomy_term_data', 't'); $query->join('taxonomy_term_hierarchy', 'h', 't.tid = h.tid'); $select = $query ->fields('t', array('tid','name')) ->condition('t.vid',3) ->condition('h.parent',$key) ->orderBy('t.name'); $tids = $select->execute()->fetchAll(); $ctvrte = array(); $ctvrte['All'] = 'vyberte čtvrť'; foreach ($tids as $tid) { $ctvrte[$tid->tid] = $tid->name; } return $ctvrte; } else{ $query = db_select('taxonomy_term_data', 't'); $query->join('taxonomy_term_hierarchy', 'h', 't.tid = h.tid'); $select = $query ->fields('t', array('tid','name')) ->condition('t.vid',3) ->condition('h.parent',0,'>') ->orderBy('t.name'); $tids = $select->execute()->fetchAll(); $ctvrte = array(); $ctvrte['All'] = 'vyberte čtvrť'; foreach ($tids as $tid) { $ctvrte[$tid->tid] = $tid->name; } return $ctvrte; } } {/syntaxhighlighter}Ve výsledku bude výběr vypadat následovně:
Jistě by se dal vytvořit i univerzálnější modul, toto výše popsané řešení je dosti specifické v tom, že v něm uvádíte konkrétní VID, abyste zajistili výběr termínů z příslušného slovníku. Ovšem podobným způsobem můžete upravit rozbalovací nabídky v jakémkoli View. Pokud jste si ještě neosvojili práci s DBTNG, doporučuji článek Zobrazení obsahu jiného webu v Drupalu 7, kde práci s DBTNG popisuji.
Sluší se říci, že část kódu, především zajištění správné funkcionality AJAXu v Exposed Filtrech, je převzata z drupal.org/node/1183418.
Příloha | Velikost |
---|---|
view_hierarchy_mod.zip | 1.52 KB |
Tvůrce webů z Brna se specializací na Drupal, WordPress a Symfony. Acquia Certified Developer & Site Builder. Autor několika knih o Drupalu.
Web Development Director v Lesensky.cz. Ve volných chvílích podnikám výlety na souši i po vodě. Více se dozvíte na polzer.cz a mém LinkedIn profilu.
Podobné články
Komentáře k článku
Našel jsem tam drobou chybu, když už je formulář odeslaný a je vybráno město, v selectu čtvrť to zobrazuje opět všechny položky, jinak řečeno, po odeslání si to nepamatuje číselný $key do fce _nastavit_ctvrti což by zřejmě mělo. Můžete poradit? Děkuji
Jo, to je pravda :-( Bohužel nemám teď čas to odzkoušet znovu. Pokud byste na něco přišel (nebo někdo jiný), napište sem. Zkusím na to mrknout později.
Díky za tak skorú odozvu, článok potešil a určite aj pomôže.
Peter
Díky za super článek, rozhodně pomůže zpříjemnit administraci.
Martin2