[Best Practise] Lokalizace formularu a komponent
- Jan Mikeš
- Člen | 771
Mam funkcni vlastni DbTranslator a hledam akorat snazsi cestu (mene psani – automaticky?) prekladani formulari a komponent.
Momentalne pouzivam toto:
Je predpoklad ze $this->translator je objekt implementujici rozhrani
Nette\Localization\ITranslator
Form:
protected function createComponentRandomForm()
{
$form = new Forms\RandomForm($dependencies);
$form->setTranslator($this->translator);
return $form;
}
U formu to resim takto, da se resit i predavanim translatoru pres
konstruktor a resit az v one tride (viz nize, to pouzivam u ostatnich
neformularovych komponent).
Neda se to resit nejak pres spolecneho predka bez predavani vsech zavislosti,
nebo je to opravdu best-way?
Komponenty:
protected function createComponentRandomControl()
{
$component = new Components\RandomControl($this->translator, $otherDependencies);
return $component;
}
// RandomControl.php:
public function render()
{
//...
$this->template->setTranslator($this->translator);
//...
}
Premyslel jsem nad vytvoreni factory, ktera by tyto komponenty obalila do dalsi komponenty, ktera by dostala translator a pripadne dalsi zavislosti primo z configu skrze factory.
Hledam pouze lepsi reseni a zpusob jak transparentne a automaticky prekladat vsechny komponenty + formulare, abych nemusel u kazdeho z desitek nove pridavanych formularu a komponent pamatovat na to, ze musim predat translator.
Jeste jsem se setkal s timto (BaseForm.php a BaseControl.php, ktere se extenduji ve formu namisto prislusnych UI class):
public function __construct(Nette\ComponentModel\IContainer $parent = NULL, $name = NULL) {
parent::__construct($parent, $name);
$this->setTranslator(Nette\Environment::getContext()->translator);
}
Toto mi prijde ale jako hodne hodne hooodne nepekne reseni.
Take me napadlo, co v attached metode volat neco ve smyslu
$this->setTranslator($presenter->translator);
Jedna se ale o skrytou zavislost a nevim zda neni v attached na preklady jiz pozde (nezkousel jsem)
Predem diky za odpovedi.
Editoval Lexi (27. 8. 2013 9:49)
- Filip Procházka
- Moderator | 4668
Děláš to zbytečně složitě.
class BaseForm extends \Nette\Application\UI\Form
{
protected function attached($parent)
{
parent::attached($parent);
if (!$parent instanceof Nette\Application\UI\Presenter) {
return;
}
$translator = $this->presenter->context
->getByType('Nette\Localization\ITranslator');
$this->setTranslator($translator);
}
}
Když tvoje formuláře budou od tohoto dědit, nebo je budeš na tomto alespoň sestavovat, tak se ti vždy po připojení do presenteru translator nastaví.
- jasir
- Člen | 746
A nebo použij (taky hosiplanovo, uff :o) autowired component factories
kdyby/autowired,
což je ten tvůj přístup přes factories.
- Jan Mikeš
- Člen | 771
Filip Procházka napsal(a):
Děláš to zbytečně složitě.
$translator = $this->presenter->context
Ok, takze v attached neni pozde, neni ale context taky fujky? Kazdopadne lazy reseni to je, ale nevim jestli by pro me oko uz nebylo hezci $this->presenter->translator (translator mivam vzdy zaregistrovat v hlavnim BasePresenteru, takze se nemusim obavat ze by nekde chybel)
jasir napsal(a):
A nebo použij (taky hosiplanovo, uff :o) autowired component factories kdyby/autowired, což je ten tvůj přístup přes factories.
No jo, ale pokud se nemyslim tak to neudela nic jineho, nez ze mi prida service ITranslator do tovarnicky, odkud si stejne budu muset translator predat bud pomoci konstruktoru nebo nastavit ->setTranslator(), nebo jsi to myslel jinak?
Editoval Lexi (27. 8. 2013 18:17)
- enumag
- Člen | 2118
@Filip Procházka: Odkdy zrovna ty v Best Practice vlákně uvádíš context? :-O
Osobně nemám rád dědění třídy Nette\Application\UI\Form takže si Best Practice představuji přibližně takto:
class WhateverFormFactory extends \Nette\Object
{
/**
* @var \Nette\Localization\ITranslator
*/
private $translator;
public function __construct(\Nette\Localization\ITranslator $translator)
{
$this->translator = $translator;
}
public function create()
{
$form = new \Nette\Application\UI\Form;
$form->setTranslator($this->translator);
// define fields
return $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 (27. 8. 2013 18:39)
- Filip Procházka
- Moderator | 4668
Máte pravdu v tom, že context není best practise, v tomhle případě to ale ušetří opravdu hodně psaní.
Nejčistější řešení je mít
na to buď konkrétní továrnu, nebo generovanou a v setupu mít volání
setTranslator
.
- Jan Mikeš
- Člen | 771
Diky, pomohli jste mi oba, v tomto pripade pouziji asi $this->presenter->translator v attached metode, sice to neni uplne transparentni ale zde mi to usetri hodne psani a nemusim prekopavat cely projekt.
Enumagovo reseni se mi take libi, je nejaky zasadni duvod proc nepouzivat
extendovani UI\Form?
Nebo tam je pouze rozdil v pouzivani?
Rikam si, ze pokud budu vyuzivat ciste tovarny, tak prijdu o moznost dedicnosti jednotlivych formularu navzajem (subformulare..), sice stale mam kompozici a moznost pridavat containery, to ale nemusi uplne vzdy vyhovovat.
Ma to jine (ne)vyhody?
- enumag
- Člen | 2118
@Lexi:
Důvod jsem uváděl zde pro Permission. Pro formuláře platí přesně totéž. Není to až tak zásadní, je to prostě můj pohled na věc.
Pokud jde o znovuvyužití formulářů, doporučuji přečíst https://forum.nette.org/…nebo-tovarna.
- Jan Mikeš
- Člen | 771
Dopracoval jsem se k necemu takovemu, jenom si nejsem jisty jestli je to uplne ok, nebo by se to dalo jeste o neco vylepsit:
BaseFormFactory.php:
class BaseFormFactory extends Nette\Object {
/** @var Nette\Localization\ITranslator */
private $translator;
public function __construct(Nette\Localization\ITranslator $translator)
{
$this->translator = $translator;
}
public function create()
{
$form = new Nette\Application\UI\Form;
$form->setTranslator($this->translator);
return $form;
}
}
SomeFormFactory.php
class SomeFormFactory extends Nette\Object {
/** @var BaseFormFactory */
private $baseFormFactory;
public function __construct(BaseFormFactory $baseFormFactory)
{
$this->baseFormFactory = $baseFormFactory;
}
public function create()
{
$form = $this->baseFormFactory->create();
// ...
return $form;
}
// ...
}
Neni to uz moc prekombinovane s temi tovarnami? Me to pripada v poradku,
ale 100% jisty si tim nejsem.
Jde o to, ze baseform krom translatoru obsahuje i jine veci, antispam, muze
prijimat i jine sluzby a chci se vyhnout tomu, abych pri nahodnem pridani
sluzby, kterou bych chtle vyuzivat ve vsech formech musel editovat vsechny
konstruktory vsech tovarnicek.
- Milo
- Nette Core | 1283
Trochu OT… Nepotýkáte se pak s problémem, že u továren je vlastně
každý formulář UI\Form
? Hůř se hledá, kde všude v aplikaci
je formulář použit a nelze používat přesný typehint. Je to spíš
akademický problém, ale nebylo by lepší podědit UI\Form
a na
něj pak továrnu?
class AnyFrom extends UI\Form {}
class AnyFormFactory extens Nette\Object {
function create() {
$form = new AnyForm;
return $form;
}
}
- Filip Procházka
- Moderator | 4668
@enumag Víc se mi líbí @Lexi-ho řešení :) Je lépe rozšiřitelné a o ždibíček čistější.