Při kliku na odkaz se upraví záznam v databázi

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

Upravuji nějaká data (formulářem textareu), a v databázi mám údaj o tom, že je s daty pracováno.

Když se formulář odešle (ok,cancel), zároveň se záznam v databázi upraví, že s daty pracováno už není.

Jenže co když uživatel klikne na jiný odkaz na stránce a z formuláře vyjede, potřebuju, aby se při kliku na takový odkaz taky upravily data v databázi…

… kam a jak to ale napsat? Mockrát díky za rady

Filip Procházka
Moderator | 4668
+
0
-

A napadlo tě, že třeba zavře okno a záznam tam zůstane? Tohle řešit nejde. Jediné co to může částěčně suplovat je ajax. Posílat ajaxem požadavek každých 10 vteřin na zamknutí po dalších 20 vteřin, třeba.

srigi
Nette Blogger | 558
+
0
-

Aby to fungovalo uplne pri kazdom kliku (v lubovolnej destinacii Presenter:action), budes to musiet dat do BasePresentera. Podla mojho usudku by sa na toto hodil aj nejaky design pattern, konkretne Observer. Nette ma podporu pre navesovanie event listenerov na vlastne eventy (onStartup, onLoggedin), ale ako to je s podporou pre vlastne eventy, to neviem.

Spravil by som to asi tak, ze v bootsrap.php, alebo BasePresenter::starup() by som navesil nejaky callback na tento event, pomenovany povedzme onInvoiceFormLeave. Potom niekde vnutri metody BasePresenter::starup() by som podla $this->name a $this->action vyhodnotil, ci som alebo nie som vo formulari. Ak nie som, odpalim onInvoiceFormLeave.

Mozno sa uplne mylim, mozno sa mylim len v implementacii Observer, ale hadam sa k tomu vyjadri aj niekto dalsi.

Editoval srigi (22. 5. 2011 20:05)

Ondřej Mirtes
Člen | 1536
+
0
-
  1. Dotaz nemá s Nette nic společného.
  2. Děláš to špatně, nastuduj si „optimistic locking“.
ic
Člen | 430
+
0
-

V gmailu když mám rozepsanou zpráv a pokusím se zavřít okno, tak mě zadrží javascriptový confirm a zeptá se mě jestli jsem si jistý. Takže na to bude asi nějaká událost javascriptu, ale nestudoval jsem to, tak nevím. Chtělo by to taky zkontrolovat funkčtnost v ostatních prohlížečích (mám FF). Ale s Nette to teda moc nesouvisí.

Aurielle
Člen | 1281
+
0
-

Tuším že je to událost onunload…

Ondřej Mirtes
Člen | 1536
+
0
-

ic: To je úplně jiná problematika – ztráta dat při zavření okna omylem. Stačí se zeptat na textarea.val() != '' a v tom případě vyvolat confirm.

Pochopil jsem to tak, že zoltinhoovi jde o to, že když dva lidé editují ten samý záznam, aby si navzájem své změny nepřepsali.

voda
Člen | 561
+
0
-

onunload nefunguje v Opeře, v ostatních prohlížečích myslím že funguje, ale v každém se to chová trochu jinak

zoltinho
Člen | 24
+
0
-

@HosipLan, udělal jsem to přesně jak jsi říkal. Opravdu to moc nesouvisí s Nette, tak maximálně to, jestli se tu dá s kliknutím na odkaz provést dotaz do DB.

uvedu sem své řešení, se kterým mi pomohl kamarád, třeba to pomůže ostatním. Mohli byste to prosím NEmazat, ale třeba jen archivovat? Kdybych to znovu potřeboval, najdu sem odkaz na svém účtu tady na fóru..

Řešení problému: viz první příspěvek

umístění www/js/timelock.js , znaky XXXXXX je třeba doplnit, YYYYYY je formulář v presenteru treba „něcoForm“, script se nalinkuje na stránku, kde potřebujeme prodlužovat lock, jinde ne

<script type="text/javascript">
function timelock(){
	/* tahle funkce posila GET pozadavek na script timelock.php -> promenna id -> hodnota inputu s ID XXXXXX */

  /*LOCALHOST, (ten hash tam je pro bezpecnost*/
        $.get("../timestamp.php", { id: document.getElementById("frmYYYYYY-XXXXXX").value, hash: document.getElementById("frmYYYYYY-verze_hash").value } );
  /*SERVER - na freehostingu Endora mi nefungují routy, nepřidá mi to do url <presenter>, cesta k timestamp.php je tedy jiná než na localhostu*/
//      $.get("timestamp.php", { id: document.getElementById("frmYYYYYY-XXXXXX").value, hash: document.getElementById("frmYYYYYY-verze_hash").value } );
}

$(document).ready(function(){
	setInterval("timelock()",10000); // bude spoustet funkci timelock kazdych 10000ms -> 10 sekund, dokud bude spustena tahle stranka
});
</script>

Část z config.ini, už jsem tu viděl xy druhů zápisů config.ini, v dalším php z něj načítám, tak ho zde raději pro upřesnění uvedu:

; database
[production < common]
database.dsn = 'mysql:host=localhost;dbname=NAZEVDATABAZE'
database.user = JMENO
database.password = 'HESLO'

[development < common]
database.dsn = 'mysql:host=localhost;dbname=NAZEVDATABAZE'
database.user = JMENO
database.password = 'HESLO'

umístění /www/timestamp.php, XXXXXX tabulka, YYYYYY sloupec

<?php
$ini = file_get_contents("../app/config.ini");   //ini parser

$ini = explode("[production < common]",$ini);
$ini = explode("[development < common]",$ini[1]);
if($_SERVER["HTTP_HOST"]=="localhost"){ //zjistí zda jde o localhost, nebo server
  $ini = parse_ini_string($ini[1]);
}else{
  $ini = parse_ini_string($ini[0]);
}

$connector = $ini["database.dsn"];
$temp = explode(";",$connector);
$con = explode("=",$temp[0]);
$dbcon = explode("=",$temp[1]);

$host = $con[1];
$db = $dbcon[1];
$user = $ini["database.user"];
$pass = $ini["database.password"];

mysql_connect($host,$user,$pass); //connect k db
mysql_select_db($db); // pripojeni k db
$id=$_GET["id"];
if(isset($id)){
  if(is_numeric($id)){
    $hash=mysql_real_escape_string($_GET["hash"]);
  	mysql_query("UPDATE `XXXXXX` SET `YYYYYY` = '" . time() . "' WHERE id =" . $id . " AND hash = '" . $hash . "';") or die(mysql_error()); //upraveni aktualniho timestamp
  }
}
?>

Model v Nette: (Nette\database)

//zkontroluje, zda je s NĚČÍM manipulováno, když ano vrátí TRUE jinak FALSE
        public static function isActive($UUUUUU,$VVVVVV){
            $stav = self::$database->table('XXXXX')->where('UUUUUU_id', $UUUUUU)->where('id', $VVVVVV_id)->max('aktivni');

            if (($stav+20)>time()){
                    return 1;
            } else {
                    return 0; // po 20ti vterinach vrati FALSE
            }
        }

Presenter v Nette:

$isActive = NazevmodeluModel::isActive($UUUUUU_id, $VVVVVV_id);
                    if ($isActive == 1) {
                        $this->flashMessage('Počkejte, .... dovod....');
                        $this->redirect('ZZZZZZ:default', $UUUUUU_id);
                    } else {
                        $this->template->posledni_verze = jmenomodeluModel::metodaTohoCoJeOdblokovane($UUUUUU_id,$VVVVVV_id);

Presenter, formulář factory+submit, který je načtený na stránce (v šabloně: {controlXXXXXXForm}),kde je nalinkovaný timelock.js, když uživatel pracuje s formulářem, nikdo jiný se na danou stránku nedostane, dokud formulář nesubmitne, nebo bude mimo stranku (zavřené okno..) víc jak 20 sekund

protected function createComponentEditVerzeForm(){
               $form = new AppForm();
               $form->addHidden('UUUUUU_id');
               $form->addHidden('VVVVVV_id');
               $form->addHidden('verze_hash'); //bezpecnost, je tam nahodne vygenerovany string.. je uvedeny v timelock.js (je v Nette funkce pro náhodný string? musel jsem si na to napsat metodu..

               $form->addTextArea('text', NULL, 69,
               $form->addSubmit('save', 'Uložit')->setAttribute('class', 'default');
               $form->onSubmit[] = callback($this, 'editVerzeFormSubmitted');

               return $form;
}

public function editVerzeFormSubmitted(AppForm $form){
               if ($form['save']->isSubmittedBy()) {
                       $UUUUUU_id = $form['UUUUUU_id']->getValue();
                       $VVVVVV_id = $form['VVVVVV_id']->getValue();
                       $text = $form['text']->getValue();

			$id_vytvorene_verze = EditaceModel::insert_verze($this->user->id, 	$akt_zaznam_id, $akt_verze_cislo_verze, $text);
                           $this->flashMessage('Verze byla ulo┼żena.');

                           EditaceModel::unsetAktivni($XXXXXX);
                           $this->redirect('ZZZZZZ:default', $XXXXXX);
		} lse { //kdyz se tukne na zrusit
                   ... podobne jako vys, unsetne se Aktivni($XX] ...
		    ...
			nakonec redirect
               }
	}

omlouvam se za prebytecne kody navic, napsal jsem to v rychlosti a nechtel umazat neco co jsem nechtel

Ondřej Mirtes
Člen | 1536
+
0
-

Nebudu to zkoumat do detailu, ale spoustu práce v timestamp.php by sis ušetřil, kdybys ji udělal jako akci presenteru v Nette.

Filip Procházka
Moderator | 4668
+
0
-

A do příště si nastuduj http://cz.php.net/…ini-file.php :) Nette umí taky načítat Config a jak říká Ondra, jednodužší by bylo, kdyby jsi to měl v presenteru.

Pak bys mohl mít ten script i v šabloně a proměnné si doplnit pomocí latte.

<script type="text/javascript">
$(document).ready(function(){
        setInterval(function (){
		$.get({link nejakySignal!}, { id: {$id}, hash: {$hash} } );
	}, 10000);
});
</script>

A taky bych ti doporučil používat dibi, nebo Nette\Database. Srovnej:

mysql_connect($host,$user,$pass); //connect k db
mysql_select_db($db); // pripojeni k db
$id=$_GET["id"];
if(isset($id)){
  if(is_numeric($id)){
    $hash=mysql_real_escape_string($_GET["hash"]);
        mysql_query("UPDATE `XXXXXX` SET `YYYYYY` = '" . time() . "' WHERE id =" . $id . " AND hash = '" . $hash . "';") or die(mysql_error()); //upraveni aktualniho timestamp
  }
}

s dibi

dibi::connect(array(
	'user' => $user,
	'pass' => $pass,
	'database' => $db,
));

dibi::update('tabulka', array('cas%sql' => 'NOW()'))
	->where('id = %i', $_GET["id"])->and('hash = %s', $_GET["hash"])
	->execute();

Je to nesrovnatelně přehlednější a navíc ti dibi (i Nette\Database, abych nekřivdil) ošetřuje data.

Etch
Člen | 403
+
0
-

Nestudoval sem to extra důkladně, ale co se stane například v následující situaci.

Uživatel 1 otevře editaci a upravuje danou věc.
Ajaxem se pravidelně za updatuje čas zamknutí.
Uživateli 1 vypadne internet -> ajax přestane updatovat čas zamknutí a tím pádem se daná věc odemkne.
Uživatel 2 otevře editaci a začne upravovat danou věc.
Uživateli 1 naběhne internet -> ajax začne updatovat čas zamknutí.
Uživatel 1 ukládá editovaný obsah.
Uživatel 2 ukládá editovaný obsah.

V tuto chvíli by pravděpodobně uživatel 1 přišel o vše co upravil. Možná to tam ošetřené nějak máš a jen sem si toho nevšiml.

Ondřej Mirtes
Člen | 1536
+
0
-

Mícháte tu dvě rozdílné problematiky:

  • Oznámení uživateli, že stejný dokument má otevřený (a nejspíše ho edituje) jiný uživatel. To je jen kosmetická záležitost a dosud uvedený kód se z největší části zabývá tímhle.
  • Zamezení ztráty dat z důvodu přepsání jednoho dokumentu dvěma uživateli po sobě. Tento problém řeší tzv. optimistic locking (velmi jednoduchý princip, hezky ho vysvětluje dokumentace Doctrine 2) a není třeba znovuvynalézat kolo.

Pokud daný problém rozdělíte a začnete řešit zvlášť, vznikne elegantní soubor řešení bez spaghetti kódu, který tu zatím vidím.