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 :
<?php class Persons extends Record { } ?>
<?php class PersonsPayments extends Record { } ?>
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
SELECT persons.name, persons.surname personsPayments.amount ...
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.
<?php class Persons extends Record { public function get($column) { return parent::get($column); } } ?>
<?php class PersonsPayments extends Record { public function get($column) { return parent::get($column); } } ?>
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ć.
<?php class PersonsPayments extends Record { public function get($column) { switch ($column) { case 'amount': $amount = parent::get('amount'); // ... dodatkowe modyfikacje amount return $amount; default: return parent::get($column); break; } return parent::get($column); } } ?>
Idac dalej, czy Record powinien zawierac dodatkową logikę ?
<?php class PersonsPayments extends Record { // ... // public function isActive() { // .. // } } ?>
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ę :
$record = new Osoba(); $record->get('imie'); $record->get('nazwisko'); // kwota jest liczona dynamicznie, łatwiej jest zaimplementować obliczenia np. w PHP niż w SQL $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 ?