Abstrakce nad poli a objekty v Latte

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

O víkendu jsem si hrál se Symfony, kde je šablonovátko Twig. Líbí se mi tam, že mi může v šabloně jedno, jestli datový objekt je stdClass, pole nebo objekty s gettery, ke všemu se přistupuje stejně přes tečkový operátor.

Některé zápisy by šly pěkně zestručnit.

{$control['form']['input']->getLabel()}
{* případně díky Nette\Object ->label *}

by šlo napsat v Twigu asi takto:

{{ control.form.input.label }}

V případě pole je funkčnost jasná, v případě objektu se postupuje cca takto:

  1. kouknu, jestli existuje property
  2. kouknu, jestli existuje metoda getProperty (není potřeba kvůli šablonám dědit od Nette\Object)
  3. kouknu na ArrayAccess

Šlo by něco takového implementovat i do Latte? Bylo by asi potřeba najít jinou syntaxi, protože tečky kolidují se spojováním řetězců (možná šipky?).

Mikulas Dite
Člen | 756
+
0
-

Dobré zjednodušení, ale jako další filter, asi ne do rovnou latte.

Latte se snadněji překládá do php. Ale protože Object umí převádět getteru na „property“, teoreticky by stačilo ještě před latte přidat filter, který by nahradil ty tečky za ->. Akorát čistý array by se musel ošetřit, nebo podobné, které nemají jako předka Object.

Filip Procházka
Moderator | 4668
+
0
-

Stačilo by přidat nějakou utilitku na „cestování objektem“ a předpřeložit filtrem

{$neco.tralala.tralalay}
popř.
{$neco->tralala->tralalay}

na

{=Nette\ObjectTravel::in($neco)->using('tralala', 'tralalay')}

Dobrý nápad, fandím :)

Petr Motejlek
Člen | 293
+
0
-

O něčem podobném jsem přemýšlel už minulý týden. Hrál jsem si zrovna s Tapestry a tam se mi líbilo, že při tom traverzování to i samo hlídá, aby někde nevyskočila NullPointerException, kdybych náhodou volal něco nad ničím ;).

Ten nápad s tím automatickým voláním getterů/setterů, příp. přístupu k poli, je docela zajímavý, ale nemlžu se zbavit dojmu, že to přináší hodně velký WTF faktor. Pokud má objekt proměnnou, tak použiju tu. Pokud nemá proměnnou, ale má getter, použiju getter. Když nemá ani jedno, je otázka, jestli by to mělo umřít s chybou, nebo jen vrátit prázdný řetězec. Když do toho ještě přidáš kontrolu, jestli jméno tý proměnný náhodou není index pole, bude to divné.

Chtěl jsem něco, co by hlídalo, aby nedošlo k volání metody/přístupu k proměnné nad null, udělat sám. Rozšířit to o automatické volání getterů by bylo cool. Pokud chceš, můžeme se domluvit na nějaké koordinované spolupráci, abychom zbytečně oba nedělali to samé ;).

Jan Jakeš
Člen | 177
+
0
-

Super nápad. (Já bych to dal klidně přímo do Latte, ale chápu, že někteří asi budou proti.)

PJK
Člen | 70
+
0
-

Pěkné. Rozhodně by to měl být nový filtr, ať je jasné, o co jde. No a když už by to byl filtr, dá se používat i ta tečka…

Vyki
Člen | 388
+
0
-

Osobně bych byl pro spojování tečkou. Každému pak dojde, že se jedná o něco magického, protože to v PHP jinak nelze. Určitě bych zavrhl šipky, které v PHP mají své pevné místo a pro tento účel by se to chovalo naprosto neočekávaně, každý přeci předpokládá, že zápis

{$neco->tralala->tralalay}

udělá i v šabloně přesně to co by udělal v čistém PHP. Byla by to magie na druhou.

newPOPE
Člen | 648
+
0
-

No ale ked si uvedomis ze si v sablone resp. v Latte tak by to malo byt jasne.

Co sa tyka magie, tak ta je tam (myslim najvacia) ze budem pisat aaa.bbb.ccc a je mi jedno ci je to pole, objekt etc.. (A ako predpokladam, tak opat u nas v praci tomu nebude nik rozumiet :-D)

Ale keby sa to tykalo napr. vykreslovania formov tam je to velka pomoc.

PJK
Člen | 70
+
0
-

Udělal jsem si malý prototyp, separarátor si můžete změnit.

https://gist.github.com/919240

Použití:

{~var->bla->bla}
//totozne s
{~$var->bla->bla}

(Technické náležitosti jsem obšlehl z https://github.com/…rmMacros.php, rady a připomínky jsou vítány)

Předělal jsem si pro to pár templatů a jsem spíš pro klasickou šipku ->, protože mi to připadá víc čitelné.

Samozřejmě by se pak přidělaly všechny standardní funkce pro výpis proměnných (escapování atd).

Filip Procházka
Moderator | 4668
+
0
-

Ó můj bože, to chceš ten kód kopírovat do každé šablony na každý výskyt? :) Zkus to dát do té třídy a nahrazovat macro pouze statickým voláním

Mikulas Dite
Člen | 756
+
0
-

Object může implementovat array access, asi by bylo lepší dat první podmínku

if ($current implements ArrayAccess || is_array($current)) {
	// ...
} else if (is_object($current) {
	// ...
}
PJK
Člen | 70
+
0
-

HosipLan napsal(a):

Ó můj bože, to chceš ten kód kopírovat do každé šablony na každý výskyt? :) Zkus to dát do té třídy a nahrazovat macro pouze statickým voláním

Nojo, já bych rád, nejsem na tohle nijak hrdej, jenže Latte rozumím na úrovni uživatele. Chtěl jsem to tak samozřejmě udělat, jenže jsem narazil na takový problém: Když jsem tam dal jen callback do té třídy, tak jsem se zaboha nedokázal dostat k hodnotě té proměnné. Pokud víš, jak na to, tak by mě to dost zajímalo :)

grey
Člen | 94
+
0
-

Jo, zrovna na symfony jsem včera koukal a tohle se mi líbilo. A byl bych pro tečkovou syntaxi…

Filip Procházka
Moderator | 4668
+
0
-

PJK: zde prosím :) https://gist.github.com/919350 (funkční)

kravčo
Člen | 721
+
0
-

Separátor by bol krajší ako statická premenná, potom sa dá aj meniť :)

Mikulas Dite
Člen | 756
+
0
-

Ještě jsem to forknul, malinko přiupravil a přidal kontrolu, jestli to existuje, ať to vždy raději háže tu InvalidStateException než notice. https://gist.github.com/919433

Filip Procházka
Moderator | 4668
+
0
-

To je aspoň spolupráce panečku! :) Kdyby se takhle nadšeně psala dokumentace, kde bysme dneska byli :)

David Grudl
Nette Core | 8218
+
0
-

Kdysi jsem nad tím přemýšlel, ale nikoliv z důvodu úspory syntaxe (srovnejte:

{$control[form][input]->label}
{$control[form-input]->label}
{{ control.form.input.label }}

ale kvůli výkonu. Tedy při výskytu $a->b->c->d->X by se uložilo do pomocné proměnné $a->b->c->d a další $a->b->c->d->Y by se zpracovalo řádově rychleji.

Obecně jsem tedy pro (byť z jiného důvodu), ale chce to dobře promyslet syntax.

Pavel Kouřil
Člen | 128
+
0
-

Co se týče syntax, tak buď {{ control.form.input.label }} nebo klidně třeba {.control.form.input.label}; ale nevím, zda by ta tečka na začátku nekolidovala nějak?

na1k
Člen | 288
+
0
-

Pajka, myslím, že ta tečka na začátku by se velice snadno přehlédla/zapomněla

A jak by fungovalo odlišení v případě, že bych chtěl například label nějakého form. prvku (čistě teoreticky) použít jako parametr odkazu? Teď tuším funguje to, že pokud jde o řetězec, nemusí kolem něj být uvozovky. Ta tečková notace by pak lehce zaváněla konkatenací.

Tím neříkám, že se mi tečková notace nelíbí, ale neměla by být matoucí při použití v současné syntaxi. A přiznávám se, že ani nevím co všechno si můžu v Latte dovolit a tak často zkouším a většinou mi to projde :) Tak jen aby s touhle benevolencí nebyl nakonec ještě problém při přidávání nových věcí :-p

PJK
Člen | 70
+
0
-

Upravil jsem https://gist.github.com/919240

  • Obsahuje všechna vylepšení a opravy, opravuje chybu v https://gist.github.com/919433, l65
  • Upřednostňuje getter před přímým přístupem k property (měl by? zajímal by mě váš názor)
  • Přidal jsem obdobu !$ makra. (!~$ nefunguje, vyžadovalo by úpravy v parseru (https://api.nette.org/…ser.php.html#422), aby rozeznával i delší speciální jména, nicméně pro zachování konzistence jsem ho přidal).
  • Zajímalo mě, jestli je cachování referencí opravu o tolik rychleší, hrubé testy ukazují, že rozdíl je opravdu velmi výrazný: https://gist.github.com/944108

A nyní… FLAMETIME!

Syntaxe:
Pokusím se shrnout všechny možné přístupy

  • přidání nového macra {~$var->prop->bla|helper} (vězte, že tildu jsem zvolil naprosto náhodně a může to být cokoliv jiného)
  • zavedení standardního univerzálního operátoru přímo do celého latte: {$a.b.c}
  • navrhovaná syntaxe dvojitých závorek {{$a.b.c|helper}}
  • nějaký další filtr a úplně nová syntaxe. Napadá mě třeba {`$a.b.c`|helper}, což se přeloží třeba na {$a->b->getC()|helper} a dál zpracuje standardně latte

U druhé možnosti mluvím o „celém latte“. Kdybychom chtěli zavést jednotý operátor, pak by nejspíš bylo žádoucí využívat ho skuečně všude – cykly, podmínky, linky, atd… Podobná možnost je i u dvojitých závorek, čímž by se zachovali obě možnosti zápisu:

//klasicka
{if $a->b['c']}
	bla
{/if}

//nova
{{if $a.b.c}}
	bla
{{/if}}

Stejné vlastnosti má i poslední nápad, navíc by asi výrazně zjednodušil a zrychlil zpracování.

Co je potřeba rozhodnout:

  1. Chceme tenhle vylepšovák zavést jako (jedninou možnou syntaxy v celém latte | volitelnou syntaxi v celém latte | nové makro, tedy jen pro výpis)?
  2. Jaký operátor použít
  3. Detail zvoleného přístupu

Myslím, že by bylo rozumné shodnout se na první otázce a pak teprve pokračovat dál.

Můj osobní názor je takový, že současná syntaxe není nijak špatná, tudíž bych to integroval hlouběji do latte jen společně s cachováním těch referencí, jinak bych si to nechal jako šikovné rozšiřující makro. Upřímně, nejsem nijak nadšený z vyhlídky dalších větších změn, ale tohle by mohlo stát za to. (Ono by to dost slušně šlo i teď, málokdo v šablonách střídá různé typy zápisu (getter/property, []/getOffset, atd.))

Editoval PJK (27. 4. 2011 16:18)

Nox
Člen | 378
+
0
-

Dal bych asi do celého, jen pro výpisy by to nepomohlo až tolik a navíc takto by měl zbytečně člověk všude různé zápisy – narozdíl od {} vs n: vynuceně pokud by chtěl tuto abstrakci používat
(podobně ~, {{}} atd.)

Předpokládám že nevýhoda varianty „univerzálně a volitelně“ je jen výkonnostní – jedná se ale jen o překlad do šablony, takže defakto jednorázově a neměl by to IMHO být problém? V takovém případě bych byl pro tuto variantu – asi nejelegantnější

Akorát mě napadá jak to udělat aby se to nepletlo s PHP tečkou..aktuálně {if $a.„1“ == „abc1“} je možné, že?

Editoval Nox (27. 4. 2011 16:08)

Honza Marek
Člen | 1664
+
0
-

Rozhodně bych byl pro zabudování přímo do latte, aby to šlo použít i v podmínkách a podobně, nejen pro výpis.

paranoiq
Člen | 392
+
0
-

@nox: v době překladu šablony to udělat nelze (tady nepomůže ani metoda „předvídání budoucnosti“)

grey
Člen | 94
+
0
-

jsem taky pro zabudování přímo do latte, nevidím důvod proč to tam nedávat… koneckonců je to šablonovací jazyk, takže od php se můžeme vzdálit…

veena
Člen | 98
+
0
-

Pro inspiraci: tečková notace se používá i v templatovacím jazyku v djangu. Viz http://docs.djangoproject.com/…s/templates/#…

Hledá to postupně jestli ta proměnná je:

  • index v asociativním poli (slovník v pythonu)
  • atribut objektu
  • metoda (volání metody bez parametrů)
  • index číslovaného pole (list v pythonu)

Je to intuitivní a zapamatovatelný.

jtousek
Člen | 951
+
0
-

V případě Nette volání metody bez parametrů smysl nemá, respektive místo toho tu máme Nette\Object::__get().