Spracovanie formulára + výpis z databázy na základe vybraných parametrov

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
pseudonym
Člen | 57
+
0
-

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ú.

  1. 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.
  2. Ako napísať SELECT v databáze tak, aby som mal dynamické WHERE? Teda aby sa mi WHERE 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ím INNER JOIN – nemôžem použiť metódy z Nette\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
+
0
-

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
+
0
-

@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
+
0
-

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 pri redirect().

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 ako mysql_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
+
0
-

@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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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 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.

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.