Jak získat NE-autowired připojení k druhé databázi
- ludek
- Člen | 83
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
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
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 chybu
„Service of type Nette\Database\Context needed by $db1 in App\Presenters\FooPresenter::__construct() not found.
“
- h4kuna
- Backer | 740
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
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.
- ludek
- Člen | 83
Už mi to došlo.
- autowiring se skutečně vypnout musí, jinak se do obou proměnných dostane to samé (první spojení)
- 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.
- ludek
- Člen | 83
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
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
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");
...
}