vlastní formulářové prvky jako extension

Polki
Člen | 553
+
0
-

Ahoj, rád bych o radu.

Koukal jsem na vytváření vlastních formulářových prvků. OK Tady

Koukal jsem na vytváření ADD-ONů (doufám, že jsou to ty addony, které se například načítají v konfigu). OK Tady

Ale…

Jak to udělat, abych vytvořil pomocí prvního pravidla na vytváření vlastních formulářových prvků formulářový prvek a jen jej načetl jako extension a fííí.

Příklad:

config.neon

<?php
services:
	form_inputs: My/Form/Inputs
?>

FormFactory:

<?php
...
$form = new Form();
$form->addOwninput('name', 'label');

...

return $form;
...
?>

Jde mi hlavně o to, kde co musím přepsat a tak, abych nemusel nikde ručně nastavovat žádné věci v Bootstrap.php a tak.

Editoval Polki (6. 12. 2018 8:18)

Milo
Nette Core | 1283
+
0
-

Metody addText(), addSelect() a spol. jsou v podstatě jen továrny, které udělají dvě věci. Vytvoří form control a připojí ho k formuláři. Aby se daly prvky použít, není potřeba formulář nijak rozšiřovat o nové metody, není potřeba ani DI extension. Postačí:

$control = new OwnInput('label');
$form['name'] = $control;
Milo
Nette Core | 1283
+
+3
-

Ještě mě napadlo… Pokud chceš své form controls dát ostatním k dispozici a ulehčit jim integraci, můžeš připravit traitu:

trait Polki\FormControls
{
	public function addOwnInput(string $name, string $label): OwnInput
	{
		return $this[$name] = new OwnInput($label);
	}
}

a zájemce, který si bude chtít svůj formulář o tyto továrničky rozšířit jen udělá:

class AppForm extends Nette\Application\UI\Form
{
	use Polki\FormControls;
}
Polki
Člen | 553
+
0
-

@Milo To právě ale nechci.

Nejde mi o to, aby si zájemce musel vytvářet vlastní form, který bude využívat mé prvky, ale chci, aby to fungovalo jako klasický addon, přičemž když si jej nainstaluješ pomocí composeru, tak se pak jen zaregistruje v configu a automaticky jej pak budeš využívat u formulářů a to ne pomocí:

$control = new OwnInput('label');
$form['name'] = $control;

Ale pomocí

$form->add<názevMojíControl>();

Jestli mi tedy rozumíš.

Edit 1:
Aby to bylo krásné, čisté jednoduché jako použití obyč. formuláře s prvky, které si sám vytvořím a zaregistruju v bootstrapu. Použití, jak jsou všichni Nette frajeři zvyklí.

Edit 2:
A ano, chci to dát ostatním k dispozici, protože v rámci naší firmy to bude využito ve více aplikacích a bude s tím pracovat více vývojářů.

Edit 3:
Vlastně neco jako tento addon. Myslíš, že by jsi mi mohl poradit @VojtěchDobeš ?

Editoval Polki (4. 12. 2018 22:55)

jiri.pudil
Nette Blogger | 1028
+
+2
-

Ta extension může vypadat nějak takto (píšu dost z hlavy, takže to možná nebude úplně přesně, ale aspoň pro odpíchnutí to snad postačí):

use Nette\DI\CompilerExtension;
use Nette\PhpGenerator\ClassType;

class OwnInputExtension extends CompilerExtension
{

	/**
	 * Metoda afterCompile() se na rozšířeních volá poté,
	 * co se zkompiluje kontejner. Jako parametr dostává
	 * předpis onoho zkompilovaného kontejneru, takže ho
	 * může ještě na poslední chvíli upravit.
	 */
	public function afterCompile(ClassType $class): void
	{
		// typicky se upravuje metoda initialize(), která
		// se na kontejneru volá při jeho instancování
		$initialize = $class->methods['initialize'];

		// do těla metody přidáme řádek s kódem, který
		// zaregistruje extension method nad formulářem
		$initialize->addBody(
			'Nette\\Forms\\Container::extensionMethod(?, ?);',
			[
				'addOwnInput',
				'function ($container, $name, $caption = NULL) { return $container[$name] = new My\\Own\\Input($caption); }'
			]
		);
	}

}

Nicméně pokud chceš i to, aby tu metodu našeptávalo IDE nebo aby o ní věděl např. PHPStan, nevyhneš se potřebě v aplikaci vytvořit klidně prázdného potomka třídy Form a tu metodu tam aspoň oanotovat pomocí @method

Polki
Člen | 553
+
0
-

Jestli to bude fungovat, tak to bude boží. Přesně tohle jsem hledal.

Přepsat extension method ne ručně, ale přímo tady. Fakt super. Odzkouším a dám vědět.

Zatím díky.

Polki
Člen | 553
+
0
-

@jiri.pudil
Tak toto vyhazuje exception

Cannot dump closure.

Na konci toho pole. Nejake napady?

Milo
Nette Core | 1283
+
+3
-

extensionMethod() na form kontejneru je sice pohodlné, ale rozhodně ne čisté. IDE nenapoví, že tam ty metody jsou a pokud je někdo použije, bude řvát. Řeší se to poděděním UI\Form a přidáním anotací @method ..., ale to je zase pro uživatele dost pracné. Traita je IMHO nejčistější. Případně ještě helpery a wrappery typu:

PolkiInputs::addOwnInput($form, 'name', 'label');

# nebo
(new PolkiInputs($form))->addOwnInput('name', 'label');

Cokoliv z toho je myslím čistější než extensionMethod().

Polki
Člen | 553
+
-4
-

@Milo
Nechci pridavat vlastni cele formulare a ani jen vlastni prvky, od kterych bych musel rucne vytvorit instanci. O to by se mel starat formular po tom, co mu reknu, co tam chci.

Vydanim vlastniho celeho formulare se musi vsude, kde byl pouzit klasicky nette form prepsat usingy a pote i formularove prvky tak, jak potrebujeme.

Ukolem je to co nejvice zjednodusit, takze extensionMethod() mi prijde jako nejlepsi volba, jelikoz se zmeni pouze dany input.

Toto reseni je treba, jelikoz mame hotovych spousty projektu, ktere jsou dost rozsahle a tyto inputy se budou nasazovat i zde, tak aby se prace co nejvice usnadnila.

Napovedy ide jsou zbytecne v tomto pripade, protoze nepujde o zadne slozite prvky. Jde jen o rozsireni formatovani vstupnich a vystupnich dat, aby to nemusel uzivatel psat rucne ale pouzivat jen setvalue a getvalue metody.

Timto bych uzavrel tema cistoty kodu na ukor potreb.

Otazka zustava stejna. Jak se zbavit Exception pri nastavovani extensionMethod?

Edit 1
Traita je mozna cista, ale nepripada v uvahu. Na jedne aplikaci mame radove stovky formularu a pro kazdy si vytvaret vlastni formular, ktery bude vyuzivat traitu je sebevrazda.

Editoval Polki (5. 12. 2018 12:02)

CZechBoY
Člen | 3608
+
+1
-

Ten formulář snad vytváříš pomocí továrny a nebo ručně vytváříš nějaký svůj base formulář. Traitu použiješ v tom jednom base formuláři (+ kontejneru) a nemusíš nic měnit.

Polki
Člen | 553
+
0
-

Jsem ve firme kratce. Videl jsem projekty. A vsude kde vytvari formulare to delaji tak, ze v presenteru vlozi toto:

public function createComponentMyForm() {
    $form = new Ui\Form();
    ...
}

Jak do tohoto jednoduse zakomponovat traitu?
Aby se to zmenilo na jednom miste a nemusel menit vse vsude?

h4kuna
Backer | 740
+
+4
-

Polki napsal(a):

Jsem ve firme kratce. Videl jsem projekty. A vsude kde vytvari formulare to delaji tak, ze v presenteru vlozi toto:

public function createComponentMyForm() {
    $form = new Ui\Form();
    ...
}

Tento postup je taky nevhodný :) Lepší je udělat si factory a využít DI

Editoval h4kuna (5. 12. 2018 12:37)

Polki
Člen | 553
+
0
-

Za to ale ja nemuzu, ze to tak maji delane :-D

Takze..

Nejake navrhy? Aby se nemusel predelavat cely system?

Edit 1
Oni totiz se ucili v nette z návodu ‚Piseme prvni aplikaci‘. Tam je to tak ukazane, tak si mysleli, ze to je DOGMA a uz to tak deleli vsude.

Editoval Polki (5. 12. 2018 15:49)

Ondřej Kubíček
Člen | 494
+
+1
-

to je uplně jednoduché, prostě chceš klasické extension

takže se inspiruj třeba tím gpspickerem (nebo možná najdi lépe udělané) a udělej to stejně, pak jednoduše budeš moct na formu zavolat $form->myOwnInput()

Polki
Člen | 553
+
0
-

@OndřejKubíček
To je posledni moznost. Nechci se kdo vi jak dlouho prodirat něčím kódem, proto se ptam tady, kde to muze pomoct i lidem, kteri budou neco takoveho resit v budoucnu.

Ondřej Kubíček
Člen | 494
+
+1
-

nene, to je právě první možnost
nechceš traitu, nechceš upravovat desítky formulářu
co jsem pochopil, chceš si udělat extension, kterou jen přidáš do configu, čímž jednoduše rozšíříš formulář o vlastní inputy

Milo
Nette Core | 1283
+
+2
-

Ty chceš napsat DI extension bez toho, abys tomu rozuměl. Řešení je na pár řádků, ale nikdo to nenapsal, protože to je špatné řešení. A nikdo by ho v budoucnu neměl použít.

Jestli aplikace používá 100ky formulářů bez továrny, je to chyba v návrhu. Oprava je časově náročná. Může se udělat ale postupně. Třeba takhle.

  • trait s vlastními inputy
  • vlastní Form poděděný od UI\Form
  • továrnu na vlastní Form
  • tam, kde se bude formulář upravovat nebo vytvářet, použít továrnu
  • typehinty se díky dedičnosti opravit nemusí
Polki
Člen | 553
+
0
-

Ondřej Kubíček napsal(a):

nene, to je právě první možnost
nechceš traitu, nechceš upravovat desítky formulářu
co jsem pochopil, chceš si udělat extension, kterou jen přidáš do configu, čímž jednoduše rozšíříš formulář o vlastní inputy

Ano, ale posledni moznost je procitat cizi kod.

Jinak vse co jsi napsal je true.

Polki
Člen | 553
+
-4
-

Milo napsal(a):

Ty chceš napsat DI extension bez toho, abys tomu rozuměl.

Kdybych tomu rozumel, tak se tady neptam. To je myslim logicky odvoditelne. Co naopak logicky odvoditelne neni je to, ze ty si prosazujes to co rikas bez ohledu na nejake pozadavky. To je jako by ti nekdo rekl ze chce abyse si mu upekl chleba v peci ale ty se ho snazil presvedcit, ze lepsi je rohlik z trouby i kdyz on potrebuje proste jen jednoduchy chleba.

Řešení je na pár řádků, ale nikdo to nenapsal, protože to je špatné řešení. A nikdo by ho v budoucnu neměl použít.

Co je a neni spatne reseni se da polemizovat. Kazdemu se zda lepsi neco jineho. Pro nase vyvojare je delani tovarny zbytecny kod navic.
Nehlede na to, ze vetsina extension rozsiruje prave jen formular, viz napriklad hojne pouzivany replicator. Takze to asi pouzite je, ale to by byla hadka o tom a ne vecna konverzace.

Jestli aplikace používá 100ky formulářů bez továrny, je to chyba v návrhu. Oprava je časově náročná. Může se udělat ale postupně. Třeba takhle.

  • trait s vlastními inputy
  • vlastní Form poděděný od UI\Form
  • továrnu na vlastní Form
  • tam, kde se bude formulář upravovat nebo vytvářet, použít továrnu
  • typehinty se díky dedičnosti opravit nemusí

U vsech vyse uvedenych zpusobu je nutny rozsahly zasah do kodu a nebo nutna tvorba tovaren a nebo vlastnich formularu, coz jak jsem psal vyse a cemu ty nerozumis neni zadane chovani.

Kdyz se tak ohanis cistotou, tak by me taky zajimalo, co je cisteho na tom, kdyz vim, ze bude napriklad v presenteru formular, tak si na nej vytvaret tovarnu, ktery mi stejne vrati jen instanci tridy form a neudela nic jineho. Takze vznikne trida navic, coz je psani navic, processorovy cas navic, pamet navic atd by se dalo pokracovat…
A to nemluvim o tom, ze kdyz dam jednu tovarnu pro vsechny formy, tak ve formu s dvemi obyc inputy nacitat spousty dalsich knihoven ktere nepotrebuju nekde pres tovarnu je uplne stejne spatne reseni jako udelat extension primo klasickemu formu.

Takze myslim ze uz se nemame tady o cem bavit a budu primarne resit toto s @OndřejKubíček
Timto ti diky a urcite toto budu i nadale vyuzivat ve svych projektech, kde to jiz dlouhou dobu takto mam, ale tady se to tak proste bohuzel pouzit neda. Takze diky za vecny prispevek

Mysteria
Člen | 797
+
+8
-

Polki napsal(a):

Kdyz se tak ohanis cistotou, tak by me taky zajimalo, co je cisteho na tom, kdyz vim, ze bude napriklad v presenteru formular, tak si na nej vytvaret tovarnu, ktery mi stejne vrati jen instanci tridy form a neudela nic jineho. Takze vznikne trida navic, coz je psani navic, processorovy cas navic, pamet navic atd by se dalo pokracovat…

Před pár lety bych se pod to podepsal a dělal jsem to stejně. Ale věř mi, stačilo si jednou namlátit ústa a od té doby už opravdu nejsem línej, napsat ten jeden interface navíc.

Ono to navíc většinou bývá tak že ve snaze na začátku si něco ulehčit nebo ušetřit čas se něco udělá né úplně tak, jak by mělo a až za půl roku budeš chtít něco upravit, tak zjistíš, že kvůli tomu „ušetřenému“ času dáš dvojnásobek na tu potřebnou úpravu. Vlastní zkušenost. :)

Milo
Nette Core | 1283
+
+3
-

@Polki Promiň, máš pravdu, že si kód můžeš psát, jak chceš a já neodpověděl na původní otázku. Nebudu na ní ale odpovidat, myslím si že to není správné.

CZechBoY
Člen | 3608
+
+4
-

Ja bych cely to Nette zahodil, vsak to jen spomaluje a dela zbytecnosti navic ;-)
Předpokládám ze ani testy nemas, protoze taky spomalujou vyvoj a je to zbytecnej kod, kterej se vlastne ani NESPOUSTI.

Polki
Člen | 553
+
-4
-

Mysteria napsal(a):

Polki napsal(a):

Kdyz se tak ohanis cistotou, tak by me taky zajimalo, co je cisteho na tom, kdyz vim, ze bude napriklad v presenteru formular, tak si na nej vytvaret tovarnu, ktery mi stejne vrati jen instanci tridy form a neudela nic jineho. Takze vznikne trida navic, coz je psani navic, processorovy cas navic, pamet navic atd by se dalo pokracovat…

Před pár lety bych se pod to podepsal a dělal jsem to stejně. Ale věř mi, stačilo si jednou namlátit ústa a od té doby už opravdu nejsem línej, napsat ten jeden interface navíc.

Ono to navíc většinou bývá tak že ve snaze na začátku si něco ulehčit nebo ušetřit čas se něco udělá né úplně tak, jak by mělo a až za půl roku budeš chtít něco upravit, tak zjistíš, že kvůli tomu „ušetřenému“ času dáš dvojnásobek na tu potřebnou úpravu. Vlastní zkušenost. :)

@Mysteria nejde o usetreny cas. Ani o jednoduchost navrhu. Spis jde o pro a proti.
Vsadim se, ze taky nebudes cpat do use vsechny knihovny co existuji, kdyby si je nahodou nekdy potreboval, ale pridas ji tam az tehdy, kdyz je to treba.
Proc? Proc vlastne je treba kdnihovny linkovat rucne a neni vsechno uz defaultne prilinkovane?
Dokazete si predstavit jak by to trvalo? To je reseni, ktere proste neudelas, protoze hello world by se zobrazoval tyden.

Proc to tak teda delat u formularu? Jasne, pokud potrebuju specificky formular, ktery budu jen rozsirovat, tak je nejlepsi volba udelat nejakeho predka, ktery bude pro tyto formulare spolecny a pouzivat jej tam, kde je treba. Takze v tomto pripade nejlepsi pouzit tovarnicku, cimz eliminujes do znacne miry duplicitu kodu. Tak samo, kdyz u techto formularu je treba udelat obsluhu, ktera se stale opakuje, tak tuto obsluhu nebudes tvorit 1000× v presenteru, ale vhodis ji jednoduse do tovarnicky a tahas si formular odtud.

Ale pouzivat tovarnicku na formulare, ktere ani jeden nemaji zadnou stejnou cast vyjma radku new Form();??? Jaky je tam ucel tovarnicky? Neudela kod prehlednejsim, neodstrani duplicity (misto new Form musis stale nejak ziskavat instance formu byt z tovarnicky), zvysuje processorovy cas potrebny k vykonani dane akce, zvysuje naroky na pamet, jelikoz se na haldu ukladaji nove instance trid a v neposledni rade jde o zabrany cas tvorbou teto tovarnicky. Jedine plus je, ze kdyby nekdy vyvstaly duplicity, tak je lze spojit do tovarnicky. Ale kdyz mas navrh formularu a kazdy je jiny, tak to je spise kontraproduktivni.

Nyni pristoupim na toto reseni a udelam vlastni tovarnicku na vlastni form, ktery rozsiruje klasicky form o formularove prvky. A ted nasledujici UseCase:

  • formular vyuzije kolega v aplikaci.
  • kolega zjisti, ze potrebuje dalsi input, ktery nema problem si vyrobit.
  • vyrobi si novy input a aby to mohli pouzivat ostatni, tak si k nemu vytvori take svou tovarnicku na svuj form a zaregistruje si jej jako extension.
  • kolega pouziva vesele muj form a svuj form.
  • treti kolega prijde a rekne ja potrebuji inputy z formulare od kolegy1 a zaroven v tom samem formulari potrebuji inputy od kolegy2
  • jak na to???

V tento moment zpoza zavesu vyskoci traita a hodi po nem ockem.
Pojd si pro me ja jsem reseni.
A kolega 3 zajasa jupiiii.
Kolega1 i Kolega2 predelaji vlastni formulare na traity a Kolega3 si vesele vytvori svuj form, ktery pouzije obe dve traity.
Situace zda se vyresena.
Jenze, takovych trait si vytvori N. Kazda bude mit nejakou svoji slozitost a kdyz pak bude pomoci tohoto formulare kolega3 delat komponentu, ktera bude mit napriklad 1 klasicky addText, tak i takovy formular bude mit narocnost komplexniho formulare, protoze se nacita spousta metod a pravidel.
Nic proti traitam ale v takovem pripade Kolega3 koukne po reseni, kde misto pouzivani toho sameho formu, kde se vyuzivaji vsechny traity sahne po reseni, kdy pro kazdy formular vytvori vlastni tovarnicku, ktera mu vrati instanci formulare upraveneho tak, aby vyuzival traity pouze ty, ktere je opravdu nutnost nacitat.

Pokud se pletu v tom jak funguji traity prosim o opravu.

Timto resenim ale vznikne M tovaren, kde je kazda jina a opet jsme u problemu, ze tyto tovarny jsou vlastne kus navic, ktery prinasi jen a pouze zvyseny processorovy cas, vetsi naroky na pamet a v neposledni rade take casove naroky na psani techto tovaren.

Nyni prichazi na radu opomijene extension method, ktere vlastne funguje stejne jako traita, ale s tim rozdilem, ze k tomu nemusis psat navic zadnou tridu bokem, takze odpada processorovy cas navic a pametova rezie trid navic.
Tim jsme si nacitani jednoho formulare zrychlili sice asi a milisekundu, ale pri 1000 pripojenych uzivatelich naraz se usetri vterina casu. Coz uz je sakra dost.
Kouzlo navic spociva v tom, ze se jiz nemusi dal nic psat a tedy to nezrychluje pouze odpoved klientovi, ale take cas nutny pro vyvoj aplikace. Ano, je zde stejny problem jako pri traite a pri N vlastnich formularovych prvcich, ale toto se do jiste miry resi tim, ze vlastne tato metoda neexistuje do doby, nez je pouzita. (Pokud se nepletu) je to jako kdyz se pouziva dedicnost a priradite potomka do promenne rodice. V pameti je ulozen cely potomek, ale processorova prace s tim je rychlejsi, jelikoz processor nevi, ze tyto metody a promenne potomka existuji, dokud mu to nereknes.

Ac to hodne lido ocividne nema rado, tak extensionMethod nebylo pridano pro nic za nic. Stejne jako php magicke gettery a settery. Kouzelne dynamicky pridavat a ubirat libovolne property? To je sen. Ulehci to psani kodu a instance te same tridy mohou mit jine property, coz je nekdy treba a nijak to nezpomaluje cas vykonavani, jelikoz nemusi tyto tridy dedit od spolecneho predka, ktery ma vsechny mozne property, ktere si vubec muze clovek nadeklarovat, ale ma jen ty sve, cimz s ostatnimi nepracuje. Vytvari se az kdyz jsou treba.

Taky se timto da poukazat na Nette motto: less code = more security. Vytvaret zbytecne tridy muze byt bezpecnostni dira.

Programatori nemaji radi to, co bylo vymysleno pro dobrou vec a urychli to vse, ale jsou natolik hloupi, ze to neumeji pouzit a tedy tam delaji spousty chyb. Viz GOTO.

@Milo
Nepsal jsem, ze si muzu kod psat jak chci. Jen jsem se snazil poukazat na to, ze kdyz nekdo pise kod podle navodu na ofiko Nette strankach a neni tam vysvetleno proc je to zrovna takto, tak pak vyvstavaji problemy, ktere se daji jednoduse resit. Ovsem proc to delat jednoduse, kdyz to jde tak krasne slozite ze?
To, ze tady na foru nedokazou ostatni odpovedet primo na otazku na kterou se tazajici pta, jsem si celkem zvykl, kdyz koukam na dotazy jinych. Ale ze to tak bude delat i nekdo, kdo ma znacku Nette Core, tak to jsem si nemyslel. Cekal jsem, ze takovy clovek uz bude trochu nad veci, dobre poradi, jak by to melo byt spravne (coz jsi udelal), ale take zodpovi dotaz, na ktery se tazajici ptal i kdyz s vystrahou NEDOPORUCENO.
Ocividne to tak nefunguje a jako vzdy si budu muset poradit sam. Nejakymi hacky, ktere se ani me nelibi. Uz se nedivim, ze na jinych nez nette forech je vetsinou konecna odpoved: ‚nepouziven nette‘ a to ja se za nej biju zuby nehty, prptoze mi za tech 12 let, co delam web aplikace usetril hodne prace.

Spis me mrzi tento pristup ale s tim nic neudelam kazdy jsme nejaky. Naopak si myslim, ze kdyz nemas v umyslu odpovvedet na otazku, tak by si se nemel konverzace ucastnit. Nic proti tve osobe. Ale takove komentare jsou stejne zbytecne jako tridy, ktere vytvori instanci neceho a jen tu vrati.

A vzhledem k tomu mottu, co jsem zminoval bych cekal, ze nette nejakou rychlou ficuru na to jak pridat formularove prvky do formulare tak, aby se o vytvareni jejich instanci staral sam formular bude mit a ne, ze kvuli naprogramovani bananu budu muset vytvorit gorillu a k ni cely prales.

Edit 1
Zkoušel jsem najít pár článků o traitách, abych si na ně udělal obrázek i od jiných lidí, než jen od zastánců Nette. Našel jsem tento článek a přijde mi celkem zajímavý. Můžete se tu podělit o názor.

Editoval Polki (5. 12. 2018 22:32)

Polki
Člen | 553
+
-2
-

CZechBoY napsal(a):

Ja bych cely to Nette zahodil, vsak to jen spomaluje a dela zbytecnosti navic ;-)
Předpokládám ze ani testy nemas, protoze taky spomalujou vyvoj a je to zbytecnej kod, kterej se vlastne ani NESPOUSTI.

@CZechBoY
Je fakt, ze napsane neco pomoci nette je pomalejsi, nez neco napsaneho pomoci cisteho PHP.

Otazka ale je, co chces delat. Jestli chces porad dokola overovat ty same veci, nebo pouzit framework, ktery to dela za tebe.

Pokud jde vsak o otazku toho, jestli si pri hello world vytvoris pro kazde pismenko tridu, ktera bude vzdy vracet jen dany znak a pak jeste tridu, ktere zadas ze chces ten znak a ona ti ho vrati, a nebo ty znaky napises rovnou do retezce, tak asi vsichni programatori na svete vi, co si zvolis.

Proste michas jabka s hruskama. Jak jsem popsal vyse. Tovarna ok, ale je zbytecna, kdyz je kazdy form jiny.

Pokud jde o testovani, tka se rika kdo netestuje testuje dvakrat. Otazka je, na kolik jsou programatori verici a veri vsemu, co jim rekne autorita typu ucitel ve skole, nebo jsou zvidavi a uci se to sami. Ja se kdyz se nudim bavim procitanim skript a to ze mi nekdo rekne takhleze se to dela mi vzdycky pripomene klasicky Opici experiment. Proste mi to tak ve skole rekli, tak to tak delam aniz vim proc a jake to ma dusledky a dopad.

Ono kdyz si problem dostatecne zjednodusis, tak psani testu je jen takova vec, ktera te ujisti o tom, ze jeste nespis. Nedokazu si moc predstavit, ze by nekdo testoval napriklad metodu na to, jestli je cislo sude.

Co chces testovat na jednom radku? A kdyz je metoda rozsahla a jsi chytry, tak ji rozkouskujes do logickych celku tak, aby kazda funkce provadela jednu vec a to tak, ze mas osetrene vsechny stavy co mohou nastat. V takovem pripade je pak test k tomu, ze si opravdu jen overis jestli vratil to, co mel, coz ale delas vlastne uz pri psani te metody a potom ten test nikdy nespustis, jelikoz mas vse osetrene a pripadnou chybu ti oznami tracy, ale tato chyba je mimo tvou metodu, protoze ji mas tak simple udelanou, ze v ni chyba neni a mas to otestovane pro vsechny vstupy.

Pri psani teto metody samozrejme pouzijes sem tam nejaky dump, ale az otestujes vsechny vstupy a vystupy, tak dumpy smazes a vis, ze je nikdy uz nepouzijes, jelikoz to otestovane uz mas.

Delam komplexni systemy uz 12 let a za tu dobu jsem test jako test ve smyslu treba vytvoreni testu pomoci Nette testeru, nebo podobneho nikdy neudelal a to ani v Jave, C#, C++ a dalsich, ve kterych delam. A vzdy program pracoval jak ma a rychle jak ma.

Takze ne Testy nepisu.

Edit 1
Mas ode me palec za vtipnost a dobre podany sarkasmus.

Editoval Polki (5. 12. 2018 20:54)

Polki
Člen | 553
+
-5
-

Taky mi prijde dost zajimave, ze se tu vsichni ohanite tim jak nette umite zvladate jak se ma co delat a jak je co ciste a kdo vi co, ale odpovedet mi na tento dotaz, nebo jednomu chudakovi na jeho dotaz , tak to je problem.

Nemluve o tomto chudakovi, ktery potreboval poradit s celkem banalni veci a vsichni se na nej 17 dni pekne vykaslali.

Polki
Člen | 553
+
0
-

@Milo a když je extensionMethod tak špatné, tak proč při vyhledávání na mě jako první vyskočí tento post, který je psaný přímo Davidem? Že by David záměrně dělal něco, co je naprosto k ničemu a nemělo by se to nikdy použít?

Mysteria
Člen | 797
+
+1
-

Polki napsal(a):
Stejne jako php magicke gettery a settery. Kouzelne dynamicky pridavat a ubirat libovolne property? To je sen. Ulehci to psani kodu a instance te same tridy mohou mit jine property, coz je nekdy treba a nijak to nezpomaluje cas vykonavani, jelikoz nemusi tyto tridy dedit od spolecneho predka, ktery ma vsechny mozne property, ktere si vubec muze clovek nadeklarovat, ale ma jen ty sve, cimz s ostatnimi nepracuje. Vytvari se az kdyz jsou treba.

Jestli tohle myslíš vážně, tak bych opravdu chtěl vidět ty komplexní systémy bez statické analýzy a testů, které fungují vždy správně a rychle, protože řekl bych že za těhle podmínek se ta šance blíží limitně nule. :) Respektive pár takových jsem viděl a místo úpravy se vždy přepisovala celá aplikace. Možná byla o pár ms pomalejší, ale minimálně byla vždy v takovém stavu, že na tom mohl kdokoliv další znalý daného frameworku pracovat, tzn. v Nette byly použity principy z Nette, v Symfony ze Symfony (tzn. v Symfony projektu použiju Twig i když Latte je pro mne mnohem lepší, ale to je mi k ničemu, když se ti 99% lidí na správu aplikace vykašle, protože jsi použil něco, co se prostě v Symfony nepoužívá).

Nehledě na to, že když ti jde o milisekundy procesorového času, tak nevidím důvod proč použít na něco tak kritického PHP, když můžu použít třeba GO, který bude 100× rychlejší samo o sobě i bez toho, abych řešil jeden „blbej“ interface.

Polki
Člen | 553
+
-3
-

Mysteria napsal(a):

Polki napsal(a):
Stejne jako php magicke gettery a settery. Kouzelne dynamicky pridavat a ubirat libovolne property? To je sen. Ulehci to psani kodu a instance te same tridy mohou mit jine property, coz je nekdy treba a nijak to nezpomaluje cas vykonavani, jelikoz nemusi tyto tridy dedit od spolecneho predka, ktery ma vsechny mozne property, ktere si vubec muze clovek nadeklarovat, ale ma jen ty sve, cimz s ostatnimi nepracuje. Vytvari se az kdyz jsou treba.

Jestli tohle myslíš vážně, tak bych opravdu chtěl vidět ty komplexní systémy bez statické analýzy a testů, které fungují vždy správně a rychle, protože řekl bych že za těhle podmínek se ta šance blíží limitně nule. :) Respektive pár takových jsem viděl a místo úpravy se vždy přepisovala celá aplikace. Možná byla o pár ms pomalejší, ale minimálně byla vždy v takovém stavu, že na tom mohl kdokoliv další znalý daného frameworku pracovat, tzn. v Nette byly použity principy z Nette, v Symfony ze Symfony (tzn. v Symfony projektu použiju Twig i když Latte je pro mne mnohem lepší, ale to je mi k ničemu, když se ti 99% lidí na správu aplikace vykašle, protože jsi použil něco, co se prostě v Symfony nepoužívá).

Nehledě na to, že když ti jde o milisekundy procesorového času, tak nevidím důvod proč použít na něco tak kritického PHP, když můžu použít třeba GO, který bude 100× rychlejší samo o sobě i bez toho, abych řešil jeden „blbej“ interface.

Spousty projektů. Viz Baťa , Fama Services od TescoSW atd…
Ale to zas tak podstatný není. Podstatný je, že ne na všech jsem dělal sám a nikdy se mnou nikdo problém neměl. Naopak se všichni divili, jak můžu pracovat tak rychle a efektivně. Například v TescoSW mi zadali úkol a řekli, máš na to měsíc. za týden jsem přišel s hotovým a oni na mě koukali, že to není možný.
To samé v Baťa. Dali mi dva úkoly a že prý kdy asi bych to mohl mít hotové. Tak jsem si pročetl zadání a říkám. Pokud to nestihnu do večera, tak Vám to pošlu v neděli. (Byl pátek večer.) Říkali, že to není možné, že jejich kodéři nad podobnými úkoly strávili skoro měsíc. No je fakt, že jsem to dodal až v pondělí. Ale coby ne.

A úpravy? Jako upřímně…

  • Když to děláš mnou popsaným způsobem, tak přidání funkcionality = přidat pár metod, které neovlivní dosavadní chování.
  • Když potřebuješ upravit aktuální chování, sáhneš do některé metody a upravíš její chování tak, aby to odpovídalo tomu co chceš a jelikož jsou metody podle mého návrhu tak jednoduché, tak není důvod ji upravit a v rychlosti otestovat úpravy.
  • Pokud jde o úpravu konstant, tak když se držíš základní metodiky psaní kódů, tak úprava jedné konstanty pozmění chování, ale málokdy je schopna ti zapříčinit chybu metody. Jasně, pokud někdo v metodě, která řeší odmocninu pozmění konstantu podmínky větší než 0 například na číslo 1, tak v intervalu <1, 0> se odmocnina neprovede a při záporné hodnotě to vyhodí exception. Takový člověk je pak ovšem sám trošku mimo, nehledě na to, že tato podmínka se dá nahradit metodou isPositive(), nebo makrem, které je rychlejší na vykonávání, což sice v PHP není, ale pořád se oplatí vědět, že něco takového existuje. A pak je jasné, že asi metodu isPositive nebudu měnit aby zjišťovala, jestli je číslo v intervalu (infinity, –5> tím, že pozměním konstantu podmínky, ale vytvořím si na to jinou metodu, která to bude dělat

Chci tím poukázat na to, že dobře navržený kód se lehce upravuje, Využívá plnou sílu jazyka a Frameworku, který je použit a zároveň jej není nutné testovat, jelikož metody pro práci jsou tak triviální a mají takovou funkci, která nelze zaměnit, čímž odpadá nutnost psaní automatických testů.

Edit 1
Jo a pokud jde o milisekundy a zvolení jiného jazyka. Každý jazyk má pro a proti a když děláš projekt, tak na základě požadavků je třeba rozlišit, jaký jazyk nasadíš. Nelze říct umím toto, tak to v tom bude. Prostě každé se hodí na něco. Otázka je, jestli když chceš dělat web, u kterých se říká, že stránka, která se načítá déle, než 200ms je špatná stránka, tak s výkonem dnešních PC nepotřebuješ sahat po špičkových výkonnostních technologiích, ale stačí ti PHP a nad tím i třeba to Nette.

Dobré. Klasická stránka (vygenerovaný text z Lorem Ipsum) se načte dejme tomu za 1ms. Pokud k tomu přidám nějakou třídu, vzroste toto například na 2ms. Přitom jen pořád zobrazuji jeden a ten samý text. Pak si přidám traitu, vlastní implementaci Stringu, jelikož se mi ten od nette nelíbí, nebo nedostačuje atd… Při oné implementaci Stringu, když nedostačuje klasická budiž. Ale například načtu knihovnu Strings a nebudu s ní pracovat, nebo třeba jen pro jistotu zavolám Strings:trim() nad tímto řetězcem i když vím, že je to statický řetězec a žádné nechtěné mezery jsem tam nedal. Toto mi rázem zvedne čas i když o milisekundy. S každou takovou úpravou, která je úplně zbytečná vzroste čas o pro jednoduché počítání o milisekundu. Při stovce takových zbytečností ti jeden dotaz na stránku bude trvat 100ms.

Místo 1ms, při které je výsledek úplně stejný a navíc i v té 1ms jsou dodrženy všechny náležitosti jazyka i frameworku…

Pak si představ, že na stránku přistoupí naráz ve stejný čas 100 lidí. Pro jednoduchost počítejme s jednojádrovým processorem, který zvládá tedy naráz pouze jeden process a i když je tedy processů více, tak jsou ve frontě a vykonávány po kouskách.

Tedy by to vypadalo následovně:

  • při připojení 100 lidí na stránku bez zbytečností = 100ms čas pro obsloužení všech lidí, takže se všem načte stránka za 100 ms (cca, v reálu by se jednomu načetla za 1ms, druhému za 2ms … stému za 100ms)
  • při připojení 100 lidí na stránku se zbytečnostmi = 100ms potřebný čas každý a jelikož by se každý připojoval jako nový process, tak by processor se snažil stejně pracovat na všech a díky přerozdělení processů by KAŽDÉMU trvalo načíst stránku cca 100 * 100ms = 10s.

Toto je už sakra rozdíl. A to jen proto, že byl programátor pako a udělal spousty zbytečností, tak v prvním případě se do 200ms dostaneme a v druhém jde zákazník radši ke konkurenci. Přičemž oba příklady pouze vypíší nějaký text.

Tak samo si vezměme příklad z praxe při vstupním pohovoru do firmy. Řekněme, že běžný plat průměrného programátora v ČR se pohybuje kolem 300/hod. To si člověk řekne to je dobrý. Jenže zaměstnavatel řekne já Vám dám 299/hod.
Tak si řekneš koruna sem, koruna tam. Beru.
Pak se podíváš na výplatní pásku a vidíš za měsíc (20 prac dní po 8mi hod.) 47840,–

Řekneš si to jsem si dobře vydělal.
Jenže když si to spočítáš s těmi 300/hod, tak už to je 48000,–
Řekneš si 160 korun, to mě nespasí.

Jenže pak jsi v práci rok, dva, deset.
A po 20 letech se podíváš zpět a řekneš si. Za celou dobu jsem vydělal 11 481 600,–
Kdybych bral 300/hod, tak jsem vydělal 11 520 000,–
A pak si řekneš, co by jsi tak udělal s těmi 38400,–, které by sis vydělal, když by si pracoval za 300 a ne za 299 a dával si třeba co hodinu tu korunu někam do pokladničky.

A teď si vem, že zaměstnavatel ti neřekne místo 300,– 299,–
Řekne ti 250,– a ty se s ním smlouváním dohodneš na nějakých optimistických 285,–

Pak si po té době budeš říkat jó kdybych tehdy vzal tu práci, kde mi dají fakt těch 300,– to bych si dneska za těch 576000 ,– pořídil nějaký fajnový fáro.

Přitom odvedeš stejnou práci za jiné peníze.

A pak se ptej zaměstnavatele, jestli je pro něj jednodužší řešit koruny, což jsou desetiny procenta v rámci výplaty, nebo přejít na jiný jazyk, za který se platí 2× méně, ale zato trvá vývoj 2× déle.

Pořád pro něj bude lepší použít dražší jazyk, ale mít hodně zakázek a snížit plat byť o korunu, než platit méně, ale delší dobu, protože platově ho to vyjde nastejno, ale jestli udělá za rok 10 projektů, nebo 5 je sakra rozdíl.

Editoval Polki (6. 12. 2018 0:24)

Milo
Nette Core | 1283
+
0
-

@Polki Tím posledním příspěvkem jsem chtěl naznačit, že už nebudu odpovídat. Diskuze se tu rozplizla na X nesouvisejících témat.

BigCharlie
Člen | 283
+
0
-

@Milo vím, že tu nechceš odpovídat (chápu proč), ale v jedné situaci mě nenapadá lepší řešení než extension method – ostatně proto jsem tohle našel.

Má někdo nápad, jak elegantně vyřešit custom control na úrovni containeru jinak než extension metodou? Tj. úryvek pseudokódu:

	$form = new Form; // UI form
	$mySection = $form->addContainer('mySection');
	$mySection->addMyControl();
}

Pro tuhle situaci lepší nápad nemám. Podědit si container a udělat si metodu na přidání vlastního containeru?

Martk
Člen | 651
+
+2
-

@BigCharlie Traita:

trait TCustomFormControls {

	public function addContainer(...) {
		... // returns new CustomContainer();
	}

	public function addMyControl(...) {
		...
	}

}
class CustomForm extends Form {

	use TCustomFormControls;

}
class CustomContainer extends Container {

	use TCustomFormControls;

}

Editoval Martk (29. 12. 2018 8:15)

Polki
Člen | 553
+
0
-

@Martk trait je možná fajn, ale přemrštěné používání vede k obřím třídám, kterým se chceš vyhnout.

souhlas, že by to takto šlo udělat, takže pokud @BigCharlie vyloženě nemáš nějaký důvod, proč traitu nepoužít, tak bych to tak zkusil.

@Martk dá se u trait udělat, že se budou z git repa stahovat pomocí composeru? Pokud ano máš návod jak na to? Příklad: mám 20 různých inputů a v aplikaci z nich použiju jen 5. Abych nemusel natáhnout všechny, tak natáhnu jen těch 5 a to tak, že každý input = 1 traita a tedy tam budu mít 5 use v každém formu, kde je chci použít (popřípadě v předkovi.).
Tedy chci pomocí composeru natáhnout jen těchto 5 trait, které budu využívat.

Poslední dotaz. Když budu chtít input dělat pomocí nějaké komponenty, která bude mít vlastní renderer, jak na to v traitách?

Martk
Člen | 651
+
0
-

@Polki Používám je velmi zřídka a jen tam, kde je to vhodné a splňuje to účel, tady imho ano.

Když si stáhneš celý repozitář přes composer, tak composer načítá třídy, až když je kód v běhové smyčce potřebuje. Těch 5 nepoužitých neincludne

Na poslední dotaz nemůžu odpovědět, protože nerozumím o co má jít.

BigCharlie
Člen | 283
+
0
-

@jiri.pudil skoro si to trefil (z hlavy!?!). Pro ostatní: kdybyste to chtěli použít, ten callback napište místo druhého otazníku. Když je jako argument, zkompiluje se to na řetězec (a dojde k chybě, řetězec není callable). Funguje – ale nakonec jsem skončil u traity, viz níže. Díky za tip.

@Martk Ano, nad tímhle jsem přemýšlel. Dělá to totéž co extension method + výhody: napovídá IDE, neřve PHPStan. Otestováno, funguje. Díky za nasměrování.

@Milo Díky za původní postrčení.

@Polki Nemáš něco komplexního z posledních let na githubu? Rád bych se přiučil…

Polki
Člen | 553
+
0
-

Martk napsal(a):

@Polki Používám je velmi zřídka a jen tam, kde je to vhodné a splňuje to účel, tady imho ano.

Když si stáhneš celý repozitář přes composer, tak composer načítá třídy, až když je kód v běhové smyčce potřebuje. Těch 5 nepoužitých neincludne

Na poslední dotaz nemůžu odpovědět, protože nerozumím o co má jít.

@Martk Takže pokud si vytvořím 20 trait, tyto hodím na git a stáhnu pomocí composeru a následně použiji jen 2 takto:

class MyForm extends Form {
    use MyTraits\trait1;
    use MyTraits\trait8;

    public function __construct(Translator $translator = null, IContainer $parent = null, $name = null) {
        parent::__construct($parent, $name);
        $this->setTranslator($translator);
    }
}

tak se ze staženého balíčku s traitama reálně využijí pouze tyto dvě (trait1 a trait8) a to až v momentě volání new MyForm(); ?

Pokud ano, tak je to supr.

K poslední otázce:

Máme například Google API na našeptávání adresy. Toto API umí do jednoho text inputu vložit naformátovanou adresu a ještě zapne našeptávač.
Toto API ale také umí udělat to, že ti jednotlivé části adresy vrátí jako JSON:

{address_parts: [
					postal_code: xxxxx,
					route: xxxxx,
					city: xxxxx
				]
}

Toto je pseudokód. Každopádně nějak takto response od google vypadá. Když si tato rozkouskovaná data chceš poslat na server, tak nejjednodužší je udělat další hidden input a tento plnit oním JSONem. Na straně serveru pak value hidden inputu dekodovat.

Otázka je, co když chci tuto funkcionalitu zabalit do ->addAddress($name, $label);

tedy udělat traitu, která přidává metodu ->addAddress() a při vykreslení pomocí makra ‚input‘ vykreslí místo jednoho pole to pole, které je vidět s našeptávačem a druhé hidden pole, které se bude plnit tím JSONem.

získání dat pomocí $values->$name vrátí asociativní pole, které obsahuje rozkouskovanou adresu podle JSONu a taky navíc celou adresu z inputu s našeptávačem.

Snad jsem to napsal dostatečně.

Polki
Člen | 553
+
0
-

@Polki Nemáš něco komplexního z posledních let na githubu? Rád bych se přiučil…

@BigCharlie nemám GitHub zatím nepoužívám. Ale uvažuju ze začnu, tak možná hodím nějaké kódy a pak ukázku.

Martk
Člen | 651
+
0
-

@Polki Ano, přesně takto to funguje. PHP zjistí, že třída „neexistuje“, zavolá autoload funkce, ta načte pomocí svého algoritmu požadovanou třídu (php soubor) a pak zjistí, že třída existuje a pokračuje dále, jinak končí s errorem.

Na to bych použil přepsání metod getControl(), loadHttpData(), getValue() ve třídě AdressControl. Tady máš příklad. Dal jsem tam navíc checkbox a v loadHttpData zpracovávám hodnotu checkboxu. getValue() upravíš podle sebe

Polki
Člen | 553
+
0
-

Martk napsal(a):

@Polki Ano, přesně takto to funguje. PHP zjistí, že třída „neexistuje“, zavolá autoload funkce, ta načte pomocí svého algoritmu požadovanou třídu (php soubor) a pak zjistí, že třída existuje a pokračuje dále, jinak končí s errorem.

Na to bych použil přepsání metod getControl(), loadHttpData(), getValue() ve třídě AdressControl. Tady máš příklad. Dal jsem tam navíc checkbox a v loadHttpData zpracovávám hodnotu checkboxu. getValue() upravíš podle sebe

@Martk A co když si vytvořím vlastní form, který má v sobě všechny tyto traity? Funguje to stejně? Tedy i když je v tom formu v use všech 20 trait, ale já vytvořím instanci formu, kde používám jen jednu traitu, je to pak LAZY? Tedy, že traity v use, ze kterých nevolám žádné funkce nevyužije?

Edit 1:
Ptám se proto, že je víc user friendly vytvářet jednu instanci třídy, která to vše umí, než si pamatovat hrozné množství trait, které musím před použitím přidat do use.

Editoval Polki (29. 12. 2018 19:35)

Martk
Člen | 651
+
0
-

Myslím, že se v tomto případu se načtou všechny traity. Ale takovéto optimalizace jsou k ničemu, zaměřil bych se na něco jiného a cachoval. Správná cache ti ušetří ms. Je velice dobré, že máš takovéto přemýšlení, ale nepřeháněl bych to.

Polki
Člen | 553
+
0
-

@Martk Super. Díky moc.