Potřeboval jsem použít multiselectu v e-shopu, který právě dělám, pro určení souvisejícího zboží a narazil jsem na pár zádrhelů, které zde popíši.
1. Jak naplnit multiselect daty, při použití skupin
Toto má být výsledek
Je třeba dodat data ve formátu:
array ( "Bombóny" => array ( 15 => "Hašlerky" 16 => "Bompary" ), "Lízátka" => array ( 11 => "Chupachups" ), "Čokolády" => array ( 12 => "Milka" 17 => "Orion" ), "Ostatní" => ( 14 => "Špalet" 18 => "Pendrek" ) ) |
Jak tento formát získat záleží zejména na struktuře databáze. Předpokládám, že pokud je „ideální“, tak by to šlo pomocí vhodné asociace (viz příklad níže). Vzhledem k tomu, že moje databáze má jinou strukturu a kýženému výsledku jsem se musel dobrat mnohem složitěji (iterace nad daty, dotahování dalších dat pomocí dalších selectů, rekurze, …), tak následující příklad je jen myšlenka, jak by to mohlo fungovat (chybí tam například výběr IDcek a následující nutné UNSETy).
$res = dibi::query('SELECT [nazev_menu], [nazev_produktu] FROM [menu] JOIN [zbozi] USING ([kod_menu]) ORDER BY [nazev_menu], [nazev_produktu]'); $res->fetchAssoc('nazev_menu', '=', 'nazev_produktu'); |
Po získání dat stačí již jednoduše zavolat:
$form->addMultiSelect('items', 'Související produkty:', $items); |
2. Vybrání default hodnot
NETTE očekává pro formulářový prvek multiselect formát:
array(id1, id2, id3); |
Ten získáme malým trikem pomocí fetchPairs (příklad mojí konkrétní funkce z modelu):
public function getRelated($id) { return $this->connection->select('id_2') ->from(self::TABLE_PAGES_RELATED) ->where('id_1=', $id) ->fetchPairs('id_2', 'id_2'); // tento řádek je ten důležitý } |
A následně v presenteru:
$model = new RelatedModel; $items = $model->getRelated($id); $form = $this->getComponent('itemsForm'); $form->setDefaults(array('items'=> $items)); |
3. Uložení získaných dat
NETTE nám z formuláře vrátí data ve formátu:
"items" => array ( 0 => "15" 1 => "11" ) |
ale takto je nelze předhodit SQL dotazu pro INSERT a musím je tedy přetransformovat do tvaru ID=>ID. Což vypadá ve výsledku takto:
"items" => array ( array ( "id_1" => "14", "id_2" => "15" ), array ( "id_1" => "14", "id_2" => "11" ) ) |
kde id_1 je id editovaného záznamu a id_2 jsou související id (ze stejné tabulky jako id_1).
Takový formát se již dá předhodit dibi. Konkrétní funkce z mého modelu vypadá následovně:
public function setRelated($id, $data) { dibi::query('DELETE FROM [pages_related] WHERE [id_1]=%i', $id); // vyčistíme si data pro dané ID dibi::query('INSERT INTO [pages_related] %ex', $data); // uložíme si nové související položky. %ex provede expanzi pole pro multiinsert } |
Zde je i ukázáno jakým způsobem provést multiinsert dat, aby nebylo nutno iterovat nad daty a provádět jednotlivé inserty.
A nyní už chybí jen kus kódu, který transformuje data na výše popsaný formát. pro přehlednost ukážu celou metodu pro zpracování formulářových dat.
public function itemsFormSubmitted($form) { $data = $form->getValues(); $model = new RelatedModel; $arr = array(); foreach($data['items'] as $key => $value) { $arr[$key]['id_1'] = $data['id']; $arr[$key]['id_2'] = $value; } $model->setRelated($data['id'], $arr); $this->flashMessage('Byly aktualizovány související produkty.', 'info'); $this->redirect('this'); } |
Vše!