2009-07-21 11 views
5

Jestem zainteresowany niektórymi projektami związanymi z Rails ActiveRecord, Doctrine for PHP (i podobnymi ORMami).Za kulisami: W jaki sposób ORM "myśli"?

  • W jaki sposób ORM udaje się zrealizować funkcje, takie jak powiązane akcesoria i jak duże są zazwyczaj oczekiwane wyniki?
  • W jaki sposób ORM konstruuje zapytania wewnętrznie?
  • W jaki sposób ORM zarządza zapytaniami, zachowując arbitralny charakter wszystkiego, czego się od nich oczekuje?

Oczywiście jest to kwestia akademicka, ale wszystkie natury odpowiedzi są mile widziane!

(Mój język z wyboru jest OO PHP5.3!)

+7

Nie wolno antropomorfizować komputerów. Oni tego nie lubią. – finnw

Odpowiedz

3

Łańcuchowe wywołania metod są ortogonalne dla pytania ORM, są używane w OOP w całym miejscu. Metoda łańcuchowa po prostu zwraca referencję do bieżącego obiektu, umożliwiając wywołanie zwracanej wartości. W PHP

class A { 
    public function b() { 
     ... 
     return $this; 
    } 

    public function c($param) { 
     ... 
     return $this; 
    }  
} 


$foo = new A(); 
$foo->b()->c('one'); 
// chaining is equivilant to 
// $foo = $foo->b(); 
// $foo = $foo->c(); 

Co do sposobu konstruowania zapytań, istnieją dwie metody. W ActiveRecord takich jak ORM istnieje kod, który analizuje metadane bazy danych. Większość baz danych ma jakieś polecenia podobne do SQL lub SQL, aby wyświetlić te meta-dane. (MySQL's DESCRIBE TABLE, tabela Oracle USER_TAB_COLUMNS)

Niektóre ORMy opisują tabele bazy danych w neutralnym języku, takim jak YAML. Inni mogą wywnioskować strukturę bazy danych ze sposobu, w jaki stworzyłeś modele Object (chcę powiedzieć, że Django to robi, ale minęło trochę czasu odkąd na to patrzyłem). Wreszcie istnieje podejście hybrydowe, w którym zastosowano jedną z dwóch poprzednich technik, ale dostarczane jest oddzielne narzędzie do automatycznego generowania YAML/etc. lub pliki klas.

Jedna z nazw i typów danych tabeli jest znana, całkiem łatwo jest pragmatycznie napisać zapytanie SQL, które zwraca wszystkie wiersze lub określony zestaw wierszy spełniających określone kryteria.

chodzi o ostatnie pytanie,

Jak ORM zarządzać zapytań podczas utrzymanie arbitralną naturę wszystkich że oczekuje się od niego?

Twierdzę, że odpowiedź brzmi "niezbyt dobrze". Po przejściu poza metaforę jedno-tabelową, jednoprzedmiotową, każda ORM ma inne podejście - filozofię, w jaki sposób zapytania SQL powinny być używane do modelowania obiektów. W skrócie, jest to tak proste jak dodawanie nowych metod budujących zapytania oparte na założeniach ORM (np. Metoda "findManyToManyRowset" Zend_Db_Table)

+0

Dzięki! To naprawdę świetna odpowiedź. Tak naprawdę, ORM czerpie swoją złożoność z funkcji, które oferuje na dość prostym generatorze zapytań? –

+0

To jeden ze sposobów na wprowadzenie go. Byłbym jednak ostrożny z terminem "ORM". Istnieje wiele osób, które nie uznają rzeczy takich jak AcrtiveRecord za prawdziwą ORM, ale raczej narzędzie do wdrażania ORM. Więcej tutaj http://kore-nordmann.de/blog/why_active_record_sucks.html –

+0

To na pewno prawda. Zwykle nazywam moją kopalnią OO dostępu do danych lub podstawową klasą danych OO. Dzięki za informacje :) Być może będę musiał podać bardziej szczegółowe pytanie o to, jak generowane są same zapytania. Określanie, które kolumny do pobrania jest łatwe. Ale jestem pewien, że istnieje pewien złożony proces generowania klauzuli WHERE i innych warunków ... –

0

Chained Akcesory nie są naprawdę wielka sprawa: wy return $this z metody setter. Boom, gotowe, działa na tylu poziomach, ile chcesz.

+0

Masz na myśli metodę "getter"? Po prostu przechowujesz stan dowolnej żądanej metody i przywracasz oryginalny obiekt, aby otrzymać dalsze poprawki. Powiedz w PHP, jaki byłby dobry sposób, aby wiedzieć, że jesteś na końcu łańcucha - i dlatego powinieneś zwracać wyniki zamiast "tego"? –

+0

Nie, mam na myśli metody setera. Łączenie akcesorów przy * czytaniu * akcesorów, zamiast * dodawaniu * akcesorów, jest dla mnie bezsensowne. – chaos

+0

Interesujące, mogę po prostu używać terminu "źle". Czy możesz wyjaśnić, jak to możliwe? –

1

Stworzyłem prezentację na temat budowy PHP DataMapper, który może być interesujące dla ciebie.Został on nagrany na wideo w Oklahoma City Coworking Collaborative kiedy przedstawił go tam dla grupy użytkowników PHP:

wideo: http://blip.tv/file/2249586/

Prezentacja Slajdy: http://www.slideshare.net/vlucas/building-data-mapper-php5-presentation

Prezentacja była w zasadzie na początku pojęcie z phpDataMapper, chociaż wiele się zmieniło od tego czasu.

Mam nadzieję, że pomogą ci lepiej zrozumieć wewnętrzne funkcjonowanie ORM-ów.

2

W jaki sposób ORM udaje się zrealizować funkcje, takie jak powiązane akcesoria i jak duże są zazwyczaj działania?

Nikt chyba nie odpowiedział na to pytanie. Mogę szybko opisać, w jaki sposób Doctrine robi to w PHP.

W Doctrine, żadne z pól widocznych w modelu obiektowym nie jest faktycznie zdefiniowane dla tej klasy. W twoim przykładzie, $ car-> owners, nie ma faktycznego pola o nazwie "owner" zdefiniowanego w klasie $ car.

Zamiast tego ORM używa magicznych metod, takich jak __get and __set. Więc jeśli użyjesz wyrażenia takiego jak $ car-> color, wewnętrznie wywołaj PHP Doctrine_Record #__ get ('color').

W tym momencie ORM może to zaspokoić w razie potrzeby. Istnieje wiele możliwych projektów tutaj. Może przechowywać te wartości w tablicy o nazwie $ _values, na przykład, a następnie zwrócić $ this -> _ values ​​['color']. Doctrine w szczególności śledzi nie tylko wartości każdego rekordu, ale także jego status względem trwałości w bazie danych.

Jednym z przykładów tego, co nie jest intuicyjne, są relacje Doctrine. Kiedy dostajesz odniesienie do samochodu $, ma on związek z tabelą Osoby, która nazywa się "właściciele". Tak więc dane dla $ car-> owners są w rzeczywistości przechowywane w osobnej tabeli z danych dla samego samochodu $. ORM ma dwie możliwości:

  1. Za każdym razem, gdy ładujesz $ użytkownika, ORM automatycznie dołącza do wszystkich powiązanych tabel i zapełnia informacje w obiekcie. Teraz, kiedy robisz $ car-> owners, dane już tam są. Ta metoda jest jednak powolna, ponieważ obiekty mogą mieć wiele relacji, a te relacje mogą same mieć relacje. Więc dodajesz wiele połączeń i niekoniecznie korzystasz z tych informacji.
  2. Za każdym razem, gdy ładujesz $ user, ORM zauważa, które pola są załadowane z tabeli User i zapełnia je, ale żadne pola załadowane z powiązanych tabel nie są załadowane. Zamiast tego niektóre metadane są dołączane do tych pól, aby oznaczyć je jako "nie załadowane, ale dostępne". Teraz, gdy napiszesz wyrażenie $ car-> właściciele, ORM widzi, że relacja "właściciele" nie została załadowana, i wydaje osobne zapytanie, aby uzyskać te informacje, dodać je do obiektu, a następnie zwrócić te dane. Wszystko to dzieje się w sposób przejrzysty, bez potrzeby uświadamiania sobie tego.

Oczywiście Doctrine używa numeru 2, ponieważ numer 1 staje się niewygodny w przypadku prawdziwej witryny produkcyjnej o umiarkowanej złożoności. Ale ma również skutki uboczne. Jeśli używasz kilku relacji w $ car, Doctrine załaduje każdy z nich osobno, gdy będziesz mieć do niego dostęp. Tak więc kończysz z 5-6 zapytaniami, gdy tylko może być wymagana tylko jedna.

Doctrine pozwala zoptymalizować tę sytuację za pomocą Doctrine Query Language. Poinformujesz DQL, że chcesz załadować obiekt samochodu, ale także dołączyć do jego właścicieli, producenta, tytułów, zastawów itp. I załaduje wszystkie te dane do obiektów.

Whew! Długa odpowiedź. Zasadniczo jednak dostałeś się w sedno "Jaki jest cel ORM?" i "Dlaczego powinniśmy używać jednego?" ORM pozwala nam nadal myśleć w trybie obiektowym w większości przypadków, ale abstrakcja nie jest doskonała, a przecieki w abstrakcji mają tendencję do wychodzenia jako kary za wydajność.

Powiązane problemy