Jak získat NE-autowired připojení k druhé databázi

ludek
Člen | 83
+
0
-

Zdravím, potřebuju se ve stejném presenteru připojit ke dvěma databázím.
Původní stav: config/local.neon

database:
   dsn: 'mysql:host=127.0.0.1;dbname=prvni'
   user: '...'
   password: '...'

Presenter:

/** @var Nette\Database\Context */
protected $database1;

public function injectDatabase1(Nette\Database\Context $db1) {
    $this->database1 = $db1;
}

Nic víc. Výsledek je, že v $this->database1 je připojení, můžu napsat $this->database1->table(... Prostě jako v quickstartu .

Teď potřebuju sahat ve stejném presenteru do dvou databází. V local.neon jsem pojmenoval první připojení a připsal druhé:

database:
   db1:
       dsn: 'mysql:host=127.0.0.1;dbname=prvni'
       user: '...'
       password: '...'

   db2:
       dsn: 'mysql:host=127.0.0.1;dbname=druha'
       user: '...'
       password: '...'

V DI kontejneru na Tracy panelu vidím:

  • database.db1.connection - Autowired: yes
  • database.db2.connection - Autowired: no

A teď prosím radu: jak v presenteru, kde už mám jedno připojení, dostat i to druhé

/** @var Nette\Database\Context */
protected $database1;

/** @var Nette\Database\Context */
protected $database2;

public function injectDatabase1(Nette\Database\Context $db1) {
    $this->database1 = $db1;
}

	???

Abych mohl používat $this->database1->table(... i $this->database2->table(...

h4kuna
Backer | 740
+
0
-

Tohle chytře úplně nejde. Tu inject metodu smaž a udělej to přes kontruktor nebo setter.

namespace App;

class FooPresenter // extends ...
{
	/** @var Nette\Database\Context */
	protected $database1;

	/** @var Nette\Database\Context */
	protected $database2;

	public function __construct(Nette\Database\Context $db1, Nette\Database\Context $db2) {
    	$this->database1 = $db1;
    	$this->database2 = $db2;
		dumpe($db1, $db2); // done
	}
}

Zapiš si Presenter do neonu.

services:
  - App\FooPresenter(@database.db1.context, @databse.db2.context)

Nicméně není úplně optimální mít spojení v presenteru. Presenter není určený, aby jsi v něm dělal byznys logiku. Na to jsou fasády a repository. Zbavíš se těch protected vlastností.

Editoval h4kuna (2. 8. 2019 14:45)

ludek
Člen | 83
+
0
-

h4kuna napsal(a):
Tohle chytře úplně nejde. Tu inject metodu smaž a udělej to přes konstruktor nebo setter.

Díky. Takto to nefunguje. Obsah obou proměnných dump($db1, $db2); je úplně stejný. Asi pořád autowiring?

Když autowiring vypnu

database:
   db1:
       dsn: 'mysql:host=127.0.0.1;dbname=prvni'
       user: '...'
       password: '...'
	   autowired: false

   db2:
       dsn: 'mysql:host=127.0.0.1;dbname=druha'
       user: '...'
       password: '...'
       autowired: false

dostanu chybuService of type Nette\Database\Context needed by $db1 in App\Presenters\FooPresenter::__construct() not found.

h4kuna
Backer | 740
+
0
-

A zapsal jsi ten presenter do neonu jak jsem psal? Protože kdyby jsi to udělal, tak to spadne na jiné chybě než co jsi postnul.

opravil jsem chybu těch služeb v neonu.

services:
  - App\Presenters\FooPresenter(@database.db1.context, @databse.db2.context)

Editoval h4kuna (2. 8. 2019 17:58)

ludek
Člen | 83
+
0
-

h4kuna napsal(a):
A zapsal jsi ten presenter do neonu jak jsem psal?

Myslím, že jsem zapsal dobře.
Ale dokud nevypnu autowiring nemá to vůbec vliv. Program jede i když do neonu nedám nic a i když napíšu Vaše řešení. V obou případech se do obou proměnných dostane první připojení.

Potom vypnu autowiring, v neonu nechám service, jak jste navrhoval a dostanu tu chybu se kterou si bohužel nevím rady.

h4kuna
Backer | 740
+
0
-

To je prostě divný :).

Ukaž celý a všechny neon soubory.

Editoval h4kuna (2. 8. 2019 18:01)

ludek
Člen | 83
+
0
-

Už mi to došlo.

  1. autowiring se skutečně vypnout musí, jinak se do obou proměnných dostane to samé (první spojení)
  2. ale pak musím napsat do configu services: záznam pro presenter kde se víc připojení vytváří, ale i pro každý další, který od něj dědí a i pro jiné, které spojení potřebují, protože samo se jim to nepředá (když jsem to vypnul). Třeba:
- App\Presenters\BasePresenter (@database.db1.context, @database.db2.context)
# dědí od BasePresenter a volá $this->database1 a $this->database2
- App\Presenters\AdminPresenter (@database.db1.context, @database.db2.context)
# potřebuje první databázi:
- App\Model\Authenticator (@database.db1.context)

Tu chybu u mě vyhazoval podřízený presenter. Nebral jsem to na vědomí, protože jsem si myslel, že stačí když se to správně přiřadí nad ním (v BasePresenteru). Škoda, že to tak nejde.

Chápu, že může být trochu sporné používat to takovýmto způsobem (byznys logika v presenteru), ale pro moje účely to je vyhovující a teď už to funguje.

Díky za pomoc.

h4kuna
Backer | 740
+
+1
-

Na tohle přidávání do potomků třídy slouží v nette decorator

MajklNajt
Člen | 493
+
+2
-

@ludek čo tak vytvoriť si accessor, ktorý si budeš normálne predávať ako závislosť a v configu si do accesoru predáš tieto 2 spojenia?

ludek
Člen | 83
+
0
-

MajklNajt napsal(a):
@ludek čo tak vytvoriť si accessor, ktorý si budeš normálne predávať ako závislosť a v configu si do accesoru predáš tieto 2 spojenia?

To je asi mimo mou představivost.

Původně jsem myslel, že si obě spojení vytvořím v nějakém nadřízeném presenteru a všechny ostatní presentery, které od něj dědí, je budou mít k dispozici. Překvapilo mě, že to tak nejde – zřejmě úplně nechápu jak to funguje.

Accesorem myslíte nejakou modelovou třídu?

h4kuna
Backer | 740
+
+4
-

Aby jsi všude nemusel v neonu vyplňovat spojení ručně. Tak můžeš použít decorator a nebo Accessor.
Ukázka Accessoru pro nette 2.4

use Nette;

class Connections
{

	/** @var Nette\Di\Container */
	private $container;


	public function __construct(Nette\Di\Container $container)
	{
		$this->container = $container;
	}


	public function getDb1(): Nette\Database\Context
	{
		return $this->container->getService('database.db1.context');
	}


	public function getDb2(): Nette\Database\Context
	{
		return $this->container->getService('database.db2.context');
	}

}

Všude si pak budeš vytvářet závislost na Connecstions

Edit:
Dopsal jsem že toto řešení je pro nette 2.4

Editoval h4kuna (5. 8. 2019 9:35)

MajklNajt
Člen | 493
+
+3
-

ako píše h4kuna, prípadne môžeš využiť multi accessor generovaný priamo nette:

vytvoríš si iba interface

interface DatabaseAccessor
{
    function get($name): Nette\Database\Context
}

v configurácií predáš DB:

services:
    - DatabaseAccessor(
        db1: @database.db1.context
        db2: @database.db2.context
    )

do presenteru si potom injectuješ DatabaseAccessor:

/** @var DatabaseAccessor @inject */
public $databaseAccessor;

public function processDataOnDb1()
{
	$db = $this->databaseAccessor->get("db1");
	...
}

public function processDataOnDb2()
{
	$db = $this->databaseAccessor->get("db2");
	...
}
ludek
Člen | 83
+
0
-

To vypadá velmi zajímavě, určitě vyzkouším. Díky.