Vícenásobné rychlé stisknutí tlačítka Enter způsobí opakované spuštění kódu formuláře

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

Dobrý den,
(NetteFramework-2.0.10-PHP5.3)
mám jednoduchý formulář pro určení slevy pro každý desátý produkt, který je označen kódem. Při zadání kódu se insertem uloží kód do tabulky karta. Mám problém, že po rychlém stisknutí tlačítka Enter se kód uloží několikrát a né jednou, ale podle počtu stisknutí. Opakované stisknutí tlačítka Enter může realizovat snadno obsluha programu. Prosím o radu jak zajistit pro jeden zadaný kód jedno uložení dat.

<?php

use Nette\Application\UI\Form;
use Nette\Security as NS;

/**
 * SkladList presenter.
 *
 * @author     Karel Chramosil
 * @package    Sleva
 * Presenter, který zajišťuje evidenci kódů z tabulky karta.
 */

class KartaListPresenter extends SecuredPresenter
{
	public $SLEVAPOCET = 10;

	public function actionDefault($sLike)
	{
		// nechci vypsat žádný řádek
		//$this->template->skartas = $this->context->createKartas()->order('datum_zapsal ASC')->limit(10);
		$pocetkodu = $this->context->createKartas()->where('kod LIKE ?', $sLike)->count('*');
		$offset = $pocetkodu - $this->SLEVAPOCET;
		if ($offset < 0) $offset = 0;
		$this->template->skartas = $this->context->createKartas()->
			where('kod LIKE ?', $sLike)->order('id ASC')->
			limit($this->SLEVAPOCET,$offset );
	}

	protected function createComponentEditaceForm()
	{
		$form = new Form();
		$form->addText('kod', 'Kód slevy:', 15, 15)
			->setDefaultValue('');
		$form->addSubmit('set', 'Ulož');
		$form->onSuccess[] = callback($this, 'editaceFormSubmitted');
		return $form;
	}

	public function editaceFormSubmitted(Form $form)
	{
		$values = $form->getValues();
		$user = $this->getUser();
		$strKod = $values->kod;
		if (($values == NULL ) OR ($strKod === '')) {
			// nechci vypsat žádný řádek
			$this->template->skartas = $this->context->createKartas()->order('datum_zapsal ASC')->limit(0);
		} else {

			try {
			   // potřebuji, aby po stisknutí rychle 3x enter se provedl izert jen jednou
 				$this->context->createKartas()->insert(array(
					'kod'=>$values->kod,
					'remonte_host'=> $_SERVER['REMOTE_ADDR'],
					'remonte_port'=> $_SERVER['REMOTE_PORT'],
					'zakazan'=>0,
					'user_id'=>$user->id,
					'datum_zapsal'=>new DateTime()
				));
				//$this->flashMessage('Flag enter = '.$this->flegEnter , 'success');
				$strKodLike = '%'.$strKod.'%';
				$maxid = $pocetkodu = $this->context->createKartas()->where('kod LIKE ?', $strKodLike)->max('id');
				$pocetkodu = $this->context->createKartas()->where('kod LIKE ?', $strKodLike)->count('*');
				$sleva = $pocetkodu % $this->SLEVAPOCET; // každý desátý je sleva
				// echo $sleva;
				if ($sleva == 0) { //je sleva
					$this->context->createKartas()->where('id = ?', $maxid)->update(array(
						'datum_slevy'=>new DateTime(),
					));
				$this->flashMessage('Občan má nárok na slevu', 'success');
				} else { //není sleva
				}
				$this->redirect('KartaList:',$strKodLike);
				} catch (PDOException $e) {
			$form->addError('Zadaný řetězec není správný.');
			}
		}

	}



}
{var $title = 'Zadání kódu'}

{block content}

{form editaceForm}
<div class="editace-form">
	{control $form errors}
	<div class="pair">
	{label kod /}
	<div class="input">{input kod, autofocus => TRUE} </div>
	<br>
	<div class="pair">
		<div class="input">{input set}</div>
</div>
{/form}

<table>
	<thead>
    <tr>
        <th>Kód</th>
        <th>Oběd datum</th>
        <th>Sleva datum</th>
        <th>Host</th>
        <th>Port</th>
        <th>Zapsal</th>
    </tr>
    </thead>
    <tbody>
    {foreach $skartas as $skarta}
    {?  $userRow = $skarta->ref('user')}
    <tr class="odd">
        <td>{$skarta->kod}</td>
        <td>{$skarta->datum_zapsal|date:'j. n. Y'}</td>
        <td>{$skarta->datum_slevy|date:'j. n. Y'}</td>
        <td>{$skarta->remonte_host}</td>
        <td>{$skarta->remonte_port}</td>
        <td>{$userRow->prijmeni}</td>
    </tr>
    {/foreach}
    </tbody>
</table>

{/block}

Děkuji za radu.

Jan Tvrdík
Nette guru | 2595
+
0
-

Nejsnáze asi JS zablokovat ty opakovaná odeslání.

Karel Chramosil
Člen | 114
+
0
-

Jan Tvrdík napsal(a):

Nejsnáze asi JS zablokovat ty opakovaná odeslání.

Přiznám se že JS moc neovládám. Opakované stisknutí Enter je obecnější problém, protože jsem zkoušel jiní formulář na tabletu a tam k opakovanému stisknutí tlačítka odeslat může nastat velice snadno.

Tomáš Kolinger
Člen | 136
+
0
-

Přidat do tabulky sloupec time, kde budeš ukládat čas v sekundách – unix time. Potom vytvořit unikátní klíč nad časem a uživatelem – user_id.

Takže ti sama databáze ošetří, že uživatel může získat kód jednou za vteřinu, což ti vůbec nevadí a v případě „rychlého entru“ vyhodí databáze exception, kterou můžeš chytit a říct uživateli, že už slevu dostal.

Přes JS by to taky šlo ale když si vypnu JS, tak je bug zpátky.

Editoval Tomáš Kolinger (15. 3. 2014 20:11)

Karel Chramosil
Člen | 114
+
0
-

Dobrý nápad, ale formulář vesele počká na databázi. Nebo mám někde chybu.

CREATE TABLE IF NOT EXISTS karta (
id int(11) NOT NULL AUTO_INCREMENT,
kod varchar(15) COLLATE utf8_czech_ci NOT NULL,
datum_slevy date DEFAULT NULL,
remonte_host varchar(30) COLLATE utf8_czech_ci DEFAULT NULL,
remonte_port int(11) DEFAULT NULL,
zakazan tinyint(1) DEFAULT ‚0‘,
user_id int(11) NOT NULL DEFAULT ‚1‘,
time_zapsal time DEFAULT NULL,
datum_zapsal date NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY user_id (user_id,time_zapsal)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci AUTO_INCREMENT=367 ;

Řeším dotaz, který odpoví nějakým příznakem, že neuběhlo 20 s. Zatím neúspěšně.

	$timemaxids = $this->context->createKartas()->where('id = ?', $maxid)->where('time_zapsal + "00:00:20" < ?', new DateTime  );  //Další
Tomáš Kolinger
Člen | 136
+
0
-

Používáš špatný formát pro ten sloupec time. Musí to být unix time. Tj. počet vteřin od roku 1970… Klidně tam dej bigint(40) a hodnotu získáš pomocí PHP funkce time().

K čemu potřebuješ ten dotaz? Pokud se stane, že uživatel odešle formulář vícekrát, tak se při druhém zápisu vyhodí výjimka a data se do databáze neuloží. Tudíž nemusíš řešit tohle v PHP, protože to za tebe udělá databáze.

Karel Chramosil
Člen | 114
+
0
-

Díky za radu, ale řešení jsem nenašel. Po třech stisknutí Enter se kód třikrát uložil po vteřině.

Kód Oběd datum Oběd čas Sleva datum Host Port Zapsal
ab 16. 3. 2014 02. 54. 47 10.0.0.200 54316 Chramosil
ab 16. 3. 2014 02. 55. 04 10.0.0.200 54325 Chramosil
ab 16. 3. 2014 02. 55. 05 10.0.0.200 54327 Chramosil
ab 16. 3. 2014 02. 55. 07 10.0.0.200 54328 Chramosil

CREATE TABLE IF NOT EXISTS karta (
id int(11) NOT NULL AUTO_INCREMENT,
kod varchar(15) COLLATE utf8_czech_ci NOT NULL,
datum_slevy date DEFAULT NULL,
remonte_host varchar(30) COLLATE utf8_czech_ci DEFAULT NULL,
remonte_port int(11) DEFAULT NULL,
zakazan tinyint(1) DEFAULT ‚0‘,
user_id int(11) NOT NULL DEFAULT ‚1‘,
time_zapsal bigint(40) DEFAULT NULL,
datum_zapsal date NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY user_id (user_id,time_zapsal)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci AUTO_INCREMENT=386 ;

			try {

					if (TRUE) {
						$this->context->createKartas()->insert(array(
							'kod'=>$values->kod,
							'remonte_host'=> $_SERVER['REMOTE_ADDR'],
							'remonte_port'=> $_SERVER['REMOTE_PORT'],
							'zakazan'=>0,
							'user_id'=>$user->id,
							'time_zapsal'=> time(),
							'datum_zapsal'=>new DateTime()
						));
					}
				//$this->flashMessage('Flag enter = '.$this->flegEnter , 'success');
				$maxid = $this->context->createKartas()->where('kod LIKE ?', $strKodLike)->max('id');
				$pocetkodu = $this->context->createKartas()->where('kod LIKE ?', $strKodLike)->count('*');
				$sleva = $pocetkodu % $this->SLEVAPOCET; // každý desátý je sleva
				// echo $sleva;
				if ($sleva == 0) { //je sleva
					$this->context->createKartas()->where('id = ?', $maxid)->update(array(
						'datum_slevy'=>new DateTime(),
					));
				$this->flashMessage('Občan má nárok na slevu', 'success');
				} else { //není sleva
				}
				$this->redirect('KartaList:',$strKodLike);
				} catch (PDOException $e) {
			$form->addError('Zadaný řetězec se neuložil.');
			}
Karel Chramosil
Člen | 114
+
0
-

Díky za pomoc. Nakonec pomohlo zpoždění 4 vteřiny.

			   // potřebuji, aby po stisknutí rychle 3x enter se provedl izert jen jednou
				$maxid = $this->context->createKartas()->where('kod LIKE ?', $strKodLike)->max('id'); //Poslední id kódu.
				$timemaxids = $this->context->createKartas()->where('id = ?', $maxid)->where('time_zapsal + 4 < ?', time()); //Další záznam až po 4 vteřinách

				$flegEnter = FALSE;
			 	foreach($timemaxids as $timemaxid) {
					if ($timemaxid->time_zapsal > 0) {
						$flegEnter = TRUE;
						//$this->flashMessage('Kód se zapíše. Time_zapsal maxid = '.$timemaxid->time_zapsal , 'success');
					}
				}
				if ($flegEnter == FALSE) {
					//$this->flashMessage('Kód nebyl uložen!!!', 'success');
				}

				if ($flegEnter == TRUE) {
					$this->context->createKartas()->insert(array(
						'kod'=>$values->kod,
						'remonte_host'=> $_SERVER['REMOTE_ADDR'],
						'remonte_port'=> $_SERVER['REMOTE_PORT'],
						'zakazan'=>0,
						'user_id'=>$user->id,
						'time_zapsal'=> time(),
						'datum_zapsal'=>new DateTime()
					));
				}