Funkce pro odčítání proměnných
- quiced
- Člen | 85
Zdravím,
mám takový menší problém. Snažím se vytvořit funkci, který by uživateli odečítala kredit na základě toho, kolik kreditů je nastaveno u jednotlivých článků. Vytvořil jsem proto tuto funkci.
public function countclick(){
$first = $this->getUser()->get('credit');
$second = $this->getTutorial()->get('price');
$count = $first-$second;
$this->getUser()->update(array('credit' => $count));
}
Bohužel se nestane nic jiného, než že se zapíše nula. Jinak četl jsem již několik příspěvku na podobné téma, kde se řešilo přičítání a odčítání čísel, bohužel nikde jsem nenašel jak odčítat proměnné.
Díky za radu.
- llsm
- Člen | 121
Ocividne jsi nejak mimo misu. h4ktuna se te spravne ptal, co mas
v jednotlivych promenych, protoze pokud v nich mas cisla (tj. integer), tak
$count
musi byt take cislo. A pak uz jen musi byt sloupec
v databazi taky spravny typ.
Uplne základy:
<?php
$first = 15;
$second = 5;
$count = $first - $second;
echo $count; //vypise 10
?>
- Glottis
- Člen | 129
quiced napsal(a):
Mohl by jsi mi to co jsi napsal tady: update tabulka set sloupec atd.. trochu převést do nějakého kódu? Jinak ty mysql procedury používat nebudu už jenom proto, že to vidím poprvé v životě :D.
:) netusim co tam mas za db vrstvu tak ti vic nenapisu. musi se vykonat tohle :)
<?php
$query = "UPDATE `tabulka` SET `credit` = `credit` - {$count}";
?>
s nette database by to bylo tohle?
<?php
$db->exec("UPDATE `tabulka` SET `credit` = `credit` - ?", $count);
?>
to bys mel v metode toho tvojeho modelu? a volal bys treba
<?php
$this->getUser()->odectiKredit($count);
?>
a db se ti postara o to ze se ti krediti nebudou vyparovat :)
- quiced
- Člen | 85
Asi jsem trochu mimo mísu:D, protože nechápu co jsi napsal. Už v předešlém příspěvku jsem psal, že v databázi mám používané sloupce nastavené na int, což je podlě mě číslo, pokud ne tak mě prosím oprav :D.
Edit: Jinak používám nette database.
Editoval quiced (13. 3. 2013 23:48)
- llsm
- Člen | 121
Nette database nepouzivam, tak neznam presnou syntaxi, ale co ti brani ziskat aktualni stav kreditu samostatnym dotazem, odecist si to v aplikaci a pak uz jen ulozit do databaze vysledne cislo? Tj presne to, co mas v uvodnim prispevku.
<?php
$first = $this->getUser()->get('credit'); //pokud je dotaz tak, jak potrebujes, musi ti vratit cislo, zkontroluj si to Debuggerem
$second = $this->getTutorial()->get('price'); //pokud je dotaz tak, jak potrebujes, musi ti vratit cislo, zkontroluj si to Debuggerem
$count = $count = $first - $second; //Odecitas 2 cisla, vysledek musi byt cislo
$this->getUser()->update(array('credit' => $count));
//Volany update musi nad databazi udelat
$database->exec('UPDATE users SET ? WHERE id = ?', $credit, $asiIDUzivatele);
//promenna $credit obsahuje array('credit' => $count)
?>
Opravdu v tom nikde nevidim zadny problem. Jen si musis zkontrolovat, ze kazdy krok (tj kazda promena) ma co ocekavas a pripadne resit, proc to nema…
- petr.pavel
- Člen | 535
@quiced: Nejlepší bude, když sem zkopíruješ z debug baru, jaké dotazy se ti pouští do databáze, ať se vyhneme nepodloženým dohadům.
- quiced
- Člen | 85
Už jsem přišel na to, kde je hlavní problém. Ten je v tom, že jsem vůbec neřešil výběr podle aktuálního uživatele a aktuálně otevřené stránky. Jinými slovy jsem načítal dva celé sloupce hodnot.
Teď by mě spíše zajímalo, zda je správně to, že pokud chci vybrat jeden sloupec tabulky, tak to dělám přes ->get(‚sloupec‘) ?
- Glottis
- Člen | 129
kdyz to budes resit (to odcitani) aplikacne, zadelavas si na problem. to sem se snazil naznacit.
budou dva pozadavky na odecteni kreditu. prijdou prakticky stejne. jeste se nekde neco zakucka a nastane tohle
select1
select2
spocitas1
spocitas2
update1
update2
a update1 jako by nebyl
- jost125
- Člen | 7
Glottis napsal(a):
kdyz to budes resit (to odcitani) aplikacne, zadelavas si na problem. to sem se snazil naznacit.
budou dva pozadavky na odecteni kreditu. prijdou prakticky stejne. jeste se nekde neco zakucka a nastane tohle
select1
select2
spocitas1
spocitas2
update1
update2a update1 jako by nebyl
Protože tam chybí transakce. Tj. před selectem začni transakci a po updatu commitni a tenhle problém se ti nestane.
- quiced
- Člen | 85
To co jsi mi poslal jsem trochu upravil, tak aby to vybíralo aktuálního uživatele a aktuální článek.
public function countclick($user, $id){
$first = $this->getUser()->where(array('id' => $user))->get('credit');
var_dump($first);
$second = $this->getTutorial()->where(array('id' => $id))->get('price');
var_dump($second);
$count = $first-$second;
var_dump($count);
exit();
}
tohle vypíše toto:
bool(false) bool(false) int(0)
pro jistotu posílám i funkci z presenteru pro zobrazení
public function renderTutorial($id, $user){
$this->template->tutorials = $this->categoryRepository->findTutorial($this->getParam('id'));
$user = $this->getUser()->getIdentity()->id;
$this->template->countclick = $this->categoryRepository->countclick($user, $this->getParam('id'));
}
- llsm
- Člen | 121
Pokud ti databaze vrací místo výsledku false, znamena to, ze dotazu neodpovídají žádné řádky. To může být tím, že sestavený dotaz je špatně nebo ty záznamy v databázi opravdu nejsou… pak je jasne, ze kdyz odecitas od sebe 2 booleany, tak ti z toho nikdy cislo nevypadne…
Posli jeste toto, aby se zkontrolovalo, jestli v tech promenych mas neco:
<?php
public function countclick($user, $id){
var_dump($user);
var_dump($id);
$first = $this->getUser()->where(array('id' => $user))->get('credit');
var_dump($first);
$second = $this->getTutorial()->where(array('id' => $id))->get('price');
var_dump($second);
$count = $first-$second;
var_dump($count);
exit();
}
?>
Editoval llsm (14. 3. 2013 12:25)
- Majkl578
- Moderator | 1364
jost125 napsal(a):
Transakce si zamykají tabuky samy. Manuální lockování je samozřejmě alternativa.
To je mystifikace.
Transakce a zamykání nejsou zaměnitelné, obojí slouží k něčemu
jinému.
Použiji příklad od Glottise výše a trochu jej rozvedu. Mějme
následující situaci s dvěmi paralelně běžícími procesy nad stejným
záznamem (pro jednoduchost např. vklad/převod na bankovní účet
s počátečním zůstatkem 100,–) a aplikační logikou upravující
zůstatek; 1 a 2 rozlišují proces:
- select1 – Proces 1 pracuje s vkladem 50,–. Zjistí si tedy aktuální zůstatek, který je 100,–.
- select2 – Proces 2 pracuje s vkladem 20,–, udělá totéž a taktéž zjistí 100,–.
- spocitas1 – Proces 1 nyní provede příslušnou početní operaci a dojde k tomu, že nový zůstatek je 150,–.
- spocitas2 – Proces 2 udělá totéž a dojde k novému zůstatku 120,–.
- update1 – Proces 1 nyní uloží vypočtenou hodnotu, tedy nastaví zůstatek na 150,–.
- update2 – Proces 2 nyní chce udělat totéž, ale musí počkat na dokončení Procesu 1, jež přišel dřív. Když na něj dojde, nastaví zůstatek na 120,–, který vypočetl.
- Aktuální zůstatek je 120,–. To je ale špatně.
Co se stalo?
Jelikož oba procesy běžely paralelně, oba chybně pracovaly
s původní hodnotou. Důsledkem bylo špatné vypočtení zůstatku. Kdyby
paralelně neběžely, nestalo by se to.
Jak tomu předejít?
Vynutit procesy běžet synchronně, tedy použít zámky. Proces 1 by
v prvním kroku, select1, zjistil hodnotu a zamkl řádek pro
čtení, čímž nedovolí procesu 2 dělat totéž. Proces 2 nyní
musí počkat na dokončení procesu 1 (respektive transakce operující se
zůstatkem) než bude moci pokračovat.
Takže jaký je rozdíl mezi transakcemi a zámky?
Transakce zajišťují bezpečné provedení více operací a poskytují
možnost změny odvolat.
Zámky nabízejí možnost uzamčení záznamu před ostatními
procesy/transakcemi do doby, než bude opět bezpečné s těmito záznamy
pracovat.
(Edit: Doporučuji ještě přečíst na StackOverflow tento komentář včetně reakcí na něj.)
- quiced
- Člen | 85
llsm napsal(a):
Pokud ti databaze vrací místo výsledku false, znamena to, ze dotazu neodpovídají žádné řádky. To může být tím, že sestavený dotaz je špatně nebo ty záznamy v databázi opravdu nejsou… pak je jasne, ze kdyz odecitas od sebe 2 booleany, tak ti z toho nikdy cislo nevypadne…
Posli jeste toto, aby se zkontrolovalo, jestli v tech promenych mas neco:
<?php public function countclick($user, $id){ var_dump($user); var_dump($id); $first = $this->getUser()->where(array('id' => $user))->get('credit'); var_dump($first); $second = $this->getTutorial()->where(array('id' => $id))->get('price'); var_dump($second); $count = $first-$second; var_dump($count); exit(); } ?>
Ještě jsem to trochu upravil, aby se tam náhodou nějak nepletli ty id i když jsou stejný.
public function countclick($user, $clanek){
var_dump($user);
var_dump($clanek);
$first = $this->getUser()->where(array('id' => $user))->get('credit');
var_dump($first);
$second = $this->getTutorial()->where(array('id' => $clanek))->get('price');
var_dump($second);
$count = $first-$second;
var_dump($count);
exit();
}
a
public function renderTutorial($id, $user, $clanek){
$this->template->categories = $this->categoryRepository->findCategory();
$this->template->tutorials = $this->categoryRepository->findTutorial($this->getParam('id'));
$user = $this->getUser()->getIdentity()->id;
$clanek = $this->getParam('id');
$this->template->countclick = $this->categoryRepository->countclick($user, $clanek);
}
potom z toho vypadne toto:
int(31) string(2) "30" bool(false) bool(false) int(0)
Pokud jsem to dobře pochopil, tak z toho plyne chyba v id článku, protože to nebere jako číslo.
- h4kuna
- Backer | 740
Uvědom si že databáze je základ pro každou aplikaci a čím kvalitnější bude databáze tím kvalitnější bude aplikace, teda mělo by to tak být :D. Shnilou aplikaci nevylepší super cool Nette pokud je už shnilá databáze. Takže by nebylo od věci pročíst si k čemu jsou procedury, transakce, triggery, zámky a cizí klíče. Není tak těžké to používat a nastavovat chce to jen cvik. A pokud pro návrh databáze použiješ Adminera tak ti pomáhá nastavovat cizí klíče, pokud dodržíš konvenci pro tabulku a cizí klíče.
Časem se pak budeš divit proč se to chová tak jak se to chová, jak je popsáno výše ohledně paralelních procesů.
- llsm
- Člen | 121
Ne, chápeš to špatně. Číslo, které je předáno jako string problém nebývá. Vstupní parametry jsou takto téměř v pořádku, pokud to chceš mít pinktlich, tak můžeš ten string jednoduše přetypovat, pokud to máš nějak ochráněné, aby tam nic jiného než číslo nevlezlo:
<?php
$clanek = (int) $this->getParam('id');
?>
Z toho co vidím, tak ti ty dotazy do databáze nevrací žádné výsledky, jak jsem psal v minulém postu. Mám na mysli tyto konkrétní dva, na ty se soustřeď a snaž se opravit ty:
<?php
$first = $this->getUser()->where(array('id' => $user))->get('credit');
$second = $this->getTutorial()->where(array('id' => $id))->get('price');
?>
- hAssassin
- Člen | 293
ano, tam bude zakopany pes, ale zeptam se jinak: co delaji metody
getUser()
a getTutorial()
? A pokud me pamet neklame,
tak get()
nad Selection
vraci neco jinyho nez hodnotu
sloupecku, ne (viz zde)?
Takze ten dotaz by mel byt spis neco jako:
$userEntity = $this->getUser()->where(array('id' => $user));
$tutorialEntity = $this->getTutorial()->where(array('id' => $id));
if ($userEntity && $tutorialEntity) {
$first = $userEntity->credit;
$second = $tutorialEntity->price;
$count = $first - $second;
} else {
$this->flashMessage("Uzivatel nebo tutorial neexistuje!", "error");
}
Mozna to nebude uplne ok, ale uz si NDB moc nepamatuju a starsi zdrojaky po ruce nemam, ale.
- hAssassin
- Člen | 293
jo, takze to neco ala repozitar, to jsem predpokladal. No asi tam mam nekde chybu a asi uz vim kde :-) Zkus upravit prvni dva radky na:
$userEntity = $this->getUser()->where(array('id' => $user))->fetch();
$tutorialEntity = $this->getTutorial()->where(array('id' => $id))->fetch();
A pokud pak das dump($userEntity);
tak by ti to melo vypsat
entitu aktualniho uzivatele z DB. Pak vis ze jsi na spravny ceste a mas spravny
data, s nimiz muzes delat neco dal ;-)
EDIT: jinak ten get() se pouziva tak, ze mu tam vlozis primo hodnotu primarniho klice (tedy v obou pripadech IDcka) a uz to nemusis ani fetchovat a ono ti to vrati primo radek podle primarniho klice, takze to muzes zkusit upravit na:
$userEntity = $this->getUser()->get($user);
$tutorialEntity = $this->getTutorial()->get($id);
Editoval hAssassin (14. 3. 2013 16:12)
- hAssassin
- Člen | 293
nz, jsem rad ze jsem pomoh. kazdopadne doporuju se podivat i na ty zamky a transakce o kterych je zminka vyse. Na localhostu ti to muze jet (kor pokud jsi jediny kdo si s tim hraje) ale na produkci to muze byt o necem jinym, nahoda je blbec a ani nemusis mit na webu 1000 navstev denne ;-)
- jost125
- Člen | 7
Majkl578 napsal(a):
jost125 napsal(a):
Transakce si zamykají tabuky samy. Manuální lockování je samozřejmě alternativa.
To je mystifikace.
Transakce a zamykání nejsou zaměnitelné, obojí slouží k něčemu jinému.
Použiji příklad od Glottise výše a trochu jej rozvedu. Mějme následující situaci s dvěmi paralelně běžícími procesy nad stejným záznamem (pro jednoduchost např. vklad/převod na bankovní účet s počátečním zůstatkem 100,–) a aplikační logikou upravující zůstatek; 1 a 2 rozlišují proces:
- select1 – Proces 1 pracuje s vkladem 50,–. Zjistí si tedy aktuální zůstatek, který je 100,–.
- select2 – Proces 2 pracuje s vkladem 20,–, udělá totéž a taktéž zjistí 100,–.
- spocitas1 – Proces 1 nyní provede příslušnou početní operaci a dojde k tomu, že nový zůstatek je 150,–.
- spocitas2 – Proces 2 udělá totéž a dojde k novému zůstatku 120,–.
- update1 – Proces 1 nyní uloží vypočtenou hodnotu, tedy nastaví zůstatek na 150,–.
- update2 – Proces 2 nyní chce udělat totéž, ale musí počkat na dokončení Procesu 1, jež přišel dřív. Když na něj dojde, nastaví zůstatek na 120,–, který vypočetl.
- Aktuální zůstatek je 120,–. To je ale špatně.
Co se stalo?
Jelikož oba procesy běžely paralelně, oba chybně pracovaly s původní hodnotou. Důsledkem bylo špatné vypočtení zůstatku. Kdyby paralelně neběžely, nestalo by se to.
Jak tomu předejít?
Vynutit procesy běžet synchronně, tedy použít zámky. Proces 1 by v prvním kroku, select1, zjistil hodnotu a zamkl řádek pro čtení, čímž nedovolí procesu 2 dělat totéž. Proces 2 nyní musí počkat na dokončení procesu 1 (respektive transakce operující se zůstatkem) než bude moci pokračovat.Takže jaký je rozdíl mezi transakcemi a zámky?
Transakce zajišťují bezpečné provedení více operací a poskytují možnost změny odvolat.
Zámky nabízejí možnost uzamčení záznamu před ostatními procesy/transakcemi do doby, než bude opět bezpečné s těmito záznamy pracovat.
(Edit: Doporučuji ještě přečíst na StackOverflow tento komentář včetně reakcí na něj.)
Souhlasím a samozřejmě vím, že transakce a zamykání není totéž, ale nesouhlasím s tím, že by transakce nevyřešili onen problém. Osobně si nemyslím že to patří do tohohle threadu, ale když už jsme to načali …
Transakce mohou běžet v různých úrovních izolace. Ta „nejstriktnější“ a zároveň použitá jako výchozí v mysql je SERIALIZABLE. Tedy serializace transakcí. V případě serializace se pak uvnitř transakce zamykají pro read and write tabulky v tom pořadí, v jakém se k nim přistupuje. Jakákoliv další paralelní transakce, která chce opět přistupovat k již zamčenému zdroji musí počkat, než ho první transakce pustí. Což dělá teprve v momentě, kdy dojde ke commitu, nebo rollbacku. To dělá z dvou paralelních transakcí serializované oprace.
Příklad:
proces A započne transakci,
proces B započne transakci,
proces A udělá select do tabulky foo a zamkne tabulku foo pro read a write
proces B chce udělat select do tabulky foo, ale protože proces A zamkl foo, musí počkat
proces A udělá update do tabulky foo, což může protože vlastní zámek
proces A udělá commit a tím odemkne zámek na foo
proces B pokračuje kde přestal (pokud nedošel timeout) tedy udělá select nad foo a zamkne ji pro sebe, pak update do foo a nakonec commit a tím ji opět odemkne.
Zámkama se dá docílit naprosto samého jen je to ukecané. Zámky samkozřejmě nemají zbylé vlastnosti z ACID (resp. zámky zajišťují pouze I, independency). Transakce ještě zajišťuje atomicity, consisitency a duralbility.
Zámky mají ale jinou výhodou vlastnost oproti transakcím. Transakce zamykají v tom pořadí, jak přistupují ke zdrojům. Při manuálním zamykání mohu mít „uzamykací protokol“. Zatímco „chaotické“ zamykání transakcí může způsobovat (a u velkých projektů s velkou návštěvností způsobuje) deadlocky, při manuálním zamykání a uzamykacím protokolu (například zamykám zdroje v abecedním pořadí) k deadlocku nedojde. A proto se často používají zámky uvnitř transakcí, abych měl dodržená ACID a současně nedošlo k deadlocku.
Pokud nesouhlasíš, doporučuju nastudovat ACID, úrovně izolací transakcí (READ UNCOMMITED, READ COMMITTED, REPEATABLE READ , SERIALIZABLE) a anomálie s nima spojený.
můžeš mrknout třeba sem http://www.root.cz/…-databazich/