Připojení k další mysql databázi bez nastavení v config

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

Zdravím všechny a doufám, že jsem hledal dobře a podobný dotaz tu nebyl.

Mám následující problém: projekt, pro který dělám administraci, obsahuje mnoho dat a tak jsou určitá data po čase kopírována do jiných databází, které nejsou tak často zálohované. Jelikož se jedná o konečná data, která se již nemění, může tam být záloha třeba jednou za den (dle CRONu, který to přetahuje) namísto záloh hodinových či častějších. S tímto nic neudělám, není v mé pravomoci to měnit.
Pak mám v hlavní databázi v tabulce řekněme orders sloupec dbname, který obsahuje přesný název db, na kterou se mám dotázat pro získání položek objednávek.
V hlavní databázi je tabulka orders_items a má naprosto stejnou strukturu, jako stejně nazvaná tabulka v db data_2012_project_cz.

To, co potřebuji, je připojení k různým předem neznámým (pro nastavení v config.neon) databázím. Jelikož databází může přibývat (nemusí být jen po letech, mohou být po měsících, speciálních projektech apod), nevidím reálné vytvoření všech možných připojení v configu a navíc bych je měl předávat v parametrech při vytváření služby, ale jak vím, které budu potřebovat?

Z toho důvodu jsem udělal následující:

  1. vytvořil jsem parametry v config.local.neon se vším, jak se připujuji v default připojení
  2. tyto parametry používám při default připojení a zároveň je předávám jako parametr při vytváření mé služby, která má přistupovat k různým databázím, ale vždy stejné tabulce
  3. při hledání položek objednávek pak použiji dbname a pokud není prázdné (nachází se to v jiné databázi), vytvářím nový Database\Context
  4. vytvořenému Context předávám Database\Connection vytvořené z údajů, které jsem si z configu získal skrze parametr, avšak jako dbname logicky použiji to, co bylo uložené v orders
  5. jako Database\Structure předávám Structure základní database pomocí $this->database->getStructure()

Výše uvedený postup mi funguje, teda co jsem zatím měl možnost odzkoušet. Nejsem si však jistý, zda je správný a zda nemá minimálně bezpečnostní riziko – uživatel a heslo se předává v parametru volání služby a je v dané třídě dostupné, ačkoliv je v private proměnné.
Výhodu však vidím v tom, že mohu používat stejné metody se stejnými dotazy (používám objektový zápis ->table('')->where('', '')->fetch...), jen je nebudu volat vždy nad $this->database (neboli výchozí databází), ale nad funkcí, které předám hodnotu z dbname jiné tabulky a ta rozhodne, zda mi vrátí default context, nebo nově vytvořený.

Vidí v mém postupu někdo zásadní problém? Udělal by to někdo lépe či jinak? Raději ještě znovu opakuji, že se seznam databází mění (přibývají) a není možné je udržovat v configu a že navrhnutou strukturu nemohu měnit.

Popř. doporučovali byste tvořit dotazy raději ručně s tím, že bych měl dbname rovnou v selectu? Pokud by to bylo lepší, nebránil bych se tomu, ale raději bych stále používal objektový přístup k databázi spolu s výhodami, které Context přináší.

Děkuji předem za jakékoliv komentáře.

premek_k
Člen | 172
+
0
-

Pro tyto účely jsem používal views v aktuální db čerpající data z těch záložních db. Propojení a granty byly tedy na úrovni db stroje a z aplikace stačil konekt do jedné databáze. Ale bylo to na Oracle & MsSql & Informix strojích, zda je to reálné na MySql (resp. tvé db), to jsem nezjišťoval.

Altimit
Člen | 82
+
+1
-

co vím tak připojení k databázím se nastavuje v configu
a také se to doporučuje.

PVD
Člen | 20
+
0
-

premek_k napsal(a):

Pro tyto účely jsem používal views v aktuální db čerpající data z těch záložních db. Propojení a granty byly tedy na úrovni db stroje a z aplikace stačil konekt do jedné databáze. Ale bylo to na Oracle & MsSql & Informix strojích, zda je to reálné na MySql (resp. tvé db), to jsem nezjišťoval.

Vzhledem k různosti dat a tomu, že uživatel může být jak nový s pár objednávkami, tak i starý, který má objednávky rozesety po více databázích, nevidím ve View reálný přínos. Každopádně díky!

Altimit napsal(a):

co vím tak připojení k databázím se nastavuje v configu
a také se to doporučuje.

Díky. Jak jsem psal, do Configu bych to dal rád, ale jak to mám udělat, když neznám předem které databáze budu potřebovat a jejich počet v průběhu času přibývá? Je jich v řádu desítek.

V principu mi jde o dvě věci

  1. vadí něčemu mnou vytvořený postup, zejména s ohledem na bezpečnost aplikace?
  2. existuje elegantnější řešení, které by odpovídalo správným postupům práce s databázemi v Nette?
Altimit
Člen | 82
+
0
-

Při volání v prezentru tak nahraď volání z configu na vlastní připojení
NEDOPORUČUJE SE TO!! z důvodu, že by se to dalo prolomit!
proto se doporučuje to připojit přes config jelikož tam se nastavuje i context..

premek_k
Člen | 172
+
0
-

Dalo by se to řešit i pomocí mysql direktivy use, ale jen za předpokladu, že má jeden uživatel přístup do všech Db. Mi to např. zatrhl hosting, který pro každou Db vyžadoval jiného usera. A taky to není „zas tak pěkné“ řešení…

Editoval premek_k (11. 2. 2016 7:37)

PVD
Člen | 20
+
0
-

@Altimit Nedoporučuje se co konkrétně? Co by se dalo prolomit? Já si vytvářím nový context, možná bude lepší ukázkou kus kódu:

Config.neon

parameters:
  database:
    driver: 'mysql'
    host: 'abc.def.cz'
    dbname: 'default_db'
    user: 'xxx'
    password: '***'

database:
  default:
    dsn: '%database.driver%:host=%database.host%;dbname=%database.dbname%'
    user: '%database.user%'
    password: '%database.password%'
    options: [PDO::MYSQL_ATTR_COMPRESS = true]
    debugger: true
    explain: true

services:
	orders: App\Model\OrdersManager(%database%)

OrdersManager

<?php
class OrdersManager extends \Nette\Object
{
	...
	/** @var Context */
	private $database;
	/** @var Connection */
	private $database2;

	private $dbParams;
	...
	public function __construct($dbParams, Context $database)
	{
		$this->database = $database;
		$this->dbParams = $dbParams;
	}

	public function getOrders()
	{
		$this->database2 = new Connection($this->dbParams['driver'].':host='.$this->dbParams['host'].';dbname=JINÁ_DATABÁZE', $this->dbParams['user'], $this->dbParams['password']);

		$context2 = new Context($this->database2, $this->database->getStructure());
		$orderRows = $context2->table(self::TABLE_NAME)->where(...)->...;
	}
}
?>

Výše uvedený kód mi funguje, dostanu se do požadované databáze, jejíž název jsem neznal v době tvoření Configu a hlavně – vytvoří se připojení jen k těm databázím, které reálně potřebuji, ne ke všem (několik desítek). Navíc při definování OrdersManager v configu nemusím dopředu rozhodnout, jaké databáze „dostane“, protože bych ani nemohl, jelikož může potřebovat jít do jakékoliv.

Když takto tvořím nový Context, tak stále marně přemýšlím, zda to je správný či špatný postup. Co vidím jako riziko je to předávání parametrů na připojení v konstruktoru a uchovávání v proměnné. Ale nevím…

@premek_k To zní hodně dobře, mám za to, že by měl být přístup všude (na testovacím prostředí nemám pochopitelně všechny db), pak by stačilo použít jen tuto direktivu a byl bych na jiné db. Rozhodně vyzkouším. Díky!

Edit:
Vyzkoušel jsem USE a zdá se, že je to to, co jsem hledal. Sice to má stále své úskalí v tom, že mám tabulku výpisu Orders a v té tabulce zobrazuji věci, co vypočítávám z tabulek, které jsou v těch různých databázích, ale v takových případech budu holt měnit databázi pomocí USE dle skupin objednávek, ve kterých db se nachází.
@premek_k ještě jednou moc díky, pokud nepřijde nikdo s lepším řešením, asi zůstanu u tohoto. Pochopitelně ověřím stejnost uživatele a podporu na hostingu, ale jelikož má projekt vlastní server se správcem, nebude to snad problém. ;)

Edit2:
Pochopitelně existuje řešení, že budu psát ruční SELECTy, kde bude dbname jako prefix pro tabulku (FROM data_2012.orders_items), asi je to v principu také v pořádku, ale chtěl jsem to řešit pomocí objektového přístupu…

Editoval PVD (11. 2. 2016 9:06)