Článek v rubrikách:
DbDibiOrm - DibiConnection - staticky, instančně, přistupovat přímo k dibi factory?
Jeden díl ze série článků o stavbě ORM postaveném nad dibi. Jak pracovat s konekcí k databázi? Jak DibiConnection získávat a kde ho ukládat?
Tuto otázku řešíte i mimo ORM. Máte sadu modelů s metodami, které přistupují k databázi, tedy využívají DibiConnection.
Jak ale k samotné instanci budete přistupovat?
- class BaseModel {
- public static function find($id) {
- $db=získej !! ve static kontextu !! proměnnou DibiConnection
- $db->select(.....);
- }
- public function insert($data) {
- $db->insert(.....);
- }
- }
Budete tedy volat globální továrnu přímo v metodách insert a find? Tedy dibi::getConnection()? Nebo budete volat static::getDbConnection() (jak to dělá většina ORM frameworků)?
K čemu je vlastně mít vlastní instanci připojení k databázi v konkrétním modelu?
Jediné co mne napadá: Abychom se mohli připojovat k více databázím a tedy využívali stejných objektů? Tedy všechny zůčastněné databáze budou muset mít stejnou strukturu, prostě budou totožné? A ještě proměnnou musíme měnit v globálním statickém kontextu. Tedy, abychom s modelem Customer mohli pracovat v jiné databázi musíme podědit, a vytvořit Db1Customer extends BaseModel, Db2Customer extends BaseModel?
Jak to tedy je? Je to k něčemu použitelné?
U Ormionu mi ještě nesedí, že je to naopak svázané s třídou Ormion, tzn. stejně to nelze změnit a je připojení pevně dané, je to tedy stejné jako volat globální proměnnou napřímo.
- class Ormion
- ...
- protected static function getDb() {
- if (!self::$db) {
- self::$db = new DibiConnection(Environment::getConfig("database"));
- }
- return self::$db;
- }
Zajímavé zdroje (Dependency Injection a Singleton je zlo)
Je reálné zaměnit instanci připojení k databázi?
Zajímalo, by mne co si myslíte o tom, jestli Dependency Injection má v tomto případě smysl? Považte, že celé ORM je závislé na dibi, využívá DibiFluent a tedy nemá smysl zaměňovat za jiný layer. ORM je stavěné na dibi! Pokud chceme například pro účely testování zaměnit databázi pro zápis, můžeme tak učinit v globální továrně dibi, případně před provedením konkrétního požadavku mohu na určitou část kódu zaměnit výchozí, tedy poslední použité připojení k databázi.
Ale má to smysl dělat to u konkrétní instance objektu? Představte si tento příklad:
- $customer=new Customer();
- $customer->setDbConnection(dibi::getConnection('anotherDatabase'));
- $customer->email="test@email.cz";
- $cusotmer->save();
Co když Customer před uložením kontroluje jeslti se jednoznačný identifikátor (email) nenachází v tabulce reader? Pokud ano, vyhodí upozornění, zda nechceme účty spojit? Zda se nejedná o stejného člověka? Nebo naopak nedovolí vůbec uložit zákazníka, který existuje jako čtenář. Pouze hypotéza! Kde budeme čtenáře hledat? V anotherDatabase nebo defaultDatabase? To dáme objektu Reader také vědět že by měl změnit connection? Je tedy vůbec logické měnit connection jednotlivé instanci? A další dotaz je vůbec logické měnit modelu připojení databázi jinak než globálně?
podobné články
| 09.12.2009 | DbDibiOrm | (50%) |
komentáře
RSS Komentáře
problém s instancí bude, že stěží lze použít např. ve statické metodě Customer::find(1) jak si tady šáhnu na instanci (resp. každá statická metoda co vrací sadu objektů si musí nejprve jeden vytvořit, aby získala připojení k databázi)? to mne nutí ke statické vlastnosti
- prosím určitě to neber jako kritiku, to jakým způsobem bude tahle funkce napsána je dost zásadní, proto jestli má smysl funkce typu static function find(), protože pokud je nutné přistupovat k db instančně tak tyhle funkce nemají smysl ve statickém kontextu
- no to je zhruba to co píšu v článku, tedy mám nějakou abstraktní třídu Customer? a tu podědím do Db1Customer a Db2Customer a každý znich bude pracovat s jiným connection? je to tak, to je jediná možnost využití? Chtěl sem vědět jen jestli to lze nějak reálně použít a jestli tahle funkce má význam protože, jak jsem psal, Customer může někde uvnitř spolupracovat s Productem a ten už zase neví se kterým connection pracovat?
tedy existuje příklad využítí takového Dependency Injection?
můj názor je, že v případě, že je to konstruováno tak, aby šlo využít DI, tak si odpálím větev pod nohama, aniž bych věděl jestli mám kam skočit
A není Repository to co říkám? CustomerTable, ProductTable? To je přece vzor Repository ne?
Ono ale podle mne i model řádku v tabulce, tedy Product1 (name=šroubovák), nebo Product2 (name=vrtačka), by měl snad umět pracovat s databází ne?
Co třeba potřeba
1. ukaž produkty které si někdo zakoupil společně
tak asi zavolám Product1->getProductsWhoBuyAlso()
samozřejmě bych to mohl předat nějaké Repository (ProductRepository extends Repository) a říci
ProductRepository::getProductsWhoBuyAlsoWith(Product1->id)
ale není to krkolomné? no možná je to v pořádku, pak tedy model je jen hloupá cache, instance databázového řádku a všechny akce nad ním vykonávám prostřednictvím nějaké černé krabičky
případně můžeme implementovat metodu
Product1->getProductsWhoBuyAlso() { return ProductRepository::getProductsWhoBuyAlsoWith(this->id) }
Ano, je to „jen“ cache, ale určitě ne hloupá a dělá jen jednu věc, přesně podle zásad OOP. V tom ASP.NET když použijete Ling to SQL, což je podle mě nejpoužívanější, tak se vygeneruje třída, která odpovídá řádku tabulky (což je ten váš model), pak třída, která tahá věci z tabulky do toho modelu a pak třída repository, kterou jedinou voláte a která vrací právě jen holý model. Má to několik výhod, není to závislé na databázi (myslím tím, že můžete vyměnit SQL 2008 za MySQL a přepíšete jen třídu, která tahá věci z db), je to perfektně zapouzdřené a můžete použít kolik chcete db připojení – pokud ovšem repository třída nebude singleton – a neovlivní to váš model. Všechno měníte na jednom místě. Předávat nějaké metodě instanci připojení podlě mě stojí čas, místo a nemá to tam co dělat.
- Model=řádek tabulky to je jasné, tam není co vymýšlet, ať už bude umět cokoli nebo odkazovat na cokoli co to dělá a vrací přes tento model výsledek (to máme tedy náš Customer, Product, User, Order, Invoice)
- akorát dál mi není jasná ta podobnost mezi třídou co tahá věci z tabulky a repository, přijde mi, že celkem úspěšně tohle můžeme spojit, nebo považujete Repository za v podstatě db layer?
je o tom někde nějaký rozumný a čitelný výtah? nějaký tip?
Když to spojíte, budete muset za a) přepisovat repository při změně databázového systému a za b) by jste musel psát SQL dotazy pro všechny tabulky do jedné třídy! v podstatě jde o DB layer. Repository uchovává připojení k DB a podle názvu modelu dokáže najít tu třídu, co tahá věci z tabulky. Článek se pokusím najít.



Principiálně bych doporučil odněkud brát instanci DibiConnection, třeba v konstruktoru. Pokud to bude nevyhovující, je možné vytvořit potomka, který bude mít: function __construct(){
parent::__construct(dibi::getConnection()); }