@inject kdekoliv nebo jen v presenteru?
- David Matějka
- Moderator | 6445
myslim, ze to nejak jde.. ale nepouzivej to :) ve sluzbach bys mel injectovat jen pres konstruktor
- Tomáš Jablonický
- Člen | 115
v presenterech a v controleru jinak v modelu už jen přes konstruktor nebo setr.
- zaachi
- Člen | 4
Zdravím,
mám problém v Nette 2.1 s manuálním vytvořením presenteru.
Jedná se o třídu Mailer, ve které bych rád využíval všechny fce a výhody presenteru, stejně jako své fce, které jsem již napsal do BasePresenteru:
<?php
class MailerPresenter extends BasePresenter {}
?>
Vytvoření instance v BasePresenteru:
<?php
protected $mailer;
public function startup()
{
$this->mailer = new MailerPresenter();
// Zde je třeba předat context Presenteru, ale bohužel nevím jak na to
// Ve staré verzi Nette stačilo Environment::getContext
}
public function renderXXX()
{
// Ukazka uziti
$this->mailer->registrationWebuserEmail(array('firstname' => 'Aaaa', 'email' => 'nejaky@email.cz'));
}
?>
Zkoušel jsem předat context i za pomocí Service:
services:
- App\FrontModule\Tools\MailerPresenter
A pak v BasePresenteru:
<?php
/** @var \App\FrontModule\Tools\MailerPresenter @inject */
public $mailer;
?>
Presenter se vytvoří správně, bohužel se nedaří předat potřebné závislosti. Děkuji za každou radu.
- David Matějka
- Moderator | 6445
@zaachi: priste prosim zaloz novy topic.
A k otazce: tohle neni spravny zpusob jak pouzivat presenter. Mailer by mela byt sluzba, ktera rozhodne od presenteru nededi. Co presne potrebujes za „fce a vyhody presenteru“?
- Pavel Macháň
- Člen | 282
zaachi napsal(a):
Zdravím,
mám problém v Nette 2.1 s manuálním vytvořením presenteru.Jedná se o třídu Mailer, ve které bych rád využíval všechny fce a výhody presenteru, stejně jako své fce, které jsem již napsal do BasePresenteru:
<?php class MailerPresenter extends BasePresenter {} ?>
Vytvoření instance v BasePresenteru:
<?php protected $mailer; public function startup() { $this->mailer = new MailerPresenter(); // Zde je třeba předat context Presenteru, ale bohužel nevím jak na to // Ve staré verzi Nette stačilo Environment::getContext } public function renderXXX() { // Ukazka uziti $this->mailer->registrationWebuserEmail(array('firstname' => 'Aaaa', 'email' => 'nejaky@email.cz')); } ?>
Zkoušel jsem předat context i za pomocí Service:
services: - App\FrontModule\Tools\MailerPresenter
A pak v BasePresenteru:
<?php /** @var \App\FrontModule\Tools\MailerPresenter @inject */ public $mailer; ?>
Presenter se vytvoří správně, bohužel se nedaří předat potřebné závislosti. Děkuji za každou radu.
v BasePresenter chceš závislost na MailerPresenter? To je dost divný né? Neměla to být spíš nějaká mailer služba než presenter?
- zaachi
- Člen | 4
matej21 napsal(a):
@zaachi: priste prosim zaloz novy topic.
A k otazce: tohle neni spravny zpusob jak pouzivat presenter. Mailer by mela byt sluzba, ktera rozhodne od presenteru nededi. Co presne potrebujes za „fce a vyhody presenteru“?
Nechtěl jsem zakládat nový topic, protože jinak na každém fóru tě hned zjedou, proč jsem to nevložil do již vytvořeného..
Uznávám, že to není úplně správné užití presenteru, ale potřebuju některé jeho součásti pro vytváření templat pro emaily.
- $this->link()
- $this->createTemplate()
- helpery
- překlady
- ..
Spíš by mě zajímalo jestli a jak je možné předat závislosti presenteru, vytvořenému ručně.
Díky
- David Matějka
- Moderator | 6445
@zaachi: v 2.2 by melo byt vytvareni sablon snadnejsi, nez pred tim.
Pro preklady si muzes kdekoliv injectnout ITranslator. Problem jsou ty
linky..
ale neded presenter, myslim, ze by to mohlo zpusobit i dalsi problemy a vsechno
by nemuselo fungovat korektne, pouzij radeji:
class Mailer
{
protected $application;
public function __construct(Nette\Application\Application $application)
{
$this->application = $application;
}
public function send()
{
$presenter = $this->application->getPresenter();
$presenter->createTemplate();
$presenter->link(...);
...
}
}
- zaachi
- Člen | 4
matej21 napsal(a):
@zaachi: v 2.2 by melo byt vytvareni sablon snadnejsi, nez pred tim. Pro preklady si muzes kdekoliv injectnout ITranslator. Problem jsou ty linky..
ale neded presenter, myslim, ze by to mohlo zpusobit i dalsi problemy a vsechno by nemuselo fungovat korektne, pouzij radeji:class Mailer { protected $application; public function __construct(Nette\Application\Application $application) { $this->application = $application; } public function send() { $presenter = $this->application->getPresenter(); $presenter->createTemplate(); $presenter->link(...); ... } }
mohl by jsi prosím ještě přidat způsob, jak naplníš třídu Mailer parametrem $application?
existuje tedy v Nette 2.1 alternativa pro tento kód ze starší verze?
<?php
$presenter = new SomePresenter(Environment::getContext());
$presenter->....
?>
- David Matějka
- Moderator | 6445
Mailer registrujes jako sluzbu v neonu a injectnes kam potrebujes – o predani zavislosti se postara nette
- zaachi
- Člen | 4
matej21 napsal(a):
Mailer registrujes jako sluzbu v neonu a injectnes kam potrebujes – o predani zavislosti se postara nette
Ahoj,
zkusil jsme řešení, které jsi navrhl, ale nefunguje mi zcela
optimálně:
<?php
class Mailer
{
private $application;
private $presenter;
public function __construct(\Nette\Application\Application $application)
{
$this->application = $application;
$this->my_presenter = $this->application->getPresenter();
}
public function registrationWebuserEmail($webuser)
{
$template = $this->my_presenter->createTemplate();
}
}
?>
Vytvoření služby
mailer: App\FrontModule\Tools\Mailer
Init
/** @var \App\FrontModule\Tools\Mailer @inject */
public $mailer;
Užití
$this->mailer->registrationWebuserEmail(array(''));
Dostanu hlášku:
Call to a member function createTemplate() on a non-object
Pokud to napíšu takhle:
<?php
$presenter = $this->application->getPresenter();
$template = $presenter->createTemplate();
?>
Dostanu:
Call to undefined method parent::createTemplate()
Ale například metoda link() funguje správně. Takže to vypadá na problém u templat. Nakonec budu muset asi užít FileTemplate zvlášť.
- Tomáš Votruba
- Moderator | 1114
@akadlec: pro komponenty konstruktor a pro base komponentu factory – napr. takto https://github.com/…mFactory.php
@zaachi: Muzes take „vykrast“ presenter:
$template = clone $this->application->presenter->template;
- Zax
- Člen | 370
@akadlec: Po vášnivé
diskuzi na téma @inject bych řekl, že není úplně od věci si
zapnout inject všude (třeba pomocí CompilerExtension, ať nemusíš všude
psát inject: true
) a pak v base třídách používat metody
inject*, v konkrétních třídách pak konstruktor. Je to praktické
(konstruktor si necháš volný pro potomky) a snad i docela čisté (nemůžu
posoudit, viz můj podpis XD ).
- Tomáš Votruba
- Moderator | 1114
@akadlec: Myslím, že to jde jednoduše. Na form factory jsem tě již odkázal. Všimni si event handleru onCreate (případně přidej vlastní dle potřeby na jiné místo).
Pokud to máš rozjeté a do formuláře chceš něco přidat, např. upravit jeho rendrování ve stylu bootstrap, stačí přidat event.
Je to takto srozumitelné?
Pokud na tvou situaci nelze uplatit, zkus ji sem popsat. Myslím, že existuje lepší řešení, než je mnohonásobná dědičnost.
- akadlec
- Člen | 1326
Takhle, mám normální komponenty (komponenty a formy) a ty dědí od BaseControl aplikace. A v této BaseControl která zase dědí od Nette se vkládá translator a další pomocné služby (gravatary, obrázky, atd.) a tyto služby se aktuálně předávají přes inject metody. Takže kdybych to z inject přesunul do konstruktoru tak mě tam přibude dalších cca 5 předávaných služeb.
- Tomáš Votruba
- Moderator | 1114
Rozumím, stačí použít control místo form. Tedy místo interface si vytvoříš továrničku, ukázka.
- akadlec
- Člen | 1326
Jop vím, jen sem ukázal jak to řeším já ;) dělat ještě vlastní factory classu mě připadá zase jako tuna kódu navíc ne? Navíc mi pak uniká kde se použije ten interface co je právě v tom postu co si linknul ty. Použije se tedy jako klasický interface te factory classy? Takže se pak vytvoří služba z té factory classy která bude řešit závislosti?
- Tomáš Votruba
- Moderator | 1114
Záleží na tom, jestli chceš vytvářet přehledné závislosti (vhodné
když s kódem pracuje více lidí) nebo psát co nejméně (kód vidíš jen
ty sám). Je-li pro tebe důležité to druhé, používej @inject
i v modelových třídách, ušetří-li ti to práci a čas.
Myslím ale, že je lepší závislosti neskrývat (hůře se hledají :)) +
v i presenteru
lze použít konstruktor místo @inject poměrně snadno. Pokud se
rozhodneš pro tuto variantu (ten můj odkaz na jiné téma není nejlepší
řešení, když na to teď koukám), odkážu tě tedy na
příspěvek výše
Je to méně psaní, než dědit komponentu pokaždé, když tam budu chtít
přidat závislost.
Pokud tě to zajímá, vyzkoušej implementaci sám a ptej se konkrétně.
Editoval Tomáš Votruba (9. 6. 2014 15:04)
- akadlec
- Člen | 1326
;) V podstatě ani ničeho velkého. Jednoduše mám komponentu „Avatar“ ta dědí od BaseControl aplikace a ta zase od Nettí control. A co tady řeším je inject vs constructor ;)
Komponenta avatar dostane přes konstruktor služby které vyžaduje (model, image službu atd.) V BaseControl aplikace je zase přidání služeb typu translator, gravatar, images, atd. Některé jsou napřímo přes injectMetody a něco je přes traity.
Samotná komponenta se pak vytvoří pomocí autmatické továrničky z interface. A co řeším je inject VS constructor. Protože pokud budu volat parent::__constrcut() tak si budu muset předávat vše co je postupně nutné, takže když bude komponenta avatar chtít sama o sobě 3 služby tak k nim budu muset přidat dalších cca 5 co jsou přes inject a už pak ten constructor bobtná.
- Tomáš Votruba
- Moderator | 1114
Myslíš, že bys sem mohl hodit kód (gist) všech tříd, jak to řešíš teď?
Editoval Tomáš Votruba (10. 6. 2014 19:17)
- akadlec
- Člen | 1326
Hele můžu ale fakt tam nehledej nic extra než sem v předchozím postu popsal ;)
class Control extends IPub\AppModule\Application\UI\Control
{
/**
* @param User $user
* @param IFilesManager $filesManager
* @param IUserFacade $userFacade
*/
public function __construct(
User $user,
IFilesManager $filesManager,
IUserFacade $userFacade
) {
parent::__construct();
// Owner identity
$this->userEntity = $user->getIdentity();
// File manager
$this->filesManager = $filesManager;
// Module facades
$this->userFacade = $userFacade;
}
}
abstract class Control extends \IPub\Application\UI\Control
{
/**
* Implements autowiring extension
*/
use \Kdyby\Autowired\AutowireComponentFactories;
/**
* Implement secured links for handle
*/
use \Nextras\Application\UI\SecuredLinksPresenterTrait;
}
abstract class Control extends \Nette\Application\UI\Control implements IObservable
{
/**
* @param ITranslator $translator
*/
public function injectTranslator(ITranslator $translator)
{
$this->translator = $translator;
}
}
Tady v ukázce vidíš jen jednu inject metodu, ale mám tam ještě 3 traity kde jsou taky inject metody. A o co mě jdě, jak správně ty komponenty vytvářet a předávat ji služby co požaduje.
Editoval akadlec (10. 6. 2014 20:04)
- Filip Procházka
- Moderator | 4668
Tohle rozhodně nikdy v konstruktoru nedělej
$this->userEntity = $user->getIdentity();
identita se to může v průběhu života aplikace změnit a pak bude komponenta ukazovat blbosti.
- Tomáš Votruba
- Moderator | 1114
@akadlec: Osobně bych snížil počet závislostí. Dříve jsem měl také velké komponenty, které uměly hodně věcí, kde byl inject potřeba. Např. $translator využíváš jak? Šablona a formulář jej můžou získat sami.
Vypadá to, že to máš komplikované, takže inject je na místě.
Co bys mohl (krok na delší cestě), je používat settery v configu.
- akadlec
- Člen | 1326
@Filip Procházka: mno koukám to je tam prehistorický kousek ;) ta komponenta má pak ještě vlastní setter na user account.
@Tomáš Votruba: Takhle ty přímé závislosti co
vyžaduje komponenta se ji předí v konstruktoru, to je bych řek ok, v těch
dalších parentech je pak už jen fakt ten translator, a služby co se
využijí v šabloně, ale zase jen u některých komponent, třeba gravatar
využije jen komponenta avataru či výpisu mailových adres, jiné ne apod.
Jinak translator se využívá jak v šabloně tak v některých částech kdy
se přeloží třeba hlavička confirm dialogu, či vytváření nějakých
elementů formu co je potřeba přeložit přímo než aby to
překládalo nette.
btw jak může šablona dostat translator? Měl jsem za to že ji ho musím předat v createTemplate? Totéž form, pokud mu jej nepředám v setTranslator či nezapnu inject tak jej tam nemám ne?
- Tomáš Votruba
- Moderator | 1114
Používám Kdyby\Translation, které šablony řeší samo. Tedy v presenteru ani komponentě translator nemám, přesto mám vše přeložené.
Do formu je můžeš předat jednou pomocí továrničky, viz výše. Předávat translator komponentě jen proto, abys ho mohl předat formuláři, by bylo zbytečné.
- akadlec
- Člen | 1326
nn do formu to taky dostávám přes továrničku. Kdyby\Translation mám taky a bez tohoto se mě šablony nepřekladaji:
protected function createTemplate($class = NULL)
{
$template = parent::createTemplate($class);
$template->registerHelperLoader(callback($this->translator->createTemplateHelpers(), 'loader'));
return $template;
}
- Tomáš Votruba
- Moderator | 1114
Přechod doporučuju :) Kdybys chtěl, budu na pixdevday a můžem na to mrknout.
- akadlec
- Člen | 1326
Takže zkouším přechod na 2.2 a zatím zjištuji že ty traity co sem měl pro extension aby dostaly do templaty své služby atd již nebudou potřeba, pokud jsem to pochopil správně? Přes latte makra a helpery šablony které si initnu v DI se ke všemu dostanu, je to správný postup? Udělal jsem si třeba helper getGravatarService() co mi vrátí službu gravataru a pak v makru {gravatar} k němu přistoupím přes:
$template->getGravatarService()
je to správný postup? Takto se zbavím docela dost inject metod.
- Filip Procházka
- Moderator | 4668
Pokud bude mít @David Grudl, nebo kdokoliv jiný, lepší nápad, rád to přepíšu.