data z dynamickeho formulara sa stracaju

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

zdravim

mam formular, do ktoreho dynamicky pridavam prvky. pridavaju sa rozne inputy podla kontextu. tieto nacitam ajaxovo a vkladam do formularu.

problem je v tom, ze do obsluznej funkcie sa mi tieto data nedostanu.

	public function processEdit( AppForm $form ) {
		if ( $form['save']->isSubmittedBy() ) {
			$values = $form->getValues();

			debug::dump($values);
			debug::dump($_POST);exit;
...

POST obsahuje potrebne prvky, ale $values nie – ako je to mozne? kde sa data stracaju a ako sa tomu vyhnem?

okrem teda moznosti rucne si ich sosat z POSTu … resp. este su data dostupne vo
$form->httpData
ale tahat ich odtial mi prijde rovnako suplikantske ako z POSTu.

dik za reakcie vopred

spidy
Člen | 55
+
0
-

Mám takový pocit, že ta metoda getValues vrací jenom hodnoty políček přidaných přímo pomocí Nette ($form->addText(…);), ale jak to jednoduše řešit mě teď nenapadá…

xr
Člen | 94
+
0
-

zatial to riesim takto:

$values = $form->getValues();
$dynamic = array_diff($form->httpData, $values);
unset($dynamic['save']);
22
Člen | 1478
+
0
-

obcházíš bezpečnostní mechanismus Nette, formulář se při odeslání znovu sestavuje, takže ta chybějící hodnota tam asi určitě není při sestavovaní toho formuláře.

xr
Člen | 94
+
0
-

@22:
moja metoda spociva v tom, ze mam AsyncPresenter, ktory zostavi formular na zaklade toho, co si uzivatel zada (moze pridavat rozne objekty, ktore maju rozne inputy – text / radiobutton / chebbox atd), z neho si zoberiem len vnutro (t.j. len samotne inputy) a tieto ajaxom nacitam a pridam priamo pomocou jQuery do hlavneho formulara.

jednotlive inputy patriace jednemu objektu pomenuvam takto: [nazov_objektu]_[nazov_inputu], cize podla prefixu [nazov_objektu] som schopny inputy rozdelit do skupin a potom vytvarat model podla hidden inputu nesucim typ a takto ich spracovat.

napriklad ak teda pridavam dynamicky objekt s textovymi inputmi title a description, tak do formulara pridavam input tagy typu text s name atributom nastavenym na i123_title a i123_description, kde i123 je unikatny identofikator.

inak s tym obchadzanim bezpecnosti nette – je mozne na pole hodnot inputov $dynamic (viz moj predchadzajuci post) aplikovat validaciu ineho formularu, ako toho hlavneho, ktory prave obsluhujem? t.j. toho, ktory tieto inputy vytvara (v async prezenteri).

a tiez, ak mi niekto poradi lepsiu metodu, tak ju rad prijmem.

Filip Procházka
Moderator | 4668
+
0
-

Obcházíš bezpečnostní mechanizmus Nette. Správně je to:

  • Uživatel otevře stránku s presenterem
  • actionDefault()
    • zavolá továrničku, která sestaví formulář $this['myForm'] → createComponentMyForm()
    • naplnění výchozích hodnot formuláře ->setDefaults($this->myModel->find($id))
    • tento krok není nutný, pokud neplníš formulář výchozími daty
  • v šabloně vykreslíš formulář {control myForm}
  • uživatel vyplní data a odešle formulář
  • actionDefault()
    • zavolá se úplně to stejné co předtím, sestaví se stejný formulář a můžeš mít v akci i podmínku, abys zbytečně nevolal databázi kvůli výchozím datům
  • signál na formulář $presenter->getComponent('myForm')->handleSubmit()
    • provede se automaticky
    • naprosto stejně sestavený formulář ti příjme data co uživatel odeslal
      • BEZPEČNOSTNÍ MECHANIZMUS !!!!
    • provedou se tvoje události, kde máš k dispozici správné a všechny hodnoty

Takže ty vůbec nepotřebuješ šahat na nějaké httpData, ty MUSÍŠ sestavit formulář stejně jako byl při vykreslování. To tvoje ajaxové cosi je celé špatně a radši to smaž a nikdy nikomu neukazuj. Až ti někdo rozjebe aplikaci, protože jsi dělal takové kotrmelce s ajaxem, tak nechoď brečet k nám.

Nic z toho co jsem popsal ti nebrání sestavovat dynamicky formuláře. Jenom to nebudeš dělat jako prasátko, ale sestavíš formulář, připojíš ho do presenteru a vykreslíš. Potom ho sestavíš znovu, úplně stejně a přijmeš data. Ale to už se opakuju :)

22
Člen | 1478
+
0
-

do FAQ s tím :-)

xr
Člen | 94
+
0
-

no a ty zase obchadzas to, co sa snazim dosiahnut – ako zrekonstruujem formular, ktory si uzivatel sam naklika na front-ende ? mam X skupin inputov, ktore si moze lubovolne umiestnit a moze ich byt neobmedzeny pocet v lubovolnom poradi.

Filip Procházka
Moderator | 4668
+
0
-

Já nic neobcházím. To je několik různých věcí. Ty potřebuješ nadefinovat formulář a potřebuješ ho pak někde vypisovat. Jenom jsi zvolil jeden z nejhorších přístupů :)

bazo
Člen | 620
+
0
-

xr napsal(a):

no a ty zase obchadzas to, co sa snazim dosiahnut – ako zrekonstruujem formular, ktory si uzivatel sam naklika na front-ende ? mam X skupin inputov, ktore si moze lubovolne umiestnit a moze ich byt neobmedzeny pocet v lubovolnom poradi.

strukturu toho formulara si asi niekde ptom ukladas, nie? tak podla toho si to aj zrekonstruujes

xr
Člen | 94
+
0
-

nie, nic si neukladam. zakladny formular ma napevno zopar prvkov a ostatne sa do neho sukaju ajaxom. potom sa to posle cele a ja potrebujem dostat tie hodnoty.

ok, tak teraz otazka na expertov → ked mam pole s hodnotami, ktore som dostal nekalym sposobom, kazdu sadu hodnot vytvoril jeden formular, akurat sa z neho vykuchali vnutornosti a strcili do toho zakladneho. mozem teda nejakym sposobom spustit validaciu toho tovarnickoveho formularu ?

aby som tu len nekecal o nekonkretnych veciach…
toto je block presenter – block je ten zakladny prvok, ktory sa dynamicky zvacsuje o nove prvky

	public function createComponentEdit( $name ) {
		$form = new AppForm($this, $name);

//		$form->addDynamicContainer('fero');

		foreach ( $this->setup->languages->codes as $lang ) {
			$item = new \Model\BlockText($this->itemId, $lang);
			$item->dbReload();
			$form->addText($this->encodeLangTitle('title', $lang), $lang . ': ' . $this->tr('Title', 'title-tit'), 80, 128)
					->setDefaultValue($item->title)
					->addRule(Form::FILLED, $this->tr('...!', 'title-req'));
			$form->addTextArea($this->encodeLangTitle('text', $lang), $lang . ': ' . $this->tr('Text', 'text-tit'), 80, 20)
					->setDefaultValue($item->text);
		}

		$item = new \Model\Block($this->itemId);
		$item->dbReload();

		$form->addText('category', $this->tr('Category', 'category-tit'), 30, 3)
				->setDefaultValue($item->category)
				->addRule(Form::INTEGER, $this->tr('Category is of integral type!', 'category-req'));
		$form->addText('unique', $this->tr('Unique descriptor', 'unique-tit'), 30, 3)
				->setDefaultValue($item->unique)
				->addRule(Form::FILLED, $this->tr('Fill in a unique block descriptor!', 'unique-req'));
		$form->addCheckbox('display', $this->tr('Visible', 'visible-tit'))->setDefaultValue($item->display - 1);
		$form->addSubmit('save', $this->tr('Save', 'save-btn'));
		$form->addSubmit('back', $this->tr('Cancel', 'cancel-btn'))->setValidationScope(NULL);
		$form->onSubmit[] = callback($this, 'processEdit');
	}
toto je ta sporna metoda (handler submitu)
	public function processEdit( AppForm $form ) {
		if ( $form['save']->isSubmittedBy() ) {
			$values = $form->getValues();
			$dynamic = array_diff($form->httpData, $values);
			unset($dynamic['save']);

//			debug::dump($values);
//			debug::dump($dynamic);
//			debug::dump($_POST);exit;
			// v dynamic mas dynamicky pridane prvky
			// create a base model
			$item = new \Model\Block($this->itemId);
			$item->category = $values['category'];
			$item->unique = $values['unique'];
			$item->display = $values['display'] + 1;
			$item->save();

			$mutations = $this->decodeMultiLangInput($values);
			foreach ( $mutations as $lang => $values ) {
				$item = new \Model\BlockText($this->itemId, $lang);
				$item->title = $values['title'];
				$item->text = $values['text'];
				$item->save();
			}

			$mutations = $this->decodeMultiLangInput($dynamic);
			foreach ( $mutations as $lang => $values ) {

				debug::dump($this->decodeItems($values));
				// decodeItems vracia pole skupin parov hodnot z inputov
				// array( 123 => array('text'=>'blablabla', 'obrazok'=>'/img/..'), 124 => ...)
				// tu by sa podla typu mali vytvorit jednotlive modely, prekazdu skupinu jeden.
				// naplnit a ulozit

			}
			exit;


			$this->flashMessage($this->tr('Block modified.', 'block-midif'));
		}
		$this->redirect('default');
	}

teraz toto je v Async presenteri

public function actionXY(){
...
	$form = new Form();
	$this->addItemInputs($form, $id, $number, $this->lang);
...
}

	protected function addItemInputs( Form $form, $type, $number, $lang = null, $model = null ) {
		$namePrefix = 'i' . sprintf('%03d', $number) . '_';
		switch ( $type ) {
			case \Model\Item::ITYPE_TEXT:
				$this->setView('input_text');
				$name = $namePrefix . 'text';
				if ( $lang ) {
					$name = $this->encodeLangTitle($name, $lang);
				}
				$comp = $form->addTextArea($name, $this->tr('Text', 'text-tit'), 80, 20);
				if ( !is_null($model) ) {
					$comp->setDefaultValue($model->text);
				}
				break;
			case \Model\Item::ITYPE_IMAGE:

				...

			default:
				$this->setView('empty');
				exit;
				break;
		}
		$name = $namePrefix . 'itype';
		if ( $lang ) {
			$name = $this->encodeLangTitle($name, $lang);
		}
		$form->addHidden($name, $type);
		$name = $namePrefix . 'iorder';
		if ( $lang ) {
			$name = $this->encodeLangTitle($name, $lang);
		}
		$form->addHidden($name, $number);
		return $form;
	}

tak znova tu otazku – je mozne data ziskane z toho hlavneho forumlaru v obsluhe nejako zvalidovat cez takto vytvoreny formular v Async prezenteri ? ak ano – ako? pretoze neviem. dik za rady

Filip Procházka
Moderator | 4668
+
0
-

Já se snažím.. opravdu se snažím tě pochopit a pomoct ti… Ale píšeš moc abstraktně a ten kód mi moc neřekl, popravdě jsem ho prolétl a opravdu se mi to nechce studovat.

Jestli chceš vědět jak se to dělá nette-way, tak mi musíš konkrétně popsat, čeho se snažíš dosáhnout. Protože jsem to do teď nepochopil.

22
Člen | 1478
+
0
-

Nette se musí nějak dozvědět, že uživatel přidal nějaký input, to by jsi měl dělat přes nějaký handler. Takže v action postavíš základní form a přes handlery si ho upravuješ, jak potřebuješ a co udělal user si ulož třeba do session..

xr
Člen | 94
+
0
-

bol by moj pristup bezpecny, keby som si pri validacii hlavneho formularu vytvoril ciastkove formulare, nastavil ziskane hodnoty a zvalidoval manualne takto

$form->setValues($dynamic);
$form->validate();

??

Filip Procházka
Moderator | 4668
+
0
-

Pokud dáš do formuláře prvek, třeba

$form->addContainer('neco')->addContainer('tamto')->addText('name', 'Jméno');

a v $_POST bude

$_POST['neco']['tamto']['name'] = 'Lady Gaga';

Tak po připojení formuláře do presenteru bude

$form->values['neco']['tamto']['name'] === 'Lady Gaga'

I pokud budeš po připojení formuláře do presenteru přidávat další políčka, tak se automaticky bude načítat jejich hodnota.

Ale pokud budeš formulář sestavovat na základě $_POST a né na základě nějaké struktury, nebo uložených metadata v nějakém nastavení, tak je to kontraproduktivní a zbytečné, protože by si tam uživatel zase mohl naflákat co chce.

Ani
Člen | 226
+
0
-

Udělej si signál, který ti na základě požadavku uloží třeba do session, co se má do formuláře přidat.

V továrně toho formuláře koukni do session a jestli tam něco je, tak to hned v té továrně přidej do formuláře.

Zpracovat to pak můžeš normálně.

Tohle co tu máš je velká divočina :D

xr
Člen | 94
+
0
-

takze ak spravne chapem, tak pred spracovanim formularu v handleri sa najskor cely formular zostavi ? takze ak mam createComponentForm() tovarnicku, tak tato sa zavola? to som nevedel a veci to zjednodusuje.

diky moc, idem to skusit.

Editoval xr (22. 5. 2011 11:25)

frosty22
Člen | 373
+
0
-

Napadlo mě, zda-li by nebylo možné, aby se dosadili přijaté hodnoty ještě před validací a v případě invalidace, by se ty dané hodnoty vyhodily, což by umožnilo i ony dynamicky závislé formulářové prvky. Akorát si nejsem jist, zda-li by nemohlo dojít k bezpečnostním problémům, když by asi nemělo. Uvedu na příkladě:

<?php
protected function createComponentFooForm()
{
  $form = new Form;

  $items = $db->category()->fetchPairs(...);
  $form->addSelect("category", "Kategorie:", $items)
        ->setDefault(key($items));  // nastavení výchozí hodnoty první kategorie např.

  $items = $db->subcategory()->where("category_id = ?", $form["category"]->value)->fetchPairs(...);
  $form->addSelect("subcategory", "Podkategorie:", $items);

  ...
  return $form;
}
?>

V tomto případě, pokud by formuláře prvotně naplnili přijatými hodnotami elementy a až následně je validovali, tak by to prošlo:

  1. Odešle se formulář
  2. Přijme se signál
  3. Přidá se element category a rovnou se zvaliduje, pokud je OK tak hodnota už se naplní, pokud není OK, tak se odstraní
  4. Zavolá se dotaz na podkategorie s již nově přijatým ID kategorie, čili se naplní korektně a validací projde

Aktuálně je zde problém, že se prvně složí celý formulář a až následně se validují elementy, čili se složí s tím, že ve $form[„category“]->value bude vždy výchozí hodnota a tím pádem, už se nenaplní onen závislý select korektními daty (pouze v případě stejné kategorie).

Snad si rozumíme, ale myslím si, že takto by to nemuselo být špatné řešení. Bohužel jsem zatím přímo neprošel, jak funguje vnitřně onen Forms/Container a Forms/Form, takže možná plácám kraviny.

Editoval frosty22 (31. 7. 2011 13:21)