Jak mít formulář včetně zpracování v samostatném souboru?

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

Ahoj,
používal jsem přístup přes context, tak jsem si řekl, že to změním. Problém je ovšem s formuláři.

Mám formulář kde se přidává/aktualizuje kategorie. Tento formulář mám v samostané třídě:

class CategoryForm extends Nette\Application\UI\Form

ale je problém se zpracováním formuláře, to zpracování bych chtěl provádět v rámci jedné třídy s formulářem. Jak se ale dostat k modelu? Přes context nechci, přes presenter ($this->presenter) to je (aspoň si to myslím) také nevhodné, protože v presenteru musím mít inject modelu jen kvůli tomu formuláři.

Jsem z toho zmaten a nevím kam se hnout.
Máte někdo řešení, které by umožňovalo změnu mezi přidáváním a upravováním a zároveň, aby to bylo nějak elegantně napsáno?

Jestli máte nějaké nápady, tak sem s nimi. Díky

--edit--

Již při vytváření formuláře je potřeba databáze, protože používám multilang. Kolik jazyků, tolik názvů kategorie. Takže i kdybych ten form zpracovával v presenteru, tak bych buď v presenteru musel přidávat pole nebo nějak se dostat ve té třídě k modelu.

Editoval Myiyk (30. 8. 2013 23:44)

Šaman
Člen | 2668
+
0
-

Podědíš si vlastní třídu ModelForm extends Nette\Application\UI\Form, které předáš $model (nebo databázi, nebo prostě něco, co potřebuje k práci).
Buď ho předáš v konstruktoru (ale to způsobuje problémy při vytváření), takže lepší je použít setter injection a ve verzi 2.1 přímo injector, o nastavení se postará autowiring. Předpokládám, že formuláře vytváříš pomocí továrničky v configu, jinak ten model injectneš ručně v továrně v presenteru.

Editoval Šaman (31. 8. 2013 0:02)

enumag
Člen | 2118
+
0
-

Opět si neodpustím doporučit spíše továrny než dědičnost. To současně řeší i použití modelu:

class WhateverFormFactory extends \Nette\Object
{

    /**
     * @var \WhateverRepository
     */
    private $repository;

    public function __construct(\WhateverRepository $repository)
    {
        $this->repository= $repository;
    }

    public function create()
    {
        $form = new \Nette\Application\UI\Form;
        // define fields using $this->repository
        $form->onSuccess[] = $this->formSuccess;
        return $form;
    }

    public function formSuccess(\Nette\Application\UI\Form $form) {
        //...
    }

}
services:
    - WhateverFormFactory

Inject továrny v presenteru si díky Kdyby/Autowired ušetřím:

class WhateverPresenter extends BasePresenter
{

	/**
	 * @return My\Awesome\Datagrid
	 */
	protected function createComponentWhateverForm($name, WhateverFormFactory $factory)
	{
		return $factory->create();
	}

}

Editoval enumag (31. 8. 2013 0:25)

Šaman
Člen | 2668
+
0
-

No, po naší minulé diskuzi jsem předělal dědičnost na továrny, ale tohle je podle mého správné použití dědičnosti, protože nově vzniklá třída ModelForm skutečně rozšiřuje možnosti předka a přitom to není konfugurování konkrétní instance.
Následně si nakonfiguruji konkrétní formulář pomocí továrny.

Ale pokud by bylo potřeba předávat například konkrétní repozitář, tak samozřejmě nemá smysl vytvářet ModelForm (já mám model jako service locator, takže se z něho dostanu do všech veřejných metod modelu), ale rovnou se vytvoří v továrně konrétní formulář a předá se mu příslušný repository/connection/servise/cokoliv.

Editoval Šaman (31. 8. 2013 0:53)

enumag
Člen | 2118
+
0
-

@Šaman: To plyne čistě z toho že máš model jako service locator (což taky není best practice). Sám říkáš že když by bylo třeba předávat konkrétní instanci jako já v příkladu výše tak dědičnost nemá smysl. Nevidím ale důvod předávat formuláři cokoli – vytvoření, validaci i zpracování formuláře dělám v továrně kam si závislosti injectnu, samotný formulář na žádné třídy modelu sahat nemusí.

@Myiyk: To s těmi jazyky jsem po pravdě nepochopil… Zkus to upřesnit pokud pořád nevíš jak. ;-)

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Mám dojem, že se stále opakuju, ale což takto :) ?

class CategoryForm extends Nette\Application\UI\Control
{

	private $database;

	public function __construct(Nette\Database\Connection $database)
	{
		parent::__construct();
		$this->database = $database;
	}

	protected function createComponentForm()
	{
		$form = new UI\Form;
		// $this->database is ready here.
		$form->onSuccess[] = $this->processForm;
		return $form;
	}

	public function processForm($form)
	{
		// $this->database is ready here too.
	}

}

U toho přístupu s factory mi přijde zvláštní pojmenování, ale uznávám, že to řeší kupu problémů taky.

enumag
Člen | 2118
+
0
-

@vojtech.dobes: A tu třídu CategoryForm máš jako službu nebo jak? Imho by komponenty přímo službami být neměly. Pokud ji ale jako službu nemáš tak musíš to connection ještě tahat skrz presenter abys ho předal konstruktoru.

Editoval enumag (31. 8. 2013 11:47)

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Komponentu lze jako ne-službu zaregistrovat do configu též s využitím ICategoryFormFactory interfacu. Pak stačí vyžadovat tento interface jako závislost v presenteru a v továrničce zavolat return $this->categoryFormFactory->create();.

enumag
Člen | 2118
+
0
-

@vojtech.dobes: Aha, generované továrničky, jasně. :-)

Filip Procházka
Moderator | 4668
+
0
-

Dovolil jsem si to sepsat do pla.nette best practise jak psát formulářové komponenty. Doufejme že to Davídek brzy nahodí na web ;)

enumag
Člen | 2118
+
0
-

@Filip Procházka: Nice. :-) Jen ta třída Nette\Database\Connection neexistuje, chybí \ nebo use Nette;.

Filip Procházka
Moderator | 4668
+
0
-

@enumag

  1. to je pravda pouze za předpokladu že má soubor namespace, což tady nemá
  2. proč to nefixneš? :)
enumag
Člen | 2118
+
0
-

@Filip Procházka: Protože nevím kterou možnost preferuješ, ale ok, tady je PR. S NS máš samozřejmě pravdu, ale většina lidí si tam nějaký NS stejně asi přidá.

Myiyk
Člen | 321
+
0
-

Díky za rady, druhé řešení z pla.nette best practise jak psát formulářové komponenty vypadá dobře, pokusím se to rozjet.

Myiyk
Člen | 321
+
0
-

Hmm, tak nastal problém Class AdminModule\ICategoryFormFactory used in service ‚27_AdminModule_ICategoryFormFactory‘ has not been found or is not instantiable.

services:
	- AdminModule\ICategoryFormFactory
namespace AdminModule;
use ...
class CategoryForm extends Control
{
...
}

interface ICategoryFormFactory
{
	/** @return \AdminModule\CategoryForm */
	function create();
}

Soubor s tou třídou a rozhraním (v jednom souboru je obojí) se ale includuje.

Takže chyba musí být v is not instantiable. Ale nechápu co to znamená. Co ten interface musí jako splňovat?

enumag
Člen | 2118
+
0
-

@Myiyk: Nemáš náhodou stable verzi Nette? Generované továrny jsou až v dev, na stable si je musíš napsat sám.

Myiyk
Člen | 321
+
0
-

@enumag: Mám aktuální dev2.1 z gitu

Editoval Myiyk (1. 9. 2013 0:30)

jiri.pudil
Nette Blogger | 1034
+
0
-
factories:
    - AdminModule\ICategoryFormFactory
Myiyk
Člen | 321
+
0
-

díky, funguje to

@Filip Procházka: do best-practise-formulare-jako-komponenty se nejspíš vloudila chyba

jetpack
Člen | 71
+
0
-

Tak nevím jestli je někde něco blbě, ale podle návodu: https://github.com/…ponenty.texy

mi to u tohohle kódu:

<?php
$form->onSuccess[] = function (UI\Form $form) {
			if (!$form->isValid())
				return;
			$this->flashMessage('Děkujeme...');
			$this->redirect('this');
		};
?>

háže chybu: Using $this when not in object context což je logické.

Jako použil jsem to tak:

<?php
$presenter = $this->getPresenter();
$form->onSuccess[] = function (UI\Form $form) use ($presenter) {
			if (!$form->isValid())
				return;
			$presenter->flashMessage('Děkujeme...');
			$presenter->redirect('this');
		};
?>

Editoval jetpack (22. 10. 2013 15:27)

Aurielle
Člen | 1281
+
0
-

$this je v anonymních funkcích podporováno až od PHP 5.4. A mimochodem, při volání onSuccess() už formulář prošel validací, není proto nutné volat znovu $form->isValid().

jetpack
Člen | 71
+
0
-

@Aurielle: díky za info, PHP 5.4 jsem ještě neměl čas prostudovat.

jinak si říkám, proč používat anonymní funkce, když můžu v pohodě použít nette callback? Je to v tom nějaký zásadní rozdíl, problém ?

Editoval jetpack (22. 10. 2013 17:02)