Doctrine ORM – wprowadzenie
- Mateusz Żeromski | 2009-03-15 | Bazy danych Inne Php
W ciągu kilku następnych artykułów mam zamiar napisać trochę o Doctrine. W wielkim skrócie jest to coś takiego co zastepuje funkcje mysql_query oraz umila i przyspiesza pracę z bazami danych – oczywiście po zapoznaniu się z technologią. Profesjonalnie – jest to relacyjne mapowanie danych na obiekty - Object-Relational Mapping, czyli ORM.
Prolog – jak dochodzimy do sensu istnienia ORM?
Każdy developer php w swojej karierze dochodzi do punktu, w którym wkurza go powtarzanie nauczonych na pamięć komend, i stara się coś zautomatyzować, np.: jedną z możliwości jest pisanie klasy do obsługi bazy danych – zazwyczaj w pliku DB.class.php. Wtedy już nie trzeba używać mysql_connect, tylko tworzy się obiek DB a jako parametry podaje się dane konfiguracyjne, host/login/hasło itp.
W takim stanie nasz developer nie musi już wykonywać żmudnych poleceń mysql_query, tylko np.: $db -> getProductById($id), np.:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | Class DB{ function __construct($h,$l,$p) { //tu się łaczymy } function getProductById($id){ //i tu standard, np $q = mysql_query('select * from product where id='.(int)$id); retrun $this -> getRowByQuery($q); } function getRowByQuery($q){ return mysql_fetch_array($q); } } $db = new DB('localhost', 'root', ''); $a = $db -> getProductById(5); echo $a['id']; |
Powyżej jest wersja uproszczona – zakładam że średnio rozgarnięty developer stworzy dziedziczenia i rozszerzenia, no ale skupmy się na tym co trzeba. Dzięki takiemu rozwiązaniu jest oddzielana warstwa modelu w inne miejsce od kodu działającego na tych danych. Powstaje w ten sposób MVC, model, view, controller.
Można by powiedzieć, że taka nasza klasa DB jest prostym przykładem ORM – bo końcowo już nie wpisujemy komend sql’a tylko wykonujemy metody jakiegoś obiektu – co prawda otrzymujemy tablice nie obiekty ale to tylko takie uproszczenie.
Sprawa się komplikuje w przypadku relacji, cachowania itp – wtedy nasza klasa zaczyna mieć problemy ponieważ nie przewidzieliśmy tego :). Jedyne co możemy zrobić to dodawać kolejne metody np.: getProductsCategoryBySiteId($id) itp. Jednak przy większym projekcie, większej bazie danych robi się problem. Zwłaszcza gdy mamy wiele relacji, a z naszego kodu korzystają inni.
W takiej sytuacji najlepiej będzie, jak skorzystamy z gotowego u standaryzowanego rozwiązania: Doctrine lub Propel (są też inne ale, ale te dwa są najpopularniejsze). Wtedy już nie będziemy musieli bawić się w dodawanie kolejnej metody dla każdego SQL oraz będziemy mieli pewność, że inna osoba jak przejmie po nas kod – zrozumie o co w nim chodzi – wystarczy przeczytać dokumentację.
Przykład użycia Doctrine
Załóżmy, iż mamy taką strukturę bazy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | strona - id - tytul sekcja - id - strona_id - nazwa element - id - sekcja_id - pozycja - typ box - id - element_id - typ - name - value |
Opis: Strona ma wiele sekcji. Sekcje mają wiele elementów. Elementy są dynamiczne i nie wiadomo jakie będą miały docelowe wartości, np tytuł, tresc, autor, data utworzenia itp, dlatego element ma wiele boxow. Box ma pola name i value. W przypadku gdy element ma mieć 30 atrybutów – posiada 30 wierszy w tabeli Box.,dodatkowo ma pole typ aby było wiadomo jak wyświetlić informację np
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //przechowywane dane box id element_id typ name value 1 3 txt tytul tresc 2 3 txt content tres 3 3 gfx mini url_obarzka 4 3 gfx maxi url obrazka 5 3 txt zrodlo opis_zrodla ...... //potrzebny wynik tytul -> tresc content - >tres mini -> url_obrazka maxi -> urlObrazka zrodlo -> opis zrodka .... |
Naszym celem jest wyświetlenie strony, przez co musimy przejść przez wszystkie relacje, normalnie – byłoby to skomplikowane ponieważ musimy napisać dobrego sql, no i dodatkowo musimy prze konwertować dane z boxa z wersji wierszowej na kolumnową. W doctrine to byłka z masłem :)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //nie wiem czy to zadziala pisze z pamięci :) $page = Doctrine_Query::create() ->from('Strona s') ->where('s.id = ?', 1) ->order(e.Element asc) ->fetchOne(); //ale takie odwolanie napewno $page = Doctrine::getTable('Strona')->find(1); foreach($page -> Sekcja as $sekcja){ foreach($sekcja -> Element as $element){ $values = $element -> Box -> toKeyValueArray('name', 'value'); $out .= renderElement($element -> type, $values); } } echo $out; |
Funckja renderElement jest odpowiedzialna za poprawne wyświetlenie danego typu elementu na podstawie danej tablicy. Tu muszę przypomnieć, że doctrine cachuje zapytania przez co z całą pewnością nie będziemy mieli problemów z wydajnoscią. Jak widać użycie doctrine jest banalne, wystarczy tylko trochę się przestawić na inne myślenie, i zapomnieć, że mieliśmy bazę danych – teraz mamy obiekty.
Podsumowanie
To jest tylko wprowadzenie do tego czym są ORM’y, chciałem przedstawić ogólną idee tej technologii, dlatego mało tutaj konkretów. Podałem również jeden przykład zastosowania – który również nie wiem czy dziala (pisałem z pamięci). W aktualnym projekcie (fajny duży i skomplikowany:) ) zostałem zmuszony do używania doctrine, ponieważ już jakaś część systemu tego używała i muszę przyznać, że po pierwszych trzech dniach walki – teraz mi to znacznie ułatwia pracę. Gorąco polecam tę technologię. Postaram się jakoś niebawem opisać bardziej szczegółowo te technologię.
ORM doctrine – link
http://www.doctrine-project.org/
Tagi: doctrine, mysql, Optymalizacja, orm, php
1 Siegfried 2009-06-21 10:54:10
Ha! no nie ma to jak poczytac fachowcow, blog dodany!
pozdrawiam
2 loopb 2009-11-10 15:25:26
Nie lepiej użyć toArray()?
3 Mateusz Żeromski 2009-11-10 16:05:41
Załóżmy jest tabela
name|value
param1|3
param2|4
param4|5
Użycie na obiekcie zawierającym te dane toArray() spowoduje iż otrzymam tablicę z trzeba elementami
array
array
name = param1
value = 3
array
name = param2
value = 4
array
name = param3
value = 5
Uzywając toKeyValueArray($name, $value);
Otrzymam tablice
array
param1 = 3
param2 = 4
param3 = 5
O właśnie dlatego użyłem toKeyValueArray zamiast toArray :)