Spristupnenie databazy modelom pomocou dependency injection (DI)

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

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

Ř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
+
0
-

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
+
0
-

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 :)