Spristupnenie databazy modelom pomocou dependency injection (DI)
- buksy
- Člen | 22
Vopred sa ospravedlnujem za otazku, lebo uz bola parkrat polozena a je tu aj navod, napriek tomu mam v tom zmatok (vela novych informacii) a neviem ako mam nakonfigurovat svoj model tak aby pri vytvarani mal rovno pristup k DB.
Mam triedu User ktora reprezentuje uzivatelov, v jednom presenteri mozem naraz pracovat s viacerymi instanciami tejto triedy, preto ju neregistrujem ako sluzbu (je to spravne?).
<?php
class User extends Nette\Object
{
protected $id;
protected $username;
public function __construct(Nette\Database\Connection $database)
{
$this->db = $database;
}
public function Load($id)
{
// ...
}
// ...
}
?>
Ako prosim docielim toho, aby som nemusel objektu User predavat odkaz na databazu pri vytvoreni v presenteri?
<?php
// SomePresenter
public function renderDefault()
{
$user1 = new User();
$user1->Load(1);
$user2 = new User();
$user2->Load(2);
}
?>
Moj config
common:
parameters:
php:
date.timezone: Europe/Prague
# zlib.output_compression: yes
nette:
application:
errorPresenter: Error
database:
dsn: 'mysql:unix_socket=/tmp/mysql51.sock;dbname=db'
user: user
password: pass
charset: utf8
session:
expiration: 14 days
services:
authenticator: MyAuthenticator(@nette.database.default)
routerFactory: RouterFactory
router: @routerFactory::createRouter
factories:
production < common:
development < common:
- David Matějka
- Moderator | 6445
1. tridu User si registruj jako sluzbu:
services:
userModel: SomeNamespace\User
2. nejak si dostan tu tridu do presenteru; bud pres context (radsi
ne:)), nebo pres contructor injection, nebo pres inject metodu, nebo pres
inject anotaci (v dev verzi), nebo pres autowire anotaci (pri pouziti kdyby/autowired)
nejjednodussi pro tebe bude ta inject metoda (pokud nemas dev verzi, kde je
jednodussi @inject), pr.:
class FooPresenter extends BasePresenter
{
protected $userModel;
public function injectUserModel(SomeNamespace\User $userModel)
{
$this->userModel = $userModel;
}
}
3. profit
EDIT:
az ted jsem si to precet poradne – pokud chces pracovat s vice instancemi,
tak si udelej treba UserManager, ktery se ti bude starat o nacitani a
instancovani toho User a metodu load() z useru vyhod :) – tim padem
neregitruj to User jako sluzbu, ale to UserManager
Editoval matej21 (12. 9. 2013 19:06)
- buksy
- Člen | 22
Ten UserManager je zaujimavy napad, mohol by mat metodu
getUser($id) ktora by vracala uz rovno nacitanu
(nainicializovanu s informaciami z db) instanciu objektu User
:).
Ale rozmyslam ze ked budem chciet spravit nieco taketo
$user->isFriendWith($user2); zas budem potrebovat odkaz na
db (berme to tak ze nie je mozne nacitat naraz z db vsetky
informacie o uzivatelovy (teda ani jeho priatelov)). Takze by som mohol rovno
v UserManager::getUser vracat objekt s odkazom na
db . Lenze budem mat este aj ine modely ktore budu potrebovat
pracovat s db a tiez budem pouzivat viac instancii. Preto
neviem, ci ak sa vydam touto cestou, neskoncim s tym ze moj UserManager bude
vlastne novy DI.
PS: Este jednu vec by som sa chcel spytat, ak by som nepozival DI vobec, ako v presenteri ziskam odkaz na db pri mojom konfigu aby som ho predaval do konstruktora?
- Kenji179
- Člen | 4
matej21 napsal(a):
nejak si dostan tu tridu do presenteru;
bud pres context (radsi ne:)), nebo pres contructor injection, nebo pres inject metodu, nebo pres inject anotaci (v dev verzi), nebo pres autowire anotaci
Mohl bych se zeptat, proč ne přes context? V čem jsou ty jiné metody lepší nebo horší?
- Lukaj
- Člen | 1
Řešil bych to pomocí factory. Vytvořil bych si něco jako
UserFactory
, kterou bych zaregistroval jako službu a ta by
vytvářela a nastavovala jednotlivé objekty User
.
class User
{
private $database;
public function __construct ($id, Nette\Database\Connection $database) {
$this->database = $database;
// load user with $id
}
}
class UserFactory
{
private $database;
public function __construct(Nette\Database\Connection $database) {
$this->database = $database;
}
public function create($id) {
return new User($id, $this->database);
}
}
V konfigu zaregistrovat UserFactory
jako službu a vstříknout
do presenteru.
class SomePresenter extends BasePresenter
{
private $userFactory;
public function injectUserFactory(UserFactory $userFactory) {
$this->userFactory = $userFactory;
}
public function actionDefault() {
$id = ...;
$someUser = $this->userFactory->create($id);
}
Popřípadě by šlo nechat factory vygenerovat automaticky.
Editoval Lukaj (12. 9. 2013 20:43)
- jiri.pudil
- Nette Blogger | 1032
Kenji179 napsal(a):
Mohl bych se zeptat, proč ne přes context? V čem jsou ty jiné metody lepší nebo horší?
Jsou transparentnější. Třída jasně deklaruje svoje závislosti, namísto toho, aby si je tahala „magicky“ z contextu. Proč je to dobré, se můžeš dočíst třeba rovnou v dokumentaci.
- Kenji179
- Člen | 4
jiri.pudil napsal(a):
Jsou transparentnější. Třída jasně deklaruje svoje závislosti, namísto toho, aby si je tahala „magicky“ z contextu. Proč je to dobré, se můžeš dočíst třeba rovnou v dokumentaci.
Díky za info :)