Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Wywód ORM
Forum PHP.pl > Forum > Bazy danych
ttmdear
Witajcie,

Od jakiegoś czasu piszę ORM dla PHP. Zastanawiam się nad pewnym zagadnieniem
które opisuje poniżej.

Istnieją dwie tabele w bazie danych "persons", "personsPayments".

Odpowiadające im klasy :

  1. <?php
  2.  
  3. class Persons extends Record {
  4.  
  5. }
  6.  
  7. ?>

  1. <?php
  2.  
  3. class PersonsPayments extends Record {
  4.  
  5.  
  6. }
  7.  
  8. ?>


W tabeli "personsPayments" istnieje kolumna "amount" która zawiera wartość
płatności w EUR.

Poproszono nas o to aby stworzyć zestawienie persons.name, persons.surname,
personsPayments.amount. Możemy załatwic prostym zapytaniem

  1. SELECT
  2. persons.name,
  3. persons.surname
  4. personsPayments.amount
  5. ...

Pisanie zapytań na każdy przypadek jest szybie, no ale niesie ryzyko, np. jeśli
będzie trzeba zmodyfikować wyświetlanie formatu nazwika itp. no to trzeba by
odnaleźć wszystkie te zapytania i wprowadzić poprawkę. Nie da się rownież
stworzyć na tyle uniwersalnej funkcji która by tworzyła zapytanie które będzie
można użyc w kazdym miejscu.

Dlatego chce korzystać z ORM, gdzie wszystkie dane przechodzą dodatkowo przez
klase Record ktora zostawia mi furtkę na tego typu przypadki.

  1. <?php
  2.  
  3. class Persons extends Record
  4. {
  5. public function get($column)
  6. {
  7. return parent::get($column);
  8. }
  9. }
  10.  
  11. ?>

  1. <?php
  2.  
  3. class PersonsPayments extends Record
  4. {
  5. public function get($column)
  6. {
  7. return parent::get($column);
  8. }
  9. }
  10.  
  11. ?>


Napewno plusem tego rozwiazania bedzie to że bede miał jedno miejsce
definiowania wartości. Jeśli będzie trzeba przeformatować imie np. aby
wyświetlało się z dużej litery, to jedyna zmiana będzie w recordzie.

Minusem bedzie wydajność, ograniczone sortowanie, dla kolumn zmodyfikowanych
przez rekord, sortowanie z bazy danych może byc niepoprawne.

Sprawa się bardziej komplikuje, watość kolumny "amount" powinna zostać
dynamicznie zwiększona pod pewnymi warunkami. Czyli fakt że mamy record to w
prosty sposób możemy to zaimplementować.

  1. <?php
  2. class PersonsPayments extends Record
  3. {
  4. public function get($column)
  5. {
  6. switch ($column) {
  7. case 'amount':
  8. $amount = parent::get('amount');
  9.  
  10. // ... dodatkowe modyfikacje amount
  11. return $amount;
  12.  
  13. default:
  14. return parent::get($column);
  15. break;
  16. }
  17. return parent::get($column);
  18. }
  19. }
  20.  
  21. ?>

Idac dalej, czy Record powinien zawierac dodatkową logikę ?

  1. <?php
  2. class PersonsPayments extends Record
  3. {
  4. // ... //
  5. public function isActive()
  6. {
  7. // .. //
  8. }
  9. }
  10.  
  11. ?>


Czy należało by jednak to przenieść np. do osobnego modelu \Models\Person która
by jednocześnie pracowała na dwóch recordach Persons i PersonsPayments

W tym całym wywodzie zmierzam do tego że szukam metody przeniesienia
znormalizowanej bazy dancyh na klasy w jezyku programowania. No tak aby to
wszystko było elastycznej a jednoczesnie szybkie. Wiadomo zawsze można pobrać
dane i je w dowolny sposób przerobić, ale bedzie to wolne.

Brak odzewu, odbieram jako źle opisany problem.

Spróbuje to opisać jasniej. W bazie danych mamy znormalizowane dane podzielone na tabelki. Wykonuje róże zapytania w zależności od potrzeby aby wyciągnąć różne zestawy danych.

Tworzac wiele zapytań, w różnych zestawieniach duplikuje pojęcia opisujące te same zbiory danych np. W zakładce A wywołuje zapytanie "select name from persons", w zakładce B "select name, surname from persons". Tym sposobem pojęcie określające czym jest imie osoby zostało zdefiniowane w zakładce A i B, w wyniku czego jeśli zmieni się format wyświetlania imienia (zmiana pojęcia), będziemy musieli zaaktualizować te dwa miejsca.

Ten problem moim zdaniem rozwiązuje ORM, czyli pobranie danych z bazy odbywa się przez klasę w programie, więc jeśli zmieni sie formatowanie imienia osoby, to wystarczy wprowadzić poprawkę w klasie.

Ale tutaj powstaje taki problem, że zamieniając wszystkie tabele na klasy, trace możliwość korzystania z mechanizmów bazodanowych (które sa efefktywniejsze) takich jak JOIN itp.ponieważ teraz pobieram informacje przez metodę :

  1. $record = new Osoba();
  2. $record->get('imie');
  3. $record->get('nazwisko');
  4.  
  5. // kwota jest liczona dynamicznie, łatwiej jest zaimplementować obliczenia np. w PHP niż w SQL
  6. $record->get('amount');


Robiąc to w ten sposób, z oczywistych względów trudniej będzie mi np. stworzyć zestawienie name, surname, amount.

Podsumowując, zastanawiam się nad tym czy logika powinna być wykonywana wmiare na bazie danych, czy w warstwie aplikacji, albo po trochu w obu mechanizmach.

Ja sam to widzę tak, jeśli na bazie danych to aplikacja jest mocno związana z danym silnikiem bazodanowych; Fajnie by było gdyby aplikacja bez większe trudu mogła być przeniesiona na inny silnik bazo danowy.
Natomiast, jeśli baza danych służy tylko do przechowywania danych, to cała logika przechodzi na aplikacje która nie będzie tak efektyczna, ale za to implementowanie tej że logiki jest moim zdaniem prostrze.

Czy ktoś ma swoje przemyslenia na ten temat, albo doświadczenia, jakie rozwiązanie jest lepsze.
A może nie ma takiego rozwiązania i jeśmy skazani na mix ?



bostaf
Hej ttmdear,

Cytat(ttmdear @ 25.03.2017, 19:11:07 ) *
Ale tutaj powstaje taki problem, że zamieniając wszystkie tabele na klasy, trace możliwość korzystania z mechanizmów bazodanowych (które sa efefktywniejsze) takich jak JOIN itp.ponieważ teraz pobieram informacje przez metodę

Nie, nie tracisz. ORM musi udostępniać metody pozwalające "emulować" mechanizmy bazodanowe. Jeśli Twój ORM tego nie ma, to znaczy, że JESZCZE tego nie ma smile.gif

Cytat(ttmdear @ 25.03.2017, 19:11:07 ) *
Idac dalej, czy Record powinien zawierac dodatkową logikę ?

Celem ORMa jest stworzenie obiektów programistycznych z jakichkolwiek usystematyzowanych danych. ORMa nie powinna interesować zawartość biznesowa, czyli w Twoim przypadku, nazwy pól w tabelach. Czyli:

1. Klasa Record zdecydowanie nie powinna zawierać logiki powiązanej z kolumną "amount". Ona może zawierać tylko logikę manipulacji polami rekordu.
2. Klasa PersonsPayments też nie. Bo ona ma być obiektem odwzorowującym strukturę tabeli - pól i relacji. Modyfikowanie danych na tym etapie byłoby zbyt wczesne.

Informacja o paymentach powinna być dostępna z poziomu klasy Persons, bo dokładnie tam istnieje relacja między osobami a płatnościami. I vice versa - informacja o osobie związanej z daną płatnością może być dostępna z poziomu klasy Payments.

Jednym ze sposobów byłoby być może stworzenie interfejsu, którego zadaniem byłoby pobieranie informacji o relacjach - z jakiegoś pliku konfiguracyjnego albo bezpośrednio z informacji w bazie. W taki sposób, żebyś w efekcie mógł do klasy Persons dodać metodę getPayments(). A metoda getPayments() korzystałaby metody get() w Twojej klasie Record, i metody where() (której Twój ORM jeszcze nie ma), i metody join() (której Twój ORM jeszcze nie ma) itd...


...tak mi się wydaje. To dobry temat na wypicie kilku browarów w zadymionej knajpie smile.gif

Bawiłeś się kiedyś jakimś gotowym ORMem? Wydaje mi się, że kilka testów wyklarowałoby Ci sytuację.
ttmdear
Po napisaniu tego posta przegrzebałem internet w poszukiwaniu odpowiedzi. Natrafiłem na wiele artykułów ogólnie hejtujących ORM w kontekście baz relacyjnych. (ORM is Vietnam of Computer Science)
Sam również na własnej skórze to odczułem, w projektach których logika była implementowana bezpośrednio w ORM)

Bogatszy o tą wiedzę smile.gif, zgadzam sie z tym co napisałeś w 100%. smile.gif


To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2024 Invision Power Services, Inc.