Připojení k další mysql databázi bez nastavení v config
- PVD
- Člen | 20
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í:
- vytvořil jsem parametry v
config.local.neon
se vším, jak se připujuji vdefault
připojení - 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
- 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 - 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
- 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
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.
- PVD
- Člen | 20
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
- vadí něčemu mnou vytvořený postup, zejména s ohledem na bezpečnost aplikace?
- existuje elegantnější řešení, které by odpovídalo správným postupům práce s databázemi v Nette?
- premek_k
- Člen | 172
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
@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)