„Pseudo“ dynamický formulář – pls help me

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

Už fakt asi blnu, ale nevím, kam jsem si zakopal psa. Dělám na průvodci importu dat z CSV do DB. Mám jednoduchý formulář, který bohužel musí mít určitý předem neznámý počet listboxů pro přiřazení sloupců z načteného CSV polím v DB (nevím totiž kolik má uploudnutý CSV sloupců). Udělal jsem si to takto (viz kod níže).
Problém je, že formulář s listboxy normálně načtu – funguje, jakmile hod dám odeslat, tak při zpracování funkcí **checkFormSubmitted **se nemůžu na dynamicky přidaná pole listbox dostat – chyba, že neexistují. Tzn. že takto jednoduše nelze pole do formulářů gnerovat? proč? V PHP s tím nemám problém (kdosi mi tu nedávno psal, že to je jen PHP…?). Je pravda, že tam mohu mít chyb, kterou už FAKT nevidím.

Díky za radu – pomoc.

//deklarované pole DB polí (seznam) pro přiřazení
private $npole = array(	'code'		=> 'Kód',
			'nazev'		=> 'Název',
			'cislo'		=> 'Číslo',
			'cena'		=> 'Cena [Kč/ks]',
			'mnozstvi'	=> 'Množství {ks]');

//továrna na formulář
protected function createComponentCheckForm()
{
	$form = new Form;
	$i=0;
	//dynamický formulář - dle počtu sloupců CSV;
	foreach($this->head as $pole => $v){
		$i++;
		$form->addSelect('mpole'.$i, $v.' »-> ', $this->npole)
			->setPrompt('[zvolte pole]');
	}
	$form->addHidden('sloupcu'); // zde si posílám počet listboxů/sloupců CSV
	$form->addSubmit('save', 'Importovat')->setAttribute('class', 'default');
	$form->addSubmit('cancel', 'Storno')->setValidationScope(NULL);
	$form->onSuccess[] = callback($this, 'checkFormSubmitted');
	$form->addProtection(self::MESS_PROTECT);
	return $form;
}

public function checkFormSubmitted(Form $form)
{if ($form['save']->isSubmittedBy()) {
	$fields = array();
        $cols = $form['sloupcu']->getValue();
	for ($i = 1; $i<=$cols; $i++) {
		//TADY to havaruje, hlasí, že pole mpole1 ve formuláři neexistuje
		//když to dumpnu tak tam fakt žádný prvek mpole není!!!!
		**$fields[$i] = $form['mpole'.$i]->value;**
	}
	dump($fields);
	$this->flashMessage('Data BY byla importována.');
        $file = $form['file']->getValue();
	$this->redirect('confirm',$file);
} else {
	$this->flashMessage('Import byl stornován.','exclamation');
	$this->redirect('default');
}
Filip Procházka
Moderator | 4668
+
0
-

Promiň, ale vůbec nechápu o co se snažíš… Možná by ti pomohlo addDynamic?

A to $i se mi tam vůbec nelíbí, koukni na Containery

mr.mac
Člen | 87
+
0
-

HosipLan napsal(a):

Promiň, ale vůbec nechápu o co se snažíš…

Na addDynamic jsem taky myslel, ale považoval jsem to za zbytečné (ještě ho nemám vyzkoušený). Kód mi připadá vcelku srozumitelný – ale asi jen pro mě. Jde mi je o to, že v rámci jednoho formuláře (s jedním tlačítke submit) potřebuji zobrazit 1 .. n listboxů se stejnými možnostmi výběru a pak po jeho odeslání si sestavím inserty pro načtení dat do DB – tedy, aybch věděl která data z kterého sloupce CSV budu insertovat do kterého příslušného pole DB. Je to jen krok přiřazení každému sloupci odpovídající pole z tabulky DB. Ani contejer si myslím pro toto není potřeba. A neboto jde udělat jednoduše, ale jsem lama a nedám to…

Filip Procházka
Moderator | 4668
+
0
-

Nejde o to, že container není potřeba. Ono je to s ním lepší, protože výsledný data jsou lépe strukturovaný

protected function createComponentCheckForm()
{
        $form = new Nette\Application\UI\Form;
	$presenter = $this;

        //dynamický formulář - dle počtu sloupců CSV;
	$mpole = $form->addContainer('mpole');
        foreach(array_values($this->head) as $i => $v){
                $mpole->addSelect($i, $v . ' »-> ', $this->npole)
			->setPrompt('[zvolte pole]');
        }

        $form->addSubmit('save', 'Importovat')
		->setAttribute('class', 'default')
		->onClick[] = callback($this, 'checkFormSaveClicked');

        $form->addSubmit('cancel', 'Storno')
		->setValidationScope(NULL)
		->onClick[] = function ($button) use ($presenter) {
			$presenter->flashMessage('Import byl stornován.','exclamation');
		        $presenter->redirect('default');
		};

        $form->addProtection(self::MESS_PROTECT);
        return $form;
}

public function checkFormSaveClicked(Form $form)
{
	$fields = $form['mpole']->values;

	// nemusíš nic přerozdělovat, jenom díky tomu, že jsi použil container už máš pole ve tvaru
	// array(0 => 'code', 1 => 'cena', ...)
        dump($fields);

        $this->flashMessage('Data BY byla importována.');
        $file = $form['file']->getValue();
        $this->redirect('confirm', $file);
}

Nějak jsem nepochopil, jak se ti tam zamíchalo to file, soubor nejde přenáše mezi requesty formulářem. Musíš ho nahrát, uložit do nějaké složky a přenášet si, třeba jeho jméno, v session.

mr.mac
Člen | 87
+
0
-

HosipLan napsal(a):

Nejde o to, že container není potřeba. Ono je to s ním lepší, protože výsledný data jsou lépe strukturovaný

EDIT: Container je funkční, díky. Pole se záhlavím sloupců se mezi requesty ztrácí i nadále. Asi cache? Jak? Nebylo by „nakopnutí“?

EDIT2: Tak a je to – cache jsem se zbytečně bál – ukládám si jen jméno file a data hlavičky csv do cache v MODELU a mám 2 metody jak je precist a pak je použiji v prezenteru a factory. To to bolelo ach jo!!! Den v pr****.

Díky za čas a práci – pokusím se to tak udělat. Díky více listboxům mám následný problém s tím, že se mi po submitu data ztratí – resp. hodnoty zvolné v listboxech mají NULL, ostatní hiden a checkbox jsou ok. Nevím, zda bych obsah pole pro listboxy neměl cachovat – jenomže to jsem zase „jako začátečník“ nepobral – zdá se mi, že bych potřeboval mít cache v presenteru, jenomže jak jsem se dočetl měla by být v modelu tak s tím bojuji. Ucelený příklad použití jsem bohužel nenašel – ostatně jako mnoho jiného. Pro nováčky je to šílená dřina. Když jsem před 2 lety začínal s PHP (klasicky) měl jsem první web za měsíc – tohle je zase od začátku – na OOP taky nejsem moc zvyklý (no ale k věci).
Jo a ten „file“ je jen název uploadnutého souboru. Jak jsem psal – dělám na průvodci importu dat z CSV do DB – takže v předchozím kroku uploadnutý soubor posílám do dalšího formuláře přes Hidden, abych ho „cestou k finálním potvrzení“ neztratil. Možná by se na to ta cache taky hodila, ale nevím jak si ji podržet mezi presentery (myslím cache->save($key, $data) a jinde cache->load($key)). No mám v tom pěknou bramboračku.

Editoval mr.mac (22. 10. 2011 22:50)

mr.mac
Člen | 87
+
0
-

HosipLan napsal(a):

Ještě jsem narazil na drobnost. Container vrací ArrayHash (viz dump):

Nette\ArrayHash(5) {
   0 => "cena" (4)
   1 => "nazev" (5)
   2 => NULL
   3 => NULL
   4 => NULL
}

A já potřebuji ověřit, zda byla vyplněná alespoň 3 povinná pole (mám jejich seznam), v submit proceduře jsem si napsal toto (v poli $nrequired mám seznam oněch tří povinných):

...
	foreach($nrequired as $req){
		if (!in_array($req, $fields)) {
	                $form['save']->addError('Nejsou přiřazena všechna povinná pole: Kód, Název, Množství');
	                return ;
		}
	}

A vyskakuje mi chyba, že $fields není pole. Nevím proč, ale konverze na „nohash“ nefunguje, mám ji napsanou takto:

protected function getNoHashFormFields($hashData)
{
	//když dostane hash pole
	$data = \Nette\ArrayHash::from($hashData);
	//vrátí zase hash :-(((
	return $data;
}

Nevíte někdo, prosím co s tím? Dokonce se mi zdálo, že mi to v minulosti fungovalo a teď najednou nic, není to pole z Contejneru nějaké jiné??

Filip Procházka
Moderator | 4668
+
0
-

ArrayHash se dá snadno přetypovat.

$hash = Nette\ArrayHash::from(array(
   0 => "cena",
   1 => "nazev",
   2 => NULL,
   3 => NULL,
   4 => NULL,
)); // objekt
$values = (array)$hash; // pole
// array(0 => "cena", 1 => "nazev", ...)

Já si myslím, že se ztácíš v tom, že formulář špatně znovu vytváříš. Funguje to tak, že formulář vytvoříš, vykreslíš, uživatel ho vyplní a odešle. Ty ho potom musíš znovu sestavit tak, jako poprvé, jinak ty pole, která v něm nebudou, vůbec nepřijme. Je to bezpečnostní opatření.

Takže to tvoje $this->head musí mít ty správné hodnoty při prvním requestu, ale při druhém! Jinak se ti to vůbec nesestaví a formulář ty data prostě zahodí.

Ty si tedy můžeš ty sloupce z toho souboru buď přečíst znovu, nebo si je uložíš do session.

A cache v presenteru ničemu nevadí (jenom se to v určitých situacích dělá jinak)

mr.mac
Člen | 87
+
0
-

HosipLan napsal(a):

ArrayHash se dá snadno přetypovat.

Díky za velmi rychlou reakci. Jak jsem psal v #5 (EDIT2) problém s druhým vykreslením jsem odstranil – naučil jsem se další novinku – použití cache (v modelu) – uložil jsem si hlavičky do cache a po odeslání se formulář korektně naplní a vykreslí neboť si data s cache znovu obnovím.
Přetypování $data = (array) $hash mě fakt dostalo – vracím se k základům PHP, jdu si číst manual. To je ostuda :-(!!

Díky moc za pomoc.

22
Člen | 1478
+
0
-

..data formuláře patří imho do session a ne do cache.

mr.mac
Člen | 87
+
0
-

22 napsal(a):

..data formuláře patří imho do session a ne do cache.

Asi máš pravdu, až budu mít čas tak to předělám, teď ale musím finišovat na další části aplikace. Import z CSV do MSSQL mi chodí – nečekal jsem to, že s tím budu válčit 2 dny – to jsou ty těžké začátky v novém prostředí… Hodně se musí pátrat/hledat – mnoho návodů je neaktuálních, ale to už je obehraná písnička.

Editoval mr.mac (23. 10. 2011 17:11)