Vytvaranie instancii – najlepsi sposob?
- tomasz_svk
- Člen | 5
Dobry den,
majme priklad: model „User“, ktory je zaregistrovany ako service. Ako
service je z dovodu, ze sa do neho musi injectovat iny service (napr.
databaza)
class BaseModel extends \Nette\Object{
public function __construct(Container $c, Database $db){...}
}
class UserModel extends BaseModel{ ... }
a potrebujem vyriesit nasledujucu situaciu … UserModel
sa musi
pouzivat na udrziavanie stavovych informacii aktualne prihlaseneho
pouzivatela – to je ok, cez AutoWiring a nie je problem k nemu pristupovat
cez getService('user');
Nasledne potrebujem instanciovat tento model pre ineho usera (napr na
vykonanie update). Nemozem pouzit $this->getService('user')
,
nakolko tam je instanciovany aktualny pouzivatel. A chcem sa vyhnut
new User($this->container, $this-getService('database'))
.
Je dobrym riesenim pouzit
$this->context->createInstance('App\Model\UserModel');
alebo
radsej new User(...);
?
Dufam, ze sa neplanuje vyradit $this->context
z
Nette\Application\UI\Presenter
, pripadne
$context->createInstance
, tak ako sa vyradilo FileTemplate (a
musel som fixovat napr. Eciovni na faktury)
Ako to riesite vy?
Dakujem.
Editoval tomasz_svk (16. 5. 2015 21:39)
- F.Vesely
- Člen | 369
Podivej se do dokumentace na Ziskavani zavislosti na injectovani sluzeb do presenteru. Rozhodne nepouzivej context, je to neciste, primo prasacke.
- tomasz_svk
- Člen | 5
F.Vesely napsal(a):
Podivej se do dokumentace na Ziskavani zavislosti na injectovani sluzeb do presenteru. Rozhodne nepouzivej context, je to neciste, primo prasacke.
Ano, ale to nie je odpoved na moju otazku, pre mna je primarne instanciovanie tz:
user1 = $this->context->createInstance('App\Model\UserModel');
user2 = $this->context->createInstance('App\Model\UserModel');
vs.
user1 = new App\Model\UserModel(...);
user2 = new App\Model\UserModel(...);
pride mi to zbytocne injectovat, nakolko k pouzitiu user1
a
user2
vobec nemusi prist.
- tomasz_svk
- Člen | 5
UserModel je iba priklad, pretoze ma nenapadla jednoducha situacia, na ktorej to vysvetlit. Berte to tak, ze:
- nema to nic spolocne s „userom“
- robi to cokolvek, udrzuje napriklad stavove premenne
Ale teraz ma napadol mozno lepsi priklad:
Mail wrapper:
class MailWrapper extends \Nette\Mail\Message{ function __construct(Template $template, Mailer $mailer){...}; function send(); ... }
/** @var MailWrapper @inject */
public $mail;
...
foreach($users as $user){
$this->mail->addTo($user->getMail());
//Var. do tpl, vola sa n-krat s inym 'name', napriklad podla uctu
$this->mail->setVariable('name',random());
$this->mail->send();
}
Tu nastava ten problem, ze \Nette\Mail\Message::addTo
appenduje
prijemcov. Aby bolo mozne poslat dalsiemu userovi mail, je potreba prave
povodneho prijemcu vymazat. Pocitajme, ze se setVariable
sa vola
v ramci jedneho mailu vela krat a vzdy sa nastavuje ina premenna (nemusi sa 2×
ta ista, a v opakovanom cykle by bola neziaduca)
Riesenia su v podstate 4:
- implementovat clean() a v kazdom cykle zavolat
- instanciovat nanovo s parametrami
- instanciovat servicu cez context
- naklonovat prazdnu servicu na zaciatku cyklu a pracovat s tym a nasledne ju uvolnit (rad by som sa vyhol)
Editoval tomasz_svk (17. 5. 2015 2:28)
- jiri.pudil
- Nette Blogger | 1034
Čisté řešení je v podstatě jen jedno: napsat si na to
MailFactory
, která ti vrátí vždy novou instanci, a injectovat
si tu továrnu.
- tomasz_svk
- Člen | 5
jiri.pudil napsal(a):
Čisté řešení je v podstatě jen jedno: napsat si na to
MailFactory
, která ti vrátí vždy novou instanci, a injectovat si tu továrnu.
Ano, nakoniec som to vyriesil bez nejakeho robustneho prepisovania kodu:
public function __construct(...){
$this->constructorParams = func_get_args();
}
public function newInstance(){
$rf = $this->getReflection();
return $rf->newInstanceArgs($this->constructorParams);
}
Editoval tomasz_svk (17. 5. 2015 13:19)
- jiri.pudil
- Nette Blogger | 1034
Ano, nakoniec som to vyriesil bez nejakeho robustneho prepisovania kodu:
Tak se nad nějakým robustním přepsáním kódu zamysli, protože tohle je prasárna, a navíc relativně pomalá (protože reflexe v runtime) :)
- newPOPE
- Člen | 648
@tomasz_svk mam pocit, ze velmi spekulujes.
Tu je nieco co potrebujes factory method pattern. Nastuduj a pouzi :)
A s tym mailom by to mozno slo urobit nejak takto:
foreach($users as $user) {
$mail = clone $this->mail;
$mail->addTo($user->getMail());
$mail->send(...);
}
- bazo
- Člen | 620
h4kuna napsal(a):
newPOPE napsal(a):
> foreach($users as $user) { > $mail = clone $this->mail; > $mail->addTo($user->getMail()); > $mail->send(...); > }
Typuji že toto si bude pamatovat předchozí odesílatele. Tj v prvním cyklu to pošleš na xx@yy.tld, v druhém xx@yy.tld, zz@yy.tld atd…
ved preto tam ma ten clone…
- Jan Suchánek
- Člen | 404
@bazo: Furt nechápu jakej je rozdíl místo clone mít radši factory, která ten email vyrobí, případně i dostane další závislosti? Proč zrovna clone, k čemu?
// inject služby, případně předání závislosti
foreach($users as $user) {
$mail = $this->emailFactory->create();
$email = $user->getEmail();
$mail->addTo($email);
$mail->send(...);
}
nebo jen ?
foreach($users as $user) {
$email = $user->getEmail();
$this->emailService->send($email);
}
Editoval jenicek (18. 5. 2015 22:06)
- newPOPE
- Člen | 648
@jenicek okej, clone
je tam preto aby sa neappendovali
prijimatelia. A na druhej strane je tam preto lebo to uz je setupnuty mail
(sablona, premenne a pod). A ked premyslam nad premennymi tak pokial je to
personalizovana vec tak budes v tom maili potrebovat meno napr takze by som to
napisal nejak takto:
foreach($users as $user) {
$this->emailService->send($user); // ma entitu usera tu si tam spracuje a posle email.
}
- tomasz_svk
- Člen | 5
@newPOPE clone nakoniec nebolo dobre riesenie, kvoli referenciam na objekty (shallow copy). Nastastie islo iba o 2 pripady, ktore sa pouzivaju iba obcas … takze reflection postacilo :)