Převod formuláře ve stylu nette 0.8 do nového Nette

miho
Člen | 13
+
0
-

Ahoj. Převzal jsem apku psanou v Nette 0.8. Potřebuji ji dostat do novější verze Nette. Je v ní hromada (100+) formulářů udělaných „postaru“:

public function renderLogin()
	{
		$form = new Form;
		$form->addText('name', 'Jméno:')->addRule(Form::FILLED, 'Zadejte jméno');
		$form->addPassword('password', 'Heslo:')->addRule(Form::FILLED, 'Zadejte heslo');
		$form->addSubmit('login', 'Přihlásit');

		if ($form->isSubmitted()) {
			if ($form->isValid()) {
				$values = $form->getValues();

				//udělej něco

			}
		}

		$this->template->form = $form;
	}

Když to budu všechno předělávat na komponenty, tak se z toho zblázním, takže se pokouším to ochcat. Když formulář ukotvím přes $form->setParent($this); tak to po odeslání hodí There is no handler for signal ‚-submit‘ in class App\Presenters\HomepagePresenter. Což dává smysl, form samotný handler nemá… ale moc mi to nepomáhá když chci, aby handloval přímo presenter. Je nějaká šance to v novém Nette rozjet v této podobě? Nebo alespoň tak, že vytvoření formu nechám jak je a handler onSuccess hodím mimo? Zkoušel jsem $form->onSuccess[] = [$this, ‚fakeHandler‘]; a přidat public function fakeHandler(UI\Form $form, $data): void { } ale hodí to stejnou chybu. Když použiju pro ukotvení $this->addComponent($form,‚loginform‘); tak to hodí The signal receiver component ‚loginform‘ is not found.

Marek Bartoš
Nette Blogger | 1146
+
0
-

addComponent() je správně pro přidání komponenty do stromu a zbavíš se tím chyby s handlerem. Problém ale je, že signál se zpracovává mezi action* a render*, přidávat definici formuláře v renderu je tedy pozdě. Přesuň definici do actionLogin() a mělo by ti to fungovat.

onSuccess nemusíš řešit, to isSubmitted() a isValid() je v podstatě jen alternativní zápis.

Editoval Marek Bartoš (28. 3. 2023 18:05)

miho
Člen | 13
+
0
-

Zkoušel jsem také


	public function renderLogin()
	{

		$form = new Form;
		$form->addText('name', 'Jméno:')->addRule(Form::FILLED, 'Zadejte jméno');
		$form->addPassword('password', 'Heslo:')->addRule(Form::FILLED, 'Zadejte heslo');
		$form->addSubmit('login', 'Přihlásit');

    $form->onSuccess[] = function (Form $form, \stdClass $values): void {

		//...

    };

    $form->setParent($this);



		$this->template->form = $form;

	}

ale se stejným výsledkem: There is no handler for signal ‚-submit‘ in class App\Presenters\HomepagePresenter. Hrozná cíťa ta Nette ;-)

m.brecher
Generous Backer | 717
+
-2
-

@miho

Když to budu všechno předělávat na komponenty, tak se z toho zblázním, takže se pokouším to ochcat.

Píšu z hlavy, takže bez záruky, ale myslím, že komponenty z toho dělat nemusíš, formuláře fungují pořád postaru i bez komponent a bez factory rovnou v presenteru.

Co možná už je jinak je metoda isValid()? Našeptávač IDE me našeptal metodu $form->isSuccess()

Tak to zkus takhle – jednoduše vyhodit isSubmitted() + isValid() a místo toho dej isSuccess() ;) mohlo by to řešit problém.

if ($form->isSuccess) {
				$values = $form->getValues();

				//udělej něco

		}

Editoval m.brecher (28. 3. 2023 18:30)

m.brecher
Generous Backer | 717
+
0
-

@miho

Napsal jsem to blbě, sorry,

problém je v tom, že chceš na formuláři nastavit handler v době, když už je po handlování = už proběhl signal processing presenteru !!

Můžeš metody renderSomeForm() přepsat na actionSomeForm() (action proběhne před zpracováním signálu) + místo ->isSuccess() použít korektní callback $form->onSuccess[] = function(…){…}

Mohou teoreticky nastat další komplikace, potom nezbyde než korektně napsat jak je doporučeno v dokumentaci createComponentSomeForm(). Zkus a uvidíš.

Editoval m.brecher (28. 3. 2023 18:30)

m.brecher
Generous Backer | 717
+
0
-

@miho

Nějak takhle:

public function actionLogin()
	{
		$form = new Form;
		$form->addText('name', 'Jméno:')->addRule(Form::FILLED, 'Zadejte jméno');
		$form->addPassword('password', 'Heslo:')->addRule(Form::FILLED, 'Zadejte heslo');
		$form->addSubmit('login', 'Přihlásit');

		$form->onSuccess[] = function (Form $form, \stdClass $values): void {

		//  dělej něco ...

    };

		$this->template->form = $form;
	}

Obejdeš tím elegantní koncept vytváření komponent až když jsou potřeba pomocí createComponent<Component>, ale když jsou toho stovky a není čas :). Fungovat by to mělo, formulář se vytvoří podle akce i kdyby se nakonec nevykresloval.

edit

Tak mě došlo, že formulář nebude v hierarchii komponent presenteru – právě proto, že jsme obešli createComponent<Component>() :(.

Tady už moje představivost končí, napadá mě ještě $presenter->addComponent(), to je poslední možnost:

public function actionLogin()
	{
		$form = new Form;
		$form->addText('name', 'Jméno:')->addRule(Form::FILLED, 'Zadejte jméno');
		$form->addPassword('password', 'Heslo:')->addRule(Form::FILLED, 'Zadejte heslo');
		$form->addSubmit('login', 'Přihlásit');

		$form->onSuccess[] = function (Form $form, \stdClass $values): void {

		//  dělej něco ...

    };

		$this->addComponent($form, 'loginForm');
		$this->template->form = $form; // tohle je otázka jestli nevyhodit, záleží jak v šabloně voláš $form
	}

Editoval m.brecher (28. 3. 2023 18:37)

miho
Člen | 13
+
0
-

Tahle verze:

    public function actionLogin() {

		$form = new Form;
		$form->addText('name', 'Jméno:')->addRule(Form::FILLED, 'Zadejte jméno');
		$form->addPassword('password', 'Heslo:')->addRule(Form::FILLED, 'Zadejte heslo');
		$form->addSubmit('login', 'Přihlásit');
        $this->addComponent($form,'loginform');

		if ($form->isSubmitted()) {
			if ($form->isValid()) {
            $values = $form->getValues();

				//...

            }
        }

		$this->template->form = $form;

    }


	public function renderLogin()
	{
	}

funguje, do té podmínky se to zcela jistě dostane ale hodí to warning: User Warning Form was submitted but there are no associated handlers.

Editoval miho (28. 3. 2023 18:37)

m.brecher
Generous Backer | 717
+
-1
-

@miho

User Warning Form was submitted but there are no associated handlers.

Ano, musíš to napsat jak jsem ti poslal $form->onSuccess[] = …, ono se to musí volat později !! Callback !! Ve fázi action není form zpracovaný a není succeeded !!

Editoval m.brecher (28. 3. 2023 18:40)

m.brecher
Generous Backer | 717
+
-2
-

@miho

Tahle verze: … funguje, do té podmínky se to zcela jistě dostane …

Nedostane a nemůže dostat !!

Formulář je vytvořený jako objekt v RAM paměti na serveru, ale NENÍ připojen do stromu komponent presenteru! Presenter obdrží signál, ale nenajde formulář ani handler.

Tipoval bych, že aby to fungoval takhle jak to máš natvrdo v action<Action>() tak nejde obejít:

a) napsat to jako callback – jak jsem Ti poslal
b) ručně přidat $form do presenteru addComponent()

Editoval m.brecher (28. 3. 2023 18:45)

Marek Bartoš
Nette Blogger | 1146
+
+1
-

Fajn, na warning u chybějícího onSuccess jsem zapomněl, ale není pravda, že by to nefungovalo bez onSuccess :) Data z http requestu jsou v action dostupná, formulář je připojený do stromu a zvládne si je vytáhnout a Nette to správně validuje.
Warning je jen pro případ, kdy je úspěšné zpracování opomněné úplně.

@mbrecher Chápu, že se snažíš pomoct, zkus to však trochu zkráceně a kontrolovat si, zda to co tvrdíš tak skutečně je :) addComponent() tam má, nic jiného třeba není.

Editoval Marek Bartoš (28. 3. 2023 18:55)

miho
Člen | 13
+
0
-

Díky za rady @Marek Bartoš a @m.brecher – určitě by to takhle šlo ale bylo by to více přepisování. Finální, extrémně hnusná… ale funkční verze zarnuje nicnedělající falešný handler a skutečný handler v action (do těch podmínek se to dostane, takže jsou splněny:


    public function fakeHandler(UI\Form $form, $data): void {
    }

    public function actionLogin() {

		$form = new Form;
		$form->addText('name', 'Jméno:')->addRule(Form::FILLED, 'Zadejte jméno');
		$form->addPassword('password', 'Heslo:')->addRule(Form::FILLED, 'Zadejte heslo');
		$form->addSubmit('login', 'Přihlásit');
        $form->onSuccess[] = [$this, 'fakeHandler'];
        $this->addComponent($form,'form');

		if ($form->isSubmitted()) {
			if ($form->isValid()) {
            $values = $form->getValues();

				//----

            }
        }

		$this->template->form = $form;

    }


	public function renderLogin()
	{
	}

zkrátka je třeba to oblbnout, aby to dělalo to, co člověk potřebuje, tedy chová se to přesně jako MacOS ;-)

BTW takové zbytečné změny jako že už není $this->user->authenticate ale $this->user->login atd, přičemž příklady na netu uvádějí tu první variantu, to dělá @DavidGrudl schválně aby naštval lidi, nebo to je jen náhoda? ;-)

Editoval miho (28. 3. 2023 18:56)

Marek Bartoš
Nette Blogger | 1146
+
+1
-

$form->onSuccess[] = function() {} bude fungovat stejně dobře.

A fakt se to nedělá proto, aby byli lidi naštvaní. Dělá se to proto, že v tom lidi často chybovali nebo funkci pochopili špatně. A jestli nechceš být další s banem, tak bude příště vhodnější dotaz, proč to tak funguje, bez souzení jak se autor snaží dělat naschvály.

miho
Člen | 13
+
0
-

Neberte svět tak vážně, byl to (pravda, mírně kousavý) vtip. Omlouvám se, pokud někoho urazil.

Nebylo lepší login přidat a do autenthicate dát deprecated warning + zavolat login?

Editoval miho (28. 3. 2023 19:17)

David Grudl
Nette Core | 8082
+
+1
-

Jaké příklady na netu uvádí tu první variantu?

(btw původní metoda tam je)

miho
Člen | 13
+
0
-

Tak to se omlouvám, jako deprecated to skutečně bylo; ve 3.2 už není, vede to na Call to undefined method. Což se dá pochopit, už je to pár let.

Marek Bartoš
Nette Blogger | 1146
+
+1
-

Proto se postupuje po jednotlivých (x.y) verzích. Opravíš chyby, posuneš se dál. Ideálně s phpstanem a phpstan-deprecation-rules, které tě na všechny deprecations upozorní.

miho
Člen | 13
+
0
-

To bych musel několikrát pošoupnout verzi PHP a tím i knihoven a linuxu, který je pod tím (nebo to dát do dockeru). To zní děsivě.

Vypadá to, že tenhle task mě naučí dělat spoooustu prasáren. Hele na tu krásu:


class dibi {

	private static \Dibi\Connection $db;

	function __construct(\Dibi\Connection $db) {
		self::$db = $db;
	}

	public static function query(...$params) {
		$args = func_get_args();
		return call_user_func_array([self::$db,'query'], $args);
	}

}

A už nemusím přepisovat 478 výskytu dibi::query různě rozesetých v kódu a přidávat přes DI databázi do desítek tříd v modelu ;-)

David Grudl
Nette Core | 8082
+
+1
-

Jestli je někde použité authenticate(), tak to opravím. Jen mi musíš říct kde.

Marek Bartoš
Nette Blogger | 1146
+
0
-

To bych musel několikrát pošoupnout verzi PHP a tím i knihoven a linuxu, který je pod tím

Stačí ti spustit samotný phpstan na jiné verzi php. Kód phpstan nespouští, takže je to safe.