Doctrine ORM – wprowadzenie

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: , , , ,

Komentarze: 3 do “Doctrine ORM – wprowadzenie”

  1. 1 Siegfried

    Ha! no nie ma to jak poczytac fachowcow, blog dodany!
    pozdrawiam

  2. 2 loopb

    Nie lepiej użyć toArray()?

  3. 3 Mateusz Żeromski

    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 :)

Napisz komentarz