Změna výběru z databáze na základě formuláře
- bicekz
- Člen | 20
Zdravím,
mám za úkol vytvořit jednoduchý ajaxový výběr z databáze. Jedná se o výběr televizních kanálů, který se má dát filtrovat podle druhu a části názvu kanálů. Mám takovýto kódu k databázi:
CREATE TABLE ChannelGroups
(
id
varchar(15) PRIMARY KEY,
name
varchar(40) NOT NULL,
order
int(11) NOT NULL DEFAULT 100
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
INSERT INTO ChannelGroups
(id
, name
,
order
) VALUES
(‚documentary‘, ‚Documentary‘, 40),
(‚erotic‘, ‚Erotic‘, 100),
(‚foreign‘, ‚Foreign‘, 90),
(‚general‘, ‚General‘, 10),
(‚children‘, ‚Children‘, 30),
(‚movie‘, ‚Movie‘, 60),
(‚music‘, ‚Music & Lifestyle‘, 80),
(‚news‘, ‚News‘, 70),
(‚other‘, ‚Other‘, 110),
(‚regional‘, ‚Regional‘, 85),
(‚sport‘, ‚Sport‘, 20);
-- Channels
CREATE TABLE Channels
(
id
varchar(50) PRIMARY KEY,
name
varchar(100) NOT NULL,
order
int(11) NOT NULL DEFAULT 1000,
channelGroup
varchar(15) DEFAULT NULL,
description
varchar(2000) NOT NULL DEFAULT 1
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
ALTER TABLE Channels
ADD KEY i_channelGroup
(channelGroup
),
ADD CONSTRAINT c_channelGroup
FOREIGN KEY
(channelGroup
) REFERENCES ChannelGroups
(id
) ON DELETE CASCADE ON UPDATE CASCADE;
INSERT INTO Channels
(id
, name
,
order
, channelGroup
, description
)
VALUES
(‚barrandov‘, ‚TV Barrandov‘, 135, ‚general‘, ‚Programovou skladbu
tvoří zábavné pořady, telenovely, soutěžní pořady a pořady pro
nejmenší.‘), …
…
Kód presenteru:
<?php
namespace App\Presenters;
use Nette;
use Nette\Application\UI\Form;
class AjaxPresenter extends Nette\Application\UI\Presenter
{
/** @var Nette\Database\Context */
private $database;
public function __construct(Nette\Database\Context $database)
{
$this->database = $database;
}
public function renderDefault()
{
$this->template->channels = $this->database->table('Channels')->order('channelGroup ASC, order ASC');
}
protected function createComponentVyberKanaluForm()
{
$form = new Form;
$group=['' => 'Všechny'];
$group += $this->database->table('ChannelGroups')->fetchPairs('id','name');
$form->addSelect('GROUP','Skupina kanálů:',$group);
$form->addText('NAZEV', 'Název:');
$form->addSubmit('send', 'Překreslit');
$form->onSuccess[] = [$this, 'handleUpdate'];
return $form;
}
public function handleUpdate(Form $form, \stdClass $values)
{
$this->renderDefault();
echo $values->GROUP;
if(isset($values->GROUP))
$this->template->channels = $this->template->channels->where('channelGroup',$values->GROUP);
if(isset($values->NAZEV))
$this->template->channels = $this->template->channels->where('channelGroup LIKE ?','%'.$values->NAZEV.'%');
$this->redrawControl('kanaly');
}
}
?>
a šablony:
<?php
{block content}
<!-- Main -->
<div n:snippet="kanaly">
{control vyberKanaluForm}
<table id="nvplTable">
<tr class="tableHeader">
<th>Jméno</th>
<th>Pořadí</th>
<th>Žánr</th>
<th>Popis</th>
</tr>
{foreach $channels as $channel}
<tr class="TableRow" onmouseover=\'changeRowColor(this, true)\' onmouseout=\'changeRowColor(this, false)\' )\'>
<td>{$channel->id}</td>
<td>{$channel->order}</td>
{if $channel->ref('ChannelGroups','channelGroup')}
<td>{$channel->ref('ChannelGroups','channelGroup')->name}</td>
{else}
<td></td>
{/if}
<td>{$channel->description}</td>
</tr>
{/foreach}
</table>
</div>
{/block}
?>
- problém: Nefunguje mi update $this->template->channels. Úvodní výpis channelů z renderDefault funguje. Když jsem příkaz z handleUpdate dal do renderDefault (akorát s natvrdo daným typem channelu např. ‚general‘, tak vše fungovalo. V handleUpdate ta hodnota z formuláře opravdu je. Spíš se mi zdá, jako bych z funkce handleUpdate neměl právo sahat na $this->template->channels. Kde mám chybu, nebo znáte někdo jiné řešení, jak by to fungovalo?
- Bude se to vůbec chovat ajaxově? Na tomto příkladu se mám právě naučit ajax. Zatím tam mám tlačítko pro testování, zda vůbec funguje výpis – očividně podle 1. problému nefunguje. Ale jinak se v tom ajaxu moc nevyznám – obalil jsem tabulku snippetem, funkci pojmenoval handleUpdate a volám redrawControl. Je to vše, nebo mi ještě něco chybí?
Editoval bicekz (6. 7. 2020 13:55)
- bicekz
- Člen | 20
Menší aktualizace dotazů:
předpokládám, že 1. věc nefungovala, protože se vlastně stránka
vždy obnovila a tak se znovu provedl renderdefault.
U ajaxu mi minimálně chyběl odkaz na funkci handleUpdate, což už
nyní mám.
Každopádně se vyskytl nový problém, který nevím, jak řešit:
3. Jak mám dostat do funkce handleUpdate hodnoty z formuláře, když se
musí výběr z databáze aktualizovat podle aktuálních hodnot z formuláře
bez odeslání tlačítkem?
- lookass
- Člen | 54
Ahoj,
radil bych udělat minimálně tyto změny:
- $form->onSuccess[] = [$this, ‚nejakaMojeFunkce‘], rozhodně to nepatří do signálu
- v renderDefault() vybíráš stále stejná data, takže to nemůže fungovat
- a to s tématem moc nesouvisí – práci s databází bych přesunul do nějakého Manageru
Výsledný kód by tedy vypadal nějak takto:
<?php
namespace App\Presenters;
use Nette\Application\UI\Form;
use Nette\Utils\ArrayHash;
class Presenter extends \Nette\Application\UI\Presenter
{
private ChanelsManager $manager;
private ?array $channelFilter = null;
private ?array $channelOrder = null;
public function __construct(MyManager $channelsManager)
{
$this->manager = $channelsManager;
}
protected function createComponentVyberKanaluForm(): Form
{
$form = new Form;
$group=['' => 'Všechny'];
$group += $this->manager->selectFromChannelGroups()->fetchPairs('id', 'name');
$form->addSelect('GROUP','Skupina kanálů:',$group);
$form->addText('NAZEV', 'Název:');
$form->addSubmit('send', 'Překreslit');
$form->onSuccess[] = [$this, 'update'];
return $form;
}
public function update(Form $form, ArrayHash $values): void
{
if(isset($values->GROUP))
$this->channelFilter = ['channelGroup' => $values->GROUP];
if(isset($values->NAZEV))
$this->channelFilter = ['name' => $values->NAZEV];
$this->redrawControl('kanaly');
}
public function renderDefault(): void
{
$this->template->channels = $this->manager->selectFromChannels($this->channelFilter, $this->channelOrder)->fetchAll();
}
}
?>
Netestováno, takže moc neručím za to, že tam nemůže být nějaká chyba.
No a co týče toho, aby byl požadavek ajaxový, tak se mkrni sem: https://doc.nette.org/…ication/ajax
Já např. používám nittro, když ho includuješ, tak se o nic víc
nemusíš starat a všechny požadavky se stanou ajaxové pokud ten konkrétní
form nebo link nedostane data-ajax=„false“
Editoval lookass (9. 7. 2020 20:47)
- lookass
- Člen | 54
bicekz napsal(a):
Vypadá to hezky, ale vzhledem k tomu, že bych musel řešit více errorů než v mém řešení, tak jsem se vrátil k němu. Opravdu není nějaké jednoduchá cesta, jak z dostat hodnoty z toho formuláře bez použití tlačítka a odeslání do dané funkce?
Tak můžeš na ty selecty navěsit form.submit(), jestli ti jde pouze o to nepoužít tlačítko.
Jinak můžeš použít své řešení bez Manageru a akorát musíš změnit obsah renderDefault(), aby to po odeslání formuláře začalo filtrovat to, co potřebuješ zobrazit. Ve finále to bude přibližně tak, jak jsem psal výše, akorát tam budou nepěkně přímo v Presenteru sql dotazy. Bez toho to nepůjde. Mrkni kdyžtak do dokumentace, je to tam dobře popsané.
- lookass
- Člen | 54
Jinak v sekci Formuláře se akorát řeší úplně stejné téma
https://forum.nette.org/…etode-render
- bicekz
- Člen | 20
Zní to podobně, ale on má tlačítko na odeslání formuláře, já žádnou funkci na zpracování formuláře nemám, a proto stejné řešení použít nemohu.
Momentálně jsem se dostal na toto řešení:
<?php
<?php
namespace App\Presenters;
use Nette;
use Nette\Application\UI\Form;
class AjaxPresenter extends Nette\Application\UI\Presenter
{
/** @var Nette\Database\Context */
private $database;
public $filter;
public function __construct(Nette\Database\Context $database)
{
$this->database = $database;
}
public function renderDefault()
{
$this->template->channels = $this->database->table('Channels')->order('channelGroup ASC, order ASC');
if(isset($filter->GROUP))
$this->template->channels = $this->template->channels->where('channelGroup',$filter->GROUP);
if(isset($filter->NAZEV))
$this->template->channels = $this->template->channels->where('channelGroup LIKE ?','%'.$filter->NAZEV.'%');
}
protected function createComponentVyberKanaluForm()
{
$form = new Form;
$group=['' => 'Všechny'];
$group += $this->database->table('ChannelGroups')->fetchPairs('id','name');
$form->addSelect('GROUP','Skupina kanálů:',$group);
$form->addText('NAZEV', 'Název:');
$form->addSubmit('send', 'Překreslit');
$form->onSuccess[] = [$this, 'handleKanaly'];
$filter = $form->getValues();
return $form;
}
public function handleKanaly()
{
if ($this->isAjax()) {
$this->payload->message = 'Success';
}
$this->renderDefault();
dump($this -> filter);
if(isset($filter->GROUP))
$this->template->channels = $this->template->channels->where('channelGroup',$filter->GROUP);
if(isset($filter->NAZEV))
$this->template->channels = $this->template->channels->where('channelGroup LIKE ?','%'.$filter->NAZEV.'%');
$this->redrawControl('kanaly');
}
}
?>
a latte:
<?php
{block content}
<!-- Main -->
<div n:snippet="kanaly" n:href="kanaly!">
{control vyberKanaluForm}
<table id="nvplTable">
<tr class="tableHeader">
<th>Jméno</th>
<th>Pořadí</th>
<th>Žánr</th>
<th>Popis</th>
</tr>
{foreach $channels as $channel}
<tr class="TableRow">
<td>{$channel->id}</td>
<td>{$channel->order}</td>
{if $channel->ref('ChannelGroups','channelGroup')}
<td>{$channel->ref('ChannelGroups','channelGroup')->name}</td>
{else}
<td></td>
{/if}
<td>{$channel->description}</td>
</tr>
{/foreach}
</table>
</div>
{/block}
?>
Myslel jsem, že by mohla stačit objektová proměnná, ale bohužel se v té ajaxové proměnné ani nevypíše. Navíc ji musím používat jako $this->filter, jinak překladač hlásí chybu – všude jinde stačí $filter. Ajax stále nemůžu otestovat, když to nefunguje. Poradí mi prosím někdo už něco konkrétního? Věřím, že stačí malá úprava a bude to fungovat, ale prostě netuším jaká…
- bicekz
- Člen | 20
Jsem vůl … stačilo všude doplnit $this->filter. Každopádně se mi
furt vypisují v handle defaultní hodnoty ve formuláři, i když už tam
mám jiné – tj.
Nette\Utils\ArrayHash #e24d
GROUP ⇒ null
NAZEV ⇒ ""
Předpokládám, že kvůli reloadu stránky po stisknutí tlačítka.
Každopádně nefunguje to automatické ajaxové překreslování výběru…
nějaké rady? Potřeboval bych to během pár dní odevzdat.
Editoval bicekz (12. 7. 2020 14:32)