DataMapper ORM i Gas ORM

Kiedyś pisałem o rozszerzaniu klasy CI_Model, natomiast nie tak dawno Slav opisał jak można zacząć pracę z gotowym rozwiązaniem tego typu, które stworzył Jamie Rumbelow. Dzisiaj chciałbym Wam przedstawić rozwiązania typu ORM, które zostały napisane specjalnie dla CodeIgniter. Będą to DataMapper ORM i Gas ORM.

Tytułem wstępu – oba powyższe rozwiązania (wbrew temu co sugeruje nazwa pierwszego z nich) opierają się o wzorzec Active Record. Czym jest jednak ORM? Najprościej rzecz ujmując, to odwzorowanie naszych relacji w bazie danych za pośrednictwem obiektów. Tak więc każdy wiersz w tabeli ma swój własny obiekt. Systemy ORM wpisują się w filozofię RAD. Po więcej informacji zapraszam np. na Wikipedię.

Teraz warto zadać sobie jedno z podstawowych pytań: czemu mamy w ogóle korzystać z któregokolwiek z tych systemów? Jak w każdym przypadku, są plusy dodatnie i plusy ujemne ;) Za korzystaniem z systemów ORM przemawia prostota i szybkość operowania na danych. Wystarczy na początku odpowiednio skonfigurować relacje między tabelami (nawet jeśli są one skomplikowane) i możemy w łatwy sposób operować na danych, bez konieczności pisania dodatkowych zapytań i komend – po prostu operujemy na obiekcie. Niestety minusem takiego podejścia jest często wydajność zapytań, które są tworzone oraz konieczność pisania dodatkowych zapytań przy bardzo skomplikowanych relacjach (a raczej próbie odpowiedniego wyciągnięcia z nich danych). Biorąc jednak pod uwagę, że bardzo często operacje na bazie danych, które wykonujemy są dosyć standardowe, to skorzystanie z systemu ORM, może przyspieszyć naszą pracę. Odpowiedź na pytanie, czy skorzystać z danego rozwiązania pozostawiam jednak każdemu do samodzielnego rozstrzygnięcia – może to zależeć od naszych indywidualnych preferencji lub projektu nad którym pracujemy. Pamiętajmy, że zazwyczaj nie istnieje jedno słuszne rozwiązanie, które musimy stosować zawsze.

Systemy ORM opierają się na dwóch prawidłowościach. Pierwszą jest późne wiązanie (Lazy Loading), a drugą wczesne wiązanie (Eager Loading). W pierwszym przypadku zapytania, które konstruuje ORM są zawsze proste (a przynajmniej powinny), czyli zwykłe polecenia SELECT, bez złączeń itp. W niektórych przypadkach może to być bardzo przydatne, ale w innych już niekoniecznie. Innymi słowy, jeśli stosujemy późne wiązanie, to ORM pobiera na początku tylko podstawowe dane o obiekcie i dopiero w momencie kiedy w kodzie naszej aplikacji poprosimy o dostęp do kolejnych elementów obiektu, wykonane zostaną kolejne zapytania do bazy.

Wyobraźmy sobie, że prosimy o listę komentarzy i mamy wcześniej odpowiednio skonfigurowane relacje między tabelami. Wykonywane jest jedno zapytanie, które dostarcza nam komentarze, a w momencie kiedy chcemy wyświetlić nazwę autora danego komentarza wykonywane są kolejne zapytania. Stajemy wtedy przed problemem o złożoności n+1. Systemy ORM domyślnie działają właśnie na tej zasadzie. Sprzyja to oczywiście podejściu RAD, ale trzeba mieć świadomość, że prędzej, czy później będzie trzeba się cofnąć do pewnego punktu i jeśli sytuacja będzie tego wymagać, skorzystać ze wczesnego wiązania. Pamiętajmy jednak, że jedno duże zapytanie nie zawsze jest lepsze od 10 mniejszych ;)

No dobrze przyszedł chyba czas, aby przedstawić możliwości obu systemów ORM.

DataMapper ORM

DataMapper ORM jest chyba jednym z najstarszych tego typu rozwiązań dla CodeIgniter – jednym słowem dojrzałym projektem. Jak do tej pory miał on trzech głównych opiekunów i wiele osób współtworzących – obecnie poszukiwana jest nowa osoba, która poprowadzi projekt dalej. DataMapper ORM jest kompatybilny z CI od wersji 1.7.2 do 2.x. Gałąź 3-dev nie jest na chwilę obecną wspierana. DataMapper ORM jest rozwiązaniem, któremu raczej nic nie brakuje. Wspiera wszystkie podstawowe funkcjonalności, włączając w to podzapytania i system rozszerzeń oraz (co może ucieszyc niektóre osoby) rozszerzenie HMVC.

Kawałek kodu zazwyczaj mówi więcej niż tysiąc słów, więc i tym razem się nim posłużę. Zrealizuję prosty przykład, o którym wcześniej wspominałem, czyli wyświetlanie komentarzy wraz z ich autorami. Na początek musimy pobrać DataMapper ORM i rozpakować do folderu, w którym znajduje się nasza instalacja CodeIgniter (korzystam z wersji 2.1.4). Następnie pozostaje nam zdefiniowanie ustawień dostępu do bazy danych w pliku application/config/database.php oraz application/config/autoload.php, gdzie ustawiamy do automatycznego ładowania biblioteki database oraz datamapper. Ostatnim krokiem będzie edycja pliku index.php, w głównym folderze frameworka, gdzie musimy dodać następującą linijkę:

Ważne, aby powyższy kod umieścić jeszcze przed wywołaniem pliku code/CodeIgniter.php‚. To tyle – właśnie zaistalowaliśmy DataMapper ORM i jest on gotowy do działania. Możemy rozpocząć implementację prostego przykładu. Najpierw w utworzonej wcześniej bazie zdefiniujmy odpowiednie tabele:

Teraz pora na zdefiniowanie relacji w naszych modelach. Najpierw plik application/models/user.php:

A następnie plik comment.php, w analogicznej lokalizacji:

Zdefiniowanie relacji jest kluczowe, ponieważ pozwala w późniejszym czasie na bardzo wygodne odwoływanie się do danych, bez konieczności tworzenia skomplikowanych zapytań. Teraz stworzymy prostą metodę dla kontrolera, która pozwoli nam na wypełnienie bazy danymi:

Powyższy kod dał nam już jakieś pojęcie na temat tego w jaki sposób możemy tworzyć dane. Skoro nasza baza danych jest już gotowa, możemy przejść do testów i zobaczyć w jaki sposób możemy pobierać dane. Wybaczcie, że nie zastosuję w przykładach widoków, ale chcę ograniczyć ilość niepotrzebnego kodu do minimum.

Pierwszy przykład będzie oparty o późne wiązanie, czyli pobieranie dodatkowych danych, tylko wtedy, kiedy na pewno będą one potrzebne.

Oto skrócony zapis zapytań, które zostaną wykonane:

Teraz pora na przykład, który wykorzysta wczesne wiązanie, czyli pobieranie danych w jednym zapytaniu:

W tym wypadku zostaną wykonane następujące zapytania:

Zauważcie jak mała zmiana w kodzie, wpływa na to, w jaki sposób pobierane są dane z bazy danych (włączmy profiler $this->output->enable_profiler(TRUE)). Oczywiście to bardzo podstawowy przykład, i DataMapper ma całą masę możliwości, z którą warto się zapoznać. Nie wszystkie zapytania są oczywiście optymalne (co sami mogliście zaobserwować) i z tym również należy się liczyć.

Gas ORM

No dobrze, skoro liznęliśmy już pierwszą bibliotekę, pora przejść do Gas ORM. Tę bibliotekę stworzył Taufan Aditya i jest ona kompatybilna z CI 2.x (jeśli chcemy używać migracji, od wersji 2.1). Gas ORM jest ciekawym rozwiązaniem, które jest zaprojektowane w taki sposób, aby wykonywać jedynie proste i szybkie zapytania – dlatego też nie ma możliwości generowania złożonych zapytań, z łączeniem tabel (JOIN). Czasami oczywiście będziemy chcieli takie zapytanie wykonać, ale wtedy nic nie stoi na przeszkodzie aby samemu je napisać. Generalnie takie podejście sprawdza się naprawdę dobrze – jak wiadomo łączenie tabel zawsze jest kosztowne.

Tak jak poprzednia omawiana biblioteka, Gas ORM posiada system rozszerzeń oraz wbudowaną walidację. GasORM wymaga PHP w wersji 5.3 i korzysta z przestrzeni nazw – do tego potrafi na podstawie struktury tabel wygenerować podstawową strukturę modeli, którą później wystarczy dostosować do naszych potrzeb (określić relacje), oraz na odwrót – utworzyć tabelę na podstawie modelu oraz tworzyć migracje. Do tego filtry, cache’owanie i obsługa wielu połączeń i baz danych. Po pełną listę funkcji odsyłam jak zawsze na stronę projektu.

Przejdźmy do konkretów. Na początek pobierzemy bibliotekę Gas ORM i przejdziemy do instalacji, która ogranicza się do skopiowania zawartości czterech folderów do ich odpowiedników w naszej aplikacji. Jeśli instalowaliśmy wcześniej DataMapper ORM, to musimy skorzystać z czystej wersji CI. Następnie dodajemy bibliotekę gas do tablicy automatycznego ładowania oraz ustawiamy dane dostępowe dla bazy danych (w tych samych plikach co poprzednio przy DataMapper). Dla samej bazy danych ponownie użyjemy tej samej struktury co w poprzednim przykładzie. Co prawda taka właśnie tabela pociągnie za sobą konieczność dodania kilku dodatkowych linijek kodu (Gas ORM zakłada domyślnie inne nazewnictwo tabel i niektórych pól, niż w naszej bazie), ale myślę, że dzięki temu lepiej zapoznamy się z możliwościami jakie mamy. Zdefiniujmy model User:

oraz model Comment:

Teraz możemy wypełnić bazę przykładowymi danymi:

Jeśli modele są już zdefiniowane, a przykładowe dane gotowe, to pora na sprawdzenie w jaki sposób można pobierać dane z bazy. Podobnie jak w przypadku poprzedniej biblioteki, tutaj również będziemy to robić na dwa sposoby. Najpierw późne wiązanie:

Oto zapytania jakie zostaną wygenerowane (zwróćmy uwagę, że Gas ORM wykonał tylko 2 zapytania pytając o dane użytkowników, a nie jak byśmy zakładali 10):

Teraz zobaczymy, jak będzie wyglądało wczesne wiązanie:

Wygenerowane zapytania to:

Jeśli włączymy profiler ($this->output->enable_profiler(TRUE)), to ponownie będziemy mogli zaobserwować zmiany jakie zachodzą w generowanych zapytaniach.

Podsumowanie

W tym małym zestawieniu przedstawiłem Wam dwa systemy ORM – czemu akurat one, a nie np. PHPActiveRecord, Doctrine, Propel, Eloquent, czy jeszcze inne biblioteki? Ponieważ zarówno DataMapper ORM jaki i Gas ORM wyrosły bezpośrednio ze środowiska CI. Wybierając jedną z tych bibliotek, otrzymujemy rozwiązanie zaprojektowane specjalnie dla CI. Dla niektórych może być to wada lub zaleta – nie mnie rozstrzygać. W każdym razie dzięki temu otrzymujemy bibliotekę, która opiera się na sterowniku bazy danych z CI, która współpracuje z walidacją danych z CI oraz która oferuje nam składnię, którą już znamy.

Nie oznacza to oczywiście, że inne rozwiązania są złe lub nie warta Waszej uwagi. Próbujcie różnych rozwiązań i dzielcie się swoimi uwagami. Jeśli ktoś chciałby zaprezentować inne rozwiązanie, które wykorzystuje przy pracy z CI, to bardzo chętnie o tym przeczytamy :)

Mam nadzieję, że tym tekstem zachęciłem Was do skorzystania z powyższych rozwiązań i sprawdzenia, czy okażą się przydatne podaczas Waszej codziennej pracy.

One thought on “DataMapper ORM i Gas ORM

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

This site uses Akismet to reduce spam. Learn how your comment data is processed.