Ověření, zda username již existuje
- Maxell92
- Člen | 38
Zdravím, potřebuji ověřit, zda zadaný email uživatele v db již neexistuje. Nette Database mi ale kumuluje podmínky a nevím, jak to obejít. Zkrácený kód:
<?php
//zpracovani formuláře
public function adminUpdateFormSubmitted(Form $form) {
$this->admins->updateAdmin($form->getValues(), $this->admin->id);
...
}
//model
public function updateAdmin($data, $id) {
if ( $this->isExist($data->email, $id) ) {
return "Tento e-mail je již registrovaný!";
}
...
}
public function isExist($email, $id) {
$this->where(array("email" => $email))->where("NOT id", $id)->fetch();
...
}
?>
Problém je, že se spustí tento SQL kód:
SELECT * FROM admin
WHERE (id
= ?) AND
(email
= ?) AND (NOT id
= ?)
První podmínka na id zůstala od chvíle, kdy jsem vybíral záznam k editaci.
Poradíte?
- David Matějka
- Moderator | 6445
vypada to, ze dedis od Nette\database\table\selection, ze? a pouzivas stale
jednu instanci.
nejlepsi by bylo, kdyby to nededilo od nette\database\selection, ale model mel
jako parametr connection s pripojenim na databazi (nette\database\connection) a
metodu getSelection, ktera vytvori novou instanci selection s prislusnym
spojenim a nazvem tabulky. a pak budes pouzivat treba
$this->getSelection()->where…
pripadne, pokud se ti to nechce moc predelavat, tak upravit tu metodu isExist na
zpusob jako $this->connection->table(„nazev_tabulky“)->where… coz
taky vytvori novou instanci selection
- castamir
- Člen | 629
To, co používáš je služba – ta si vytvoří jednu instanci a tu pak použije při dalším volání. To, co potřebuješ, je továrnička, která si vytváří vždy novou instanci.
Pro tabulku „tabulka“ uděláš továrničku třeba takto:
Tabulka.php (třída, kterou můžeš uložit třeba do /app/model nebo /app/factories či jinam do /app/ to je úplně jedno.
class Tabulka extends Nette\Database\Table\Selection {
public function __construct(Nette\Database\Connection $connection) {
parent::__construct('tabulka', $connection);
}
public function updateAdmin($data, $id) {
...
}
public function isExist($email, $id) {
...
}
}
v config.neon
factories:
tabulka: Tabulka
V presenteru vytvoříš novou instanci
$foo = $this->context->createTabulka();
Metody voláš
$this->context->createTabulka()->updateAdmin($data, $id);
// nebo v pripade instance tridy Tabulka:
$foo->updateAdmin($data, $id);
Editoval castamir (27. 8. 2012 12:36)
- Maxell92
- Člen | 38
Kód vychází z QuickStartu (Todo list), model dědí od Selection. Connection mám jako parametr v konstruktoru:
<?php
class Admins extends Selection {
public function __construct(Connection $connection) {
parent::__construct('admin', $connection);
}
...
?>
Naopak se mi nezdá moc čisté řešení to popisované v odpovědi :) Spíš bych čekal, že frontu jednoduše vymažu. Myslím, že to bude občas potřeba i v jiných případech.
- castamir
- Člen | 629
V quickstartu|model se přibližně v polovině stránky vysvětluje rozdíl mezi továrnami a službami:
„Základní rozdíl mezi službou a továrnou je ten, že továrna vytváří pokaždé, kdy si jí vyžádáte, novou instanci objektu. Oproti tomu služba vytvoří pouze jednu instanci při jejím prvním použití a pak tuto instanci používá. Službu si lze představit podobně jako singleton, akorát její jedinečnost je zajištěna pouze v rámci jednoho kontejneru.“
V případě databází můžeš s objekty továren pracovat v postatě úplně stejně jako s objekty ze služeb s tím rozdílem, že pokud máš instanci služby už použitou (např. nějakým selectem) a chceš provést nový select nad původní tabulkou, tak se ti to nepovede, neboť nový dotaz bude proveden nad tím posledním. V případě továren však můžeš vytvořit novou instanci (a nemusíš přitom ztratit tu minulou) a databázový dotaz provést.
Služby jsou definovány nejčastěji v configu (ale lze je definovat i dynamicky) a to následujícím způsobem popsaným právě v quickstartu:
services:
authenticator: Authenticator
tasks: TaskList\Tasks
users: TaskList\Users
tasklists: TaskList\Tasklists
Výše zmíněný kód deklaruje 4 služby:
- authenticator
- tasks
- users
- tasklists
Editoval castamir (27. 8. 2012 12:54)
- VojtaSvoboda
- Člen | 2
Přijde mi trošku zvláštní pro každý dotaz vytvářet novou instanci třídy modelu.
Editoval VojtaSvoboda (27. 8. 2012 13:00)
- castamir
- Člen | 629
Není potřeba vytvářet novou instanci pokaždé, ale je to vhodné tehdy, když potřebuješ přistoupit třeba k původní tabulce a nikoliv k selekci z té tabulky.
$foo = $this->context->createTabulka();
$foo->where(...)->order(...);
...
$var = $this->context->createTabulka();
...
$foo->fetch(); // prvni radek ze selekce z tabulky
$var->fetch(); // prvni radek puvodni tabulky
Naproti tomu u služby (např. tasks) by to dopadlo následovně:
$foo = $this->context->tasks;
$foo->where(...)->order(...);
...
$var = $this->context->tasks; // $var === $foo
...
$foo->fetch(); // prvni radek ze selekce z tabulky
$var->fetch(); // druhy radek ze selekce z tabulky
Editoval castamir (27. 8. 2012 13:07)
- vvoody
- Člen | 910
Možno by mal ten Selection hádzať nejakú exception keď sa pokúsi položiť druhý dotaz :D alebo snáď to má nejaký význam, že Selection umožňuje položiť viac krát dotaz nad tou istou tabuľkou?
VojtaSvoboda: To rozširovanie Selection bola hlúposť ktorá už bola opravená v quickstarte. Model tak ako je teraz v quickstarte už môže byt použitý viac krát, preto je zadefinovaný ako služba.
- Elijen
- Člen | 171
Maxell92 napsal(a):
Kód vychází z QuickStartu (Todo list), model dědí od Selection. Connection mám jako parametr v konstruktoru:
<?php class Admins extends Selection { public function __construct(Connection $connection) { parent::__construct('admin', $connection); } ... ?>
Dědit service od Selection je naprostá blbost. Bohužel se to nějak dostalo do QuickStartu a tak jsem to i já na jednom projektu začal používat :-/
Editoval Elijen (27. 8. 2012 13:32)
- vvoody
- Člen | 910
redhead napsal(a):
@vvoody: ano, má to svůj význam, např.:
$selection = ... $totalCount = $selection->count(); $itemsOnPage = $selection->limit(10, 10);
Ten count ma nenapadol. Tak potom inak, že by Selection vyhodil exception keď by bola zavolaná metóda where/order/limit po tom čo by bol tento Selection fetch/foreach-nutý?
Opýtam sa radšej priamo :D je vhodné foreach-nuť 2× ten istý selection? S tým že sa pred tým druhým foreachom zmení podmienka a budú sa napríklad aj ťahať iné stĺpce. Nebude to robiť problém napríklad v cache?
Tak som si to aj vyskúšal a celkom nepríjemné je že pre oba dotazy
sa cachujú použité stĺpce spolu. Takže prvý foreach prejde napríklad
všetky články s tým že v jeho tele vyberáme z ActiveRow len meno
článku. Doplníme podmienku do Selection na zúženie výberu a teraz pre
zmenu budeme vyberať aj celý content. Vo výsledku prvé query ťahá aj
stĺpce použité v druhom. Pokúsil som sa to teda explicitne nastaviť cez
select() čo pomohlo prvému dotazu, no pri druhom zase čo som nadefinoval pri
prvom, už neodstránim pri druhom, takže som bol nútený doplniť tie stĺpce
čo chýbali aby som neobdrzal PDOException a nič som nemohol robiť s tým
že sa mi vyberú všetky stĺpce nadefinované pred prvým foreachom aj keď
všetky nepotrebujem.
Editoval vvoody (27. 8. 2012 15:33)
- David Matějka
- Moderator | 6445
jen poznamka k te cache: to nesouvisi s tim, ze je to provadeno na stejne instanci, viz: https://forum.nette.org/…tabase-cache
- vvoody
- Člen | 910
Aha tak čo som napísal je hlúposť. Skúsil som to znovu a poriadne aj s priebežným mazaním cache a je to tak. Predsa sa to dobre zacachuje a vyberú len tie potrebné stĺpce. V query mi tie stĺpce zostali lebo pôvodne som použil všetky stĺpce v oboch foreach-och a až potom niektoré zakomentoval a nezmazal cache. Nemali by sa tie stĺpce po ich nepoužití znovu z cache odstrániť? Mohlo by sa to kontrolovať aspoň vo vývojárskom režime.
- David Matějka
- Moderator | 6445
bylo by to vhodny, ale bohuzel ne tak jednoduchy. muselo by to byt spojeno
s rozlisenim, odkud je selection vytvareno.
protoze stejnej dotaz muze byt pouzit napriklad na dvou strankach – na jedne
vypisu jen titulek a na druhe i telo clanku. kdyz bych se ale vratil na tu
stranku, kde se vypsal jen titulek, z cache by se smazalo, ze ten stejnej dotaz
volany z jineho mista chtel i telo clanku…