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

Dnes som sa náhodou uklikol pri odosielaní komentára a pribudli 2 komentáre, skúsil som kliknúť viac krát a pribudlo 8 komentárov, v podstate sa to tvári tak že si môžem klikať až kým ma to neprestane baviť :)

Samozrejme v spracovaní formuláru presmerujem/prípadne sa odošle ajaxom, konkrétne:

<?php
...
$form->onSuccess[] = $this->processCommentAdd
...

public function processCommentAdd(Form $form) {
	$values = $form->getValues();
	$comment = new Comment($values);
	$comment->user = $this->user->id;
	$comment->article = $this->article->id;
	$this->commentRepository->persist($comment);
	$this->article = $this->getArticle($this->article->webalizedName);
	$this->flashMessage($this->translator->translate('messages.comment.flashMessage'));
	if ($this->isAjax()) {
		$form->setValues(array(), true);
		$this->redrawControl();
	} else {
		$this->redirect('this');
	}
}
?>

či už načítam stránku ajaxom alebo sa presmeruje, nekonečný spam len tak pribúda

pozeral som na viacnasobne odeslani formulare a viacnasobne ulozeni do databaze

ale sú to hodne staré príspevky..
pribudlo do nette (používam 2.2.2) nejaké pekné riešenie ktoré ošetrí viacnásobné odoslanie formulára?

Editoval Matey (16. 7. 2014 0:24)

Šaman
Člen | 2666
+
0
-

Odhaduji to na chybu na tomhle řádku

<?php
$this->article = $this->getArticle($this->article->webalizedName);
?>

Nebo se ti to dokončí, vyběhne flashmessage a správně redrawne/přesměruje? Podle mě ti to po vložení komentáře skončí na chybě, která tomu dokončení zamezí. Bylo by to vidět i v adrese (pokud zakážeš ajax) – neskončíš na čistém pohledu, ale budeš mít v adrese něco jako ?do=nejakyFormSubmit

//edit: Že tam má být tohle?

<?php
$this->article = $this->getArticle($comment->article->webalizedName);
?>

Editoval Šaman (16. 7. 2014 2:21)

Matey
Člen | 142
+
0
-

v tom žiadny problém nie je, začal som používať LeanMapper, takže persistujem entitu Comment, ale ťahám si ju(ich) ako m:belongsToMany cez entitu Article, no a teda po persistovani potrebujem do aplikácie dostať čerstvé data z db, tj ten riadok ktorý sa ti nezdá(ťahám síce zbytočne aj dáta ktoré sa nemenili, čiže Article a Comment entity ktoré sa nemenili), ale inak je ten riadok dobre lebo ťahám Article entitu podľa webalizedName

ráno sem hodím kód, možno predsa len robím niečo zle

edit: nedá mi to zaspať, ono ak by som vždy len redirectol (žiadny ajax) tak v tom spracovaní nepotrebujem načítať nové dáta, nečítajú sa v action metóde, v render metóde sa predaju šablóne

ale ak použijem ajax, redrawnem, nestane sa to že sa action metóda preskočí a použije sa len render?

Editoval Matey (16. 7. 2014 3:27)

Šaman
Člen | 2666
+
0
-

Bez AJAXu ti to šlape?
S AJAXem sice nastavíš formuláři nulové hodnoty, ale jestli ta komponenta nemá snippety, tak se nepřekreslí, takže zůstane vyplněný. Teď hledám v dokumentaci nějakou ukázku, ale nikde nic. Ve starších ukázkách (Todolist) to ale bylo. Zkus obalit celý content snippetem content a ten překresluj, uvidís, jestli to pomůže.

Editoval Šaman (16. 7. 2014 3:47)

Matey
Člen | 142
+
0
-

tak tu je môj ArticlePresenter

<?php
namespace App\FrontModule\Presenters;

use LeanQuery\DomainQueryFactory;
use Nette\Application\UI\Form;
use App\Domain\Comment;
use App\Domain\CommentRepository;
use Nette\Application\UI\Multiplier;
use App\Components\Comment\ICommentControlFactory;

/**
 * @author Matej
 */
class ArticlePresenter extends BasePresenter {

	/** @var DomainQueryFactory @inject */
	public $domainQueryFactory;

	/** @var ICommentControlFactory @inject */
	public $commentControlFactory;

	/** @var CommentRepository @inject */
	public $commentRepository;

	/** @var Article */
	private $article;

	/**
	 * @param string $id (Article webalizedName)
	 */
	public function actionArticle($id) {
		$this->article = $this->getArticle($id);
		if (!isset($this->article)) {
			$this->error('Article not found');
		}
	}

	public function renderArticle() {
		$this->template->article = $this->article;
	}

	/**
	 * @return Form
	 */
	protected function createComponentCommentAdd() {
		$form = new Form;
		$form->addTextArea('content', $this->translator->translate('messages.comment.content'))
				->setRequired();
		$form->addSubmit('send', $this->translator->translate('messages.comment.send'));
		$form->getElementPrototype()->class('ajax');
		$form->onSuccess[] = $this->processCommentAdd;
		return $form;
	}

	/**
	 * @param Form $form
	 */
	public function processCommentAdd(Form $form) {
		$values = $form->getValues();
		$comment = new Comment($values);
		$comment->user = $this->user->id;
		$comment->article = $this->article->id;
		$this->commentRepository->persist($comment);
		$this->article = $this->getArticle($this->article->webalizedName);
		$this->flashMessage($this->translator->translate('messages.comment.flashMessage'));
		if ($this->isAjax()) {
			$form->setValues(array(), true);
			$this->redrawControl();
		} else {
			$this->redirect('this');
		}
	}

	/**
	 * @return Multiplier
	 */
	protected function createComponentComment() {
		return new Multiplier(function ($id) {
			return $this->commentControlFactory->create($this->findCommentById($id));
		});
	}

	/**
	 * @param int $id
	 * @return Comment
	 */
	private function findCommentById($id) {
		foreach ($this->article->comments as $comment) {
			if ($comment->id == $id) {
				return $comment;
			}
		}
	}

	/* ----- DomainQuery ----- */

	/**
	 * @param string $id
	 * @return Article[]
	 */
	private function getArticles($id) {
		$query = $this->domainQueryFactory->createQuery()
				->select('a')
				->from('App\Domain\Article', 'a')
				->where('a.published = %i', 1)
				->where('a.lang = %s', $this->locale);

		if (isset($id)) {
			$query->join('a.category', 'c')
					->select('c')
					->where('c.webalizedName = %s', $id);
		}

		return $query->getEntities();
	}

	/**
	 * @param string $id
	 * @return Article
	 */
	private function getArticle($id) {
		return $this->domainQueryFactory->createQuery()
						->select('a')
						->from('App\Domain\Article', 'a')
						->where('a.published = %i', 1)
						->where('a.lang = %s', $this->locale)
						->where('a.webalizedName = %s', $id)
						->leftJoin('a.comments', 'c')
						->select('c')
						->leftJoin('c.user', 'u')
						->select('u')
						->getEntity();
	}

}
?>

šablóna article:

{* @var $article *}
{block content}

{control navigation:categories}
<div>
	<h2 n:block=title>{$article->name}</h2>
	<small>{_messages.homepage.created_at}: {$article->created_at|date:'d. m. Y h:i'} | {_messages.homepage.comments}: {count($article->comments)}</small>
	<p>{$article->content}</p>
</div>

{snippet}
{foreach $article->comments as $comment}
	{control comment-$comment->id}
{/foreach}

{if $user->isAllowed('Comment', 'add')}
	{control commentAdd}
{/if}
{/snippet}

po odoslaní ajaxom mi pribudne nový komentár a formulár sa vynuluje, to ide, len to pri tých 60 komentároch ktoré som tam naspamoval už trvá 1,7s, asi to bude tým že naťahujem nové data z db a pre každú komponentu s komentárom vyhľadávam entitu Comment v array entit Comment {foreach $article->comments as $comment}…{/foreach}

k tej otázke o viacnásobnom odoslaní formulára, je niečo nové čo nette ponúka aby sa zabránilo viacnásobnému odoslaniu a ja to nepoužívam, alebo je to skôr mojím prístupom k tomu spracovaniu formulára?

Editoval Matey (16. 7. 2014 10:53)

Matey
Člen | 142
+
0
-

ešte dodám .. skúsil som upraviť spracovanie formulára takto (čiže žiadne načítavanie dát, to sa deje až po presmerovaní v metóde actionArticle) a vždy presmerovať

<?php
public function processCommentAdd(Form $form) {
		$values = $form->getValues();
		$comment = new Comment($values);
		$comment->user = $this->user->id;
		$comment->article = $this->article->id;
		$this->commentRepository->persist($comment);
		$this->flashMessage($this->translator->translate('messages.comment.flashMessage'));
		if (!$this->isAjax()) {
			$this->article = $this->getArticle($this->article->webalizedName);
			$form->setValues(array(), true);
			$this->redrawControl();
		} else {
			$this->redirect('this');
		}
	}
?>

stále ale nie je problém odoslať formulár viac krát :(
komentáre k článku sa plnia fakt rýchlo, od zlosti som ich poslal 18 naraz :-/ (samozrejme v reále asi nik nebude takto spamovať, ale nepáči sa mi že nedokážem zabrániť viacnásobnému odoslaniu)

Editoval Matey (16. 7. 2014 11:14)

Matey
Člen | 142
+
0
-

skúšam to prerábať rôzne.. až som to prerobil snáď na najjednoduchšiu verziu:

<?php
namespace App\FrontModule\Presenters;

use LeanQuery\DomainQueryFactory;
use Nette\Application\UI\Form;
use App\Domain\Comment;
use App\Domain\CommentRepository;

class ArticlePresenter extends BasePresenter {

	/** @var DomainQueryFactory @inject */
	public $domainQueryFactory;

	/** @var CommentRepository @inject */
	public $commentRepository;

	/** @var Article */
	private $article;

	/** @var Comment[] */
	private $comments;

	/**
	 * @param string $id (Article webalizedName)
	 */
	public function actionArticle($id) {
		$this->article = $this->getArticle($id);
		$this->comments = $this->getComments($this->article->id);
		if (!isset($this->article)) {
			$this->error('Article not found');
		}
	}

	public function renderArticle() {
		$this->template->article = $this->article;
		$this->template->comments = $this->comments;
	}

	/**
	 * @return Form
	 */
	protected function createComponentCommentAdd() {
		$form = new Form;
		$form->addTextArea('content', 'Text')
				->setRequired();
		$form->addSubmit('send', 'Poslať');
		$form->onSuccess[] = $this->processCommentAdd;
		return $form;
	}

	/**
	 * @param Form $form
	 */
	public function processCommentAdd(Form $form) {
		$values = $form->getValues();
		$comment = new Comment($values);
		$comment->user = $this->user->id;
		$comment->article = $this->article->id;
		$this->commentRepository->persist($comment);
		$this->flashMessage('komentar je pridany');
		$this->redirect('this');
	}
}
?>

šablóna:

{block content}

{control navigation:categories}

<div>
	<h2 n:block=title>{$article->name}</h2>
	<small>{_messages.homepage.created_at}: {$article->created_at|date:'d. m. Y h:i'} | {_messages.homepage.comments}: {count($article->comments)}</small>
	<p>{$article->content}</p>
</div>

<div n:foreach="$comments as $comment">
	<p>
		<h3>{$comment->user->username}</h3>
		<small>{$comment->created_at|date:'d. m. Y h:i'}</small>
	</p>
	<p>{$comment->content}</p>
</div>

{control commentAdd}

tým som si len chcel overiť či na to nemá vplyv vytváranie komponenty pre každý komentár (predĺži sa doba načítania) ale inak čo sa týka viacnásobného odoslania pri klikaní, sa nič nemení