Článek v rubrikách:

»PHP
»MySQL
»Dibi

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?

  1. class BaseModel {
  2. public static function find($id) {
  3. $db=získej !! ve static kontextu !! proměnnou DibiConnection
  4. $db->select(.....);
  5. }
  6. public function insert($data) {
  7. $db->insert(.....);
  8. }
  9. }

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.

  1. class Ormion
  2. ...
  3. protected static function getDb() {
  4. if (!self::$db) {
  5. self::$db = new DibiConnection(Environment::getConfig("database"));
  6. }
  7. return self::$db;
  8. }

Zajímavé zdroje (Dependency Injection a Singleton je zlo)

  1. Singletons are pathological liars
  2. Je singleton zlo?
  3. Where Have All the Singletons Gone?

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:

  1. $customer=new Customer();
  2. $customer->setDbConnection(dibi::getConnection('anotherDatabase'));
  3. $customer->email="test@email.cz";
  4. $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.2009DbDibiOrm(50%)

komentáře

RSS Komentáře k článku RSS Komentáře   Add to Google
15.12.2009 18:09 | Anonym (v6ak) | Předávat DibiConnection.

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::__con­struct(dibi::get­Connection()); }

15.12.2009 19:02 | Administrátor | Re: Předávat DibiConnection.

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

15.12.2009 21:08 | Anonym (v6ak) | Re: Re: Předávat DibiConnection.

Ano, pokud bude ta třída mít ty metody statické. Což jsem rozebíral vedle.

15.12.2009 21:35 | Administrátor | Re: Re: Re: Předávat DibiConnection.

a já měl pocit, že to je jeden z důvodů proč lidi toli požadují late static binding, protože ono i bez toho se dá obejít

16.12.2009 11:02 | Anonym (Honza Marek) | getDb v Ormionu

Je nějaký důvod, aby sis z Ormionu nepodědil BaseModel a v tom si metodu getDb přepsal? Tak je to zamýšlené.

16.12.2009 11:26 | Administrátor | Re: getDb v Ormionu
  1. 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
  2. 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

16.12.2009 19:59 | Anonym (Jakub) | jako ASP.NET MVC

Mě jako čísté řešení příjde nemíchat připojení k databázi vůbec do modelu, ale udělat si speciální třídu, která se bude starat o transakce s DB (bude ty modely ukládat), myslím, že se tomu říká vzor Repository

16.12.2009 20:01 | Anonym (Jakub) | Re: jako ASP.NET MVC

ještě příklad:

$user = new UserModel(); $user->name = „Jakub“;

repository::sa­ve($user);

Repository by měla asi být singleton třída, aby byla dostupná všude. Zdá se mi to jako nejčistší OOP řešení.

16.12.2009 21:40 | Administrátor | Re: Re: jako ASP.NET MVC

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->getProductsWho­BuyAlso()

samozřejmě bych to mohl předat nějaké Repository (ProductRepository extends Repository) a říci

ProductReposi­tory::getProduc­tsWhoBuyAlsoWit­h(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->getProductsWho­BuyAlso() { return ProductReposi­tory::getProduc­tsWhoBuyAlsoWit­h(this->id) }

17.12.2009 00:16 | Anonym (Jakub) | Re: Re: Re: jako ASP.NET MVC

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.

17.12.2009 00:24 | Administrátor | Re: Re: Re: Re: jako ASP.NET MVC
  1. 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)
  2. 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?

17.12.2009 23:45 | Anonym (Jakub) | Re: Re: Re: Re: Re: jako ASP.NET MVC

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.

Jméno
Název
Text
Lze používat Texy! syntax. Příklad syntaxe: "text odkazu":odkaz, **tučně**, *kurzíva*, `code`. PHP kód uzavírejte do <?php ... ?> a JavaScript do <script> ... </script>