Spracovanie formulára + výpis z databázy na základe vybraných parametrov
- pseudonym
- Člen | 57
Dobrý deň,
hľadám niečo podobné už celé doobedie a stále sa mi nič
nepodarilo nájsť.
Mám dve stránky, na ktorých je rovnaký formulár. Tento formulár slúži na vybratie viacerých parametrov a na následné vypísanie položiek z databázy, ktoré týmto parametrom vyhovujú.
- Vie mi niekto poradiť ako mám uchovať dáta napr. aj po zavolaní
redirect()
? Chcem totiž oba formuláre smerovať na jednu stránku. Rozmýšľal som, že by sa to dalo cez session, len tie sa mi nepodarilo rozbehať ani na jednoduchom príklade z dokumentácie – taktiež ani žiaden príspevok z fóra mi nepomohol. - Ako napísať
SELECT
v databáze tak, aby som mal dynamickéWHERE
? Teda aby sa miWHERE
generovalo podľa toho, aké parametre sú vybraté vo formulári? Existuje na to už niečo vytvorené, či si to musím napísať sám? Vyberám z troch tabuliek, čiže najprv ich spojímINNER JOIN
– nemôžem použiť metódy zNette\Database
, pretože nemám predpísaný formát ID.
Momentálne to je celkom rozbité, kvôli tomu, že som skúšal session a podobne.
Metóda, ktorá sa volá $form->onSuccess[]
:
public function searchFormSubmitted($form)
{
//pokus o uloženie hodnôt do SESSION + redirect
//na stránku s výsledkami a vyhľadávacím formulárom
$this->searchSession->values = $form->getValues();
//najprv pokus vypísať len všetky položky z DB tak aby ostali v SESSION
$this->searchSession->apartments = $this->context->database
->query("
SELECT *
FROM apartment
INNER JOIN apartmentflag
ON flagID = idApartmentFlag
INNER JOIN apartmenttype
ON typeID = idApartmentType
INNER JOIN project
ON projectID = idProject
ORDER BY idApartment
");
$this->redirect(":Front:Default:search");
}
Vytvorenie Session v rovnakom presenteri:
protected function startup() {
parent::startup();
$this->searchSession = $this->session->getSection("search");
}
Štart SESSION v Bootstrap.php:
if(!$container->session->isStarted()) {
$container->session->start();
}
Renderovanie search stránky:
public function renderSearch() {
$this->template->apartments = $this->searchSession->apartments;
}
Skúšal som to aj pomocou poľa POST, ale to sa mi stratilo pri
redirect()
. Keď som ten redirect()
vymazal a skúšal
to len na stránke, kde je aj formulár, tak to celkom šlo, tam som však
nevedel dynamicky generovať to WHERE
v databáze.
V tomto prípade som ku poľu POST pristupoval následovne:
public function renderSearch() {
$post = $this->presenter->getRequest()->post;
}
Za akékoľvek postrehy či rady vopred ďakujem.
Editoval pseudonym (23. 4. 2012 13:05)
- duke
- Člen | 650
V session si ukládej jen zpracovaný obsah formuláře (nejlépe s podporou vícero těchže formulářů pomocí nějakého klíče, což může být nějaký hash těch hodnot, aby bylo možné ve dvou oknech prohlížeče vidět a stránkovat různé výběry). Po zpracování formuláře pak redirektuj na presenter řešící výpis hodnot, kterému jen nastav příslušný klíč (což bude persistentní parametr toho presenteru). Tento presenter si pak už podle klíče vytáhne ze session uložená data formuláře a provede příslušné databázové dotazy a vyrenderuje šablonu.
Joiny při dotazech používat ručně nemusíš. Pomocí Nette Database lze provádět např. toto:
$apartments = $db->table('apartment');
if ($values['projectSearch'] !== '') {
$apartments->where('project.name LIKE ?', "%$values[projectSearch]%");
}
Potřebné joiny by si to mělo vytvořit automaticky (interně se to řeší
pomocí tříd v Nette\Database\Reflection
).
- pseudonym
- Člen | 57
@duke
Ďakujem, už som si to vyriešil po svojom. Zistil som ako pridať parametre do
GET – tie si prenášam medzi jednotlivými stránkami pri
redirect()
.
S tými JOIN-ami, neviem ako si to myslel. Niekde som čítal, že tabuľky by mali mať konkrétny formát spojovacích ID (takto sa mi všetky tabuľky prerábať popravde nechce).
K tej reflexii – pri nejakej otázke mi ju už niekto vyhadzoval na oči, ale stále neviem načo slúži a ako sa používa. V odpovedi som dostal link na triedu (tuším, už nepamätám), ale to mi veľmi nepomohlo. Nie je o nej niekde popísané niečo viac, resp. niečo ľudsky? ;-)
Inak, nakoniec to SESSION
fungovalo, len sa tam nedalo dať
celé pole z databázy.
Ešte jedna otázočka do pléna:
Existuje metóda, ktorá zisti, či vykonané Query vrátilo nejaké riadky?
Keď som dal test na to, či vrátilo NULL, tak to nefungovalo, pretože vždy
tam niečo vráti. Neviem len presne zistiť, či boli nejaké riadky nájdené,
resp. super by bolo keby som vedel aj presne počet tých riadkov. Niečo ako
mysql_affected_rows()
, ale priamo v Nette.
Editoval pseudonym (23. 4. 2012 17:52)
- duke
- Člen | 650
pseudonym napsal(a):
@duke
Ďakujem, už som si to vyriešil po svojom. Zistil som ako pridať parametre do GET – tie si prenášam medzi jednotlivými stránkami priredirect()
.
Jasně, pokud není formulář příliš složitý, není problém si všechny parametry posílat přímo přes query string.
S tými JOIN-ami, neviem ako si to myslel. Niekde som čítal, že tabuľky by mali mať konkrétny formát spojovacích ID (takto sa mi všetky tabuľky prerábať popravde nechce).
Toto by právě měla řešit DiscoveredReflection (tj. abys nemusel dodržovat tyto konvence). Pak je ještě ConventionalReflection, a ta na ně naopak spoléhá. Přiznám se ale, že nemohu vyloučit, že i s DiscoveredReflection se s určitými konvencemi počítá. To by možná mohl zodpovědět hrach.
K tej reflexii – pri nejakej otázke mi ju už niekto vyhadzoval na oči, ale stále neviem načo slúži a ako sa používa. V odpovedi som dostal link na triedu (tuším, už nepamätám), ale to mi veľmi nepomohlo. Nie je o nej niekde popísané niečo viac, resp. niečo ľudsky? ;-)
Dokumentace skutečně chybí. Také bych ji uvítal. To, jak to funguje zjišťuji buď přímo z kódu Nette, nebo z komentářů na fóru.
Inak, nakoniec to
SESSION
fungovalo, len sa tam nedalo dať celé pole z databázy.
To by ani nemuselo být zrovna žádané, protože obsah databáze se může měnit mezi jednotlivými zobrazeními stránky.
Ešte jedna otázočka do pléna:
Existuje metóda, ktorá zisti, či vykonané Query vrátilo nejaké riadky? Keď som dal test na to, či vrátilo NULL, tak to nefungovalo, pretože vždy tam niečo vráti. Neviem len presne zistiť, či boli nejaké riadky nájdené, resp. super by bolo keby som vedel aj presne počet tých riadkov. Niečo akomysql_affected_rows()
, ale priamo v Nette.
Lze použít count($selection)
, což je totéž jako
$selection->count()
, nebo je možnost testovat, zda metoda
fetch() nevrátila FALSE.
- pseudonym
- Člen | 57
@duke
Ešte k tomu count($selection)
, mám toto:
$query = "
SELECT *
FROM apartment
ORDER BY title
";
$this->apartments = $this->context->database
->query($query);
$this->template->apartments = $this->apartments;
Ako prese a hlavne kde môžem použiť ten count()
? Skúšal
som rôzne varianty:
echo count($query);
echo count($this->apartments);
echo count($this->context->database
->query($query));
Všetky tri vypíšu (pri vrátení viacerých položiek) 1
–
čo asi nie je korektné. Pravdepodobne tú metódu používam zle… Potreboval
by som vedieť, ako ju použiť dobre. Vopred ďakujem.
- pseudonym
- Člen | 57
duke napsal(a):
Joiny při dotazech používat ručně nemusíš. Pomocí Nette Database lze provádět např. toto:
$apartments = $db->table('apartment'); if ($values['projectSearch'] !== '') { $apartments->where('project.name LIKE ?', "%$values[projectSearch]%"); }
Potřebné joiny by si to mělo vytvořit automaticky (interně se to řeší pomocí tříd v
Nette\Database\Reflection
).
Nuž, zistil som, že ten count()
mi s mojim query nepôjde.
Musím to spraviť cez funkcie Nette frameworku a potom to funguje. Len stále
neviem ako mám spraviť ten JOIN
. Keď to skúsim natvrdo ako
v tomto príspevku, tak mi vypíše
PDOException No reference found for $apartment->project.
Kód, ktorý sa snažím rozbehať (potom neskôr by som chcel aj celé query, ktoré som písal na začiatku, toto je len kvôli skúšaniu):
$table = $this->context->database->table('apartment');
$this->apartments = $table->where("project.name", "nazov projektu");
//tu už ten count() funguje
$this->template->count = count($this->apartments);
Tabuľka apartment
obsahuje stĺpec projectID, ktorý odkazuje
na tabuľku project
. V tabuľke project
je id
nazvané idProject
a je tam aj stĺpec nazvaný name
(k nemu sa snažím dostať v príklade). Všetky tabuľky majú id nazvané
ako idNazovTabulky
. Všetky stĺpce, ktoré odkazujú na inú
tabuľku sú nazvané nazovTabulkyID
. Podľa toho, čo
som čítal by si malo Nette hľadať kľúč tak, že nájde v názve
kľúču názov tabuľky (čiže by to malo fungovať).
Neviem ako ďalej. Vie mi niekto poradiť?
Editoval pseudonym (26. 4. 2012 16:47)
- duke
- Člen | 650
Musíš si uvědomit, nad čím ten count vlastně voláš. V jednom
případě ho voláš nad Nette\Database\Table\Selection
(pak ti
funguje, protože tato třída implementuje rozhraní Countable a ve finále se
volá Nette\Database\Table\Selection::count()
), v druhém ho
voláš nad Nette\Database\Statement
, kde to tak není, a proto ti
to prostě vrátí 1, protože jde o 1 objekt toho typu. Třída
Nette\Database\Statement
dědí ze třídy
PDOStatement
, a v této metodě se mj. nachází metoda rowCount
, takže můžeš použít v tomto
případě tu. Nicméně nevím, proč se vyhýbáš použití
Nette\Database\Table\Selection
a obcházíš ji
„nízko-úrovňovým“ voláním metody
Nette\Database\Connection::query
. V podstatě tím redukuješ
celé Nette\Database na mírně chytřejší PDO.
- pseudonym
- Člen | 57
Nuž, ja sa ničomu nevyhýbam. Jednoducho mi nešlo spraviť JOIN, tak niekde som našiel, že sa to zapisuje takto.
Veľmi rád sa niečomu priučím. Práve na to som myslel, že Nette bude super, bude mi pomáhať a podobne. Bohužiaľ zatiaľ mi veľmi nepomáha (ale asi len preto, lebo zatiaľ ho používam zle).
Som úplný začiatočník, keď mi niekto napíše „Prečo používam toto a nie toto“, netuším o čom je reč. Neviem ešte celkom presne ako Nette funguje a bohužiaľ nemám čas študovať ho.
Veľmi rád by som videl nejaké hotové riešenie, ktoré sa naučím používať.
Pri tom Nette\Database\Selection
(ak som to dobre pochopil) mi
funguje count()
(podľa môjho posledného príkladu), ale zasa mi
nefunguje JOIN, aj napriek tomu, že mám v ID, ktoré odkazuje na tabuľku
názov tabuľky na ktorú odkazuje (nazovTabulkyID). Možno aj v tomto prípade
to volám zle, neviem, mám v tom trocha hokej.
- pseudonym
- Člen | 57
Takže, podarilo sa mi to rozbehať. Pre tých, ktorí niečo podobné budú hľadať:
Do BasePresenter.php
som pridal:
//premenna, na ktoru sa odkazujem v prezenteroch
protected $db;
protected function startup()
{
parent::startup();
//spustenie sluzby database z config.neon
$this->db = $this->getService('database');
}
V súbore config.neon
mám nastavenú databázu + službu
takto: (tuším to tak bolo aj v sandboxe)
common:
parameters:
database:
driver: mysql
host: localhost
dbname: test
user: test
password: test
nette:
database:
default:
dsn: '%database.driver%:host=%database.host%;dbname=%database.dbname%'
user: %database.user%
password: %database.password%
services:
database: @Nette\Database\Connection
...
Ďalej v presenteroch k databáze pristupujem takto:
$table = $this->db->table('nazovTabulky');
$this->template->premenna = $table->where("stlpec", $hodnota);
Všetky metódy sú popísané v Dokumentácii: Databáze & ORM
Pri použití JOIN musí, tak ako to tu už bolo pár ráz napísané, názov
stĺpca (foreign key) obsahovať názov tabuľky na ktorú odkazuje. Ďalšia
drobnosť – je potrebné mať fyzicky forein key v databáze. Ja som mal
ďalší problém s tým, že som použil pre databázu engine
MyISAM
. Nakoniec po študovaní tried frameworku som zistil, že
hľadá tie kľúče fyzicky, tak som zmenil engine na InnoDB
a
nanovo som pridal všetky kľúče.
Som rád, že som znova pochopil niečo nové a konečne som začal
využívať Nette\Database
tak ako sa majú. Teraz riešim zasa
iné problémy, ale to je na nový thread. Ďakujem všetkým za pomoc.
- duke
- Člen | 650
pseudonym napsal(a):
Ďalšia drobnosť – je potrebné mať fyzicky forein key v databáze. Ja som mal ďalší problém s tým, že som použil pre databázu engineMyISAM
. Nakoniec po študovaní tried frameworku som zistil, že hľadá tie kľúče fyzicky, tak som zmenil engine naInnoDB
a nanovo som pridal všetky kľúče.
Jen upřesním, že za „fyzické hledání cizích klíčů“ (což
pochopitelně nefunguje u enginu MyISAM, který cizí klíče vůbec
nepodporuje) je zodpovědná třída Nette\Database\Reflection\DiscoveredReflection
.
Krom ní je v Nette ještě k dispozici třída Nette\Database\Reflection\ConventionalReflection
,
která nic takového nedělá a spoléhá se na dodržování konvence
v názvech tabulek a sloupců. V configu je pak možné zvolit si, jakou
reflexi chceme použít (uvedením názvu třídy u parametru reflection –
výchozí je DiscoveredReflection). Samozřejmě je možné napsat si svou
vlastní třídu, která toto řeší (např. jako rozšíření jedné z těch
poskytovaných), což se může hodit u projektů, kde databázová struktura
je pevně daná, a ani jedna z dostupných tříd plně nepostačuje.