Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Baza a obiekty
Forum PHP.pl > Forum > PHP > Object-oriented programming
Marq
Moje pytanie brzmi, czy prawidłowym szkieletem aplikacji jest:

klasa bazaDanych

-statyczny obiekt bazy danych (singleton)

-metoda zapysywania czegoś, powiedzmy ogłoszenia

-metoda edycji ogłoszenia

-metoda zapisywania autora

-metoda edycji autora

etc etc..



klasa ogloszenie

-pola typu id, tytul, tresc...

-metoda zapisu, ktora przekazuje obiekt do metody z bazy danych, ktora sobie rozklada go na czesci i wpisuje do bazy

-metoda edycji, podobnie jak wyzej

etc etc....

Czy to jest poprawne, czy zapisywanie do bazy danych obiektu powinno się realizować w jakiś inny (jaki?) sposób (schemat)?



Pozdrawiam
nevt
odradzam singletona na rzecz normalnej klasy i obiektu interfejsu do bazy. przy singletonie tracisz możliwość dostępu do kilku baz, co czasami bywa przydatne...
jarek_bolo
Poczytaj sobie o takich pojęciach jak DataAccessObject i ValueObject np. tutaj.
Crozin
Klasa(y) obsługi bazy danych nie powinny zajmować się konkretnymi przypadkami typu "edycja ogłoszenia"
Marq
Cytat(Crozin @ 22.10.2008, 22:14:33 ) *
Klasa(y) obsługi bazy danych nie powinny zajmować się konkretnymi przypadkami typu "edycja ogłoszenia"




Tylko? Jak to powinno być rozwiązane?

jedna klasa dla przechowywania tylko danych usera, a druga to metody z nim związane, np dodawanie?
mike
Klasa obsługująca bazę, załóżmy, DB powinna oferować możliwość manipulacji na dowolnych danych.
A klasa, załóżmy, News ma sobie skorzystać z DB i zapisać newsa.

Ile masz młotków? Jeden do wbijania czegokolwiek czy kilkadziesiąt? Jeden do wbijania gwoździ, jeden do wbijanie wkrętów, jeden do robienia hałasu, jeden do rozbijania szkła, ....
nevt
mike - nie do końca się z tobą zgadzam, prawda, że podstawowy interfejs do bazy powinien być uniwersalny - ale tu mamy w PHP sporo gotowców jak mysqli czy PDO, natomiast w rozbudowanym projekcie warto stworzyć potomka klasy podstawowej który dodaje bardziej abstrakcyjne metody, powiedzmy np.:, $db->get_menu_content() czy $db->store_news($data).

daje to bardzo czytelny kod, do tego tak naprawdę reszty aplikacji nie obchodzi co się dzieje wewnątrz tych metod, w każdej chwili możemy zmienić silnik bazy danych czy jej wewnętrzną strukturę nie ruszając w ogóle kodu innych klas.

posługując się twoją analogią - warto mieć uniwersalny młotek do wszystkiego, a jednak wyburzając ściany posłużymy się młotem pneumatycznym, naprawiając auto - młotkiem blacharskim, a zbijając meble - młotkiem gumowym - wszystkie one są specjalizacją młotka uniwersalnego pod konkretne zastosowania...

pozdrawiam.
mike
Cytat(nevt @ 23.10.2008, 20:46:52 ) *
(...) abstrakcyjne metody, powiedzmy np.:, $db->get_menu_content() czy $db->store_news($data).
Raczysz żartować? Przecież to bazdura daleka od pojącia "abstrakcja". Abstrakcja to uogulnienie a Ty zaszywasz konkretne rzeczy tam gdzie ich nie powinno być. Spaghetti code
Idąc za Twoim przykładem menu powinieneś pobrać z jakiegoś dostawcy treści, na przykład: Content::getMenu(). Czy na przykład News::save($data)

Przykład, który podałeś od początku do końća jest chybiony.
nevt
mike - uczepiłeś sie jednego słówka "abstrakcja" jak rzep psiego ogona, to prawda pomyliłem się zamiast "abstrakcyjne" powinienem napisać "specjalizowane" - człowiekowi po całym dniu pracy zdarzają się potknięcia. z kolei twój link do spaghetti code jest kompletnie nie na miejscu i bez związku.

ale chociaż nie wciskaj młodzieży durnot, że korzystne jest rozżucenie zapytań do bazy w kilku a może we wszystkich klasach które potrzebują danych z bazy. to jest dopiero porażka. klient zleca ci dodanie kilku opcji które wymuszają wprowadzenie zmian do struktury bazy i co? zamiast mieć wszystko w jednym miejscu latasz po całym projekcie i szukasz gdzie są jeszcze odwołania do zmodyfikowanych struktur?

poza tym nie zaleciłem wrzucania specjalizowanych metod bezpośrednio do klasy będącej interfejsem do bazy. napisałem wyraźnie, że klas bazowa powinna być uniwersalna i umożliwiać stworzenie dowolnego zapytania sql.

oczywiście masz prawo do innych poglądów w tej kwesii. wszystko zależy od przyzwyczajenia, praktyki i stosowanych na co dzień wzorców. ale nie rób osobistych wycieczek i i ogólnikowych analiz pod hasłem sphagetti code. jeżeli głęboko wierzysz w swoje racje to je spokojnie i rzeczowo uzasadnij.

pozdrawiam i proponuję zakończyć tą jałową dyskusję.
Cysiaczek
Cytat
mike - nie do końca się z tobą zgadzam, prawda, że podstawowy interfejs do bazy powinien być uniwersalny - ale tu mamy w PHP sporo gotowców jak mysqli czy PDO, natomiast w rozbudowanym projekcie warto stworzyć potomka klasy podstawowej który dodaje bardziej abstrakcyjne metody, powiedzmy np.:, $db->get_menu_content() czy $db->store_news($data).

Odniosłem wrażenie, że jednak to zaleciłeś, bo jaki inny jest cel umieszczania takiego interfejsu w obiekcie, której instancja kryje się za nazwą "$db"? smile.gif

Jeśli projekt ma być rozbudowany, to nie wyobrażam sobie takiego kodu:
  1. <?php
  2. class DB extends PDO
  3. {
  4.  public function get_menu_content(){}
  5. }
  6. ?>

Zresztą - nie wyobrażam sobie w ogóle takiego kodu chyba, że programista się spieszył a pociąg smile.gif
Od tego są specjalizowane klasy Peer (używając nomenklatury Propela).
  1. <?php
  2. class PMPeer extends BasePMPeer
  3. {
  4.    public static function getInbox(sfGuardUserProfile $user)
  5.    {
  6.        $folderId=PMFolders::getInbox();
  7.        $c=self::folderPatternCriteria($user->getUserId(), $folderId);
  8.        return self::retrieveFolder($c);
  9.    }
  10.  
  11.    public static function getSentItems(sfGuardUserProfile $user)
  12.    {
  13.        $folderId=PMFolders::getSentItems();
  14.        $c=self::folderPatternCriteria($user->getUserId(), $folderId);
  15.        return self::retrieveFolder($c);
  16.    }
  17. }
  18. ?>

W kodzie użytkującym
  1. <?php
  2. // w akcji
  3. $this->pms=PMPeer::getInbox($user);
  4.  
  5. // w widoku
  6. foreach($pms as $pm)
  7. {
  8.  print $pm->getTitle();
  9. }
  10. ?>

Masz wszystko rozdzielone tak, że bardziej się nie da, a kod nadal jest w jednym miejscu - w klasie BasePMPeer, która korzysta z Propela do uzyskania obiektów danych. Propel używa Creole albo PDO, a te już odpowiadają za obsługę zapytań. To zresztą już mało kogo obchodzi, chyba, ze wykonuje jakieś specjalistyczne zapytania smile.gif

Pozdrawiam

p.s
hm, chyba znów reklamuje SF i Propela... ale to dobry wzorzec jest tongue.gif
Crozin
Wracając do przykładu z młotkiem:
Możesz mieć młotek albo młotek gumowy/blacharski/jakiśTam/jakiśInny/itp. - ale to wszystko nadal jest młotkiem.

Młotek udostępnia Ci zestaw metod, które umożliwiają pracę na nim (typu: przybij, podważ, cośTam) ale nie ma w nim przyciusku "przybij gwóźdź do sciany w toalecie" - to wykonuje już jego użytkownik. Dokładnie tak samo jest w przypadku OOP. Klasa, która operuje na bazie danych/plikach tekstowych/dokumnetach XML, INI, YAML itp. itd. sama w sobie nie powinna niczego robić z jakimś konkretnym element po póki, doputy nie dostanie zestawu poleceń z zewnątrz.
xerek
To moze i ja dorzuce swoje 3 grosze jako nowy uzytkownik.
Jakis czas temu takze zmagalem sie z tym problemem i rozwiazalem go w nastepujacy sposob.
Moim zdaniem klasa db ( warstwa abstrakcji klasy db ) powinna realizowac ogolne pojecia, powinna operowac na jakichkolwiek danych byc uniwersalna. Moim zdaniem nie warto twrzyc potomka takiej klasy - warto natomiast korzystac z niej w innych klasach jako złożenia czyli np.

1. mamy klase db
2. Klase powiedzmy News - ktora przechowuje dane w postaci tytulu,autora itp itd oraz posiada metoy dostepowe set i get

Potrzebujemy teraz danych ktore pomoga nam zapisywac Newsy, pobierac je z bazy danych aktualizowac edytowac itp..


Mozemy skorzystac z pierwotnej klasy db i dodac do niej powyzze metody ale moim zdaniem jest to tylko zaśmiecanie bloku klasy...

Mozemy rowniez dodac metody do ogolnej klasy News ale po co ? Przeciez nie jest to podejscie obiektowe prawda?

Moim zdaniem najlepsze rozwiazanie to stworzenie osobnej klasy NewsMenager ktora pobiera Kolekcje obiektow klasy News -> ( tworzy z nich kolekcje )

I tu wlasnie moim zdaniem nalezy umiesc takie metody jak getNewsByID() , getLastnews(), addNews() itp itd  - metody ktore operuja jedynie na kolekcji obiektow News

pozdrawiam
antyqjon
Cytat(xerek @ 26.12.2008, 13:39:53 ) *
I tu wlasnie moim zdaniem nalezy umiesc takie metody jak getNewsByID() , getLastnews(), addNews() itp itd  - metody ktore operuja jedynie na kolekcji obiektow News


I to właśnie robią Propelowe *Peery, co już wyżej Cysiaczek wspomniał. smile.gif
LBO
A ja nie wiem czemu ludzie tak szaleją za klasami serwisowymi vide Propelowe Peery, Javowe Managery smile.gif

Wymusza to tworzenie co najmniej zestawu klas, które teoretycznie tyczą się tego samego obszaru domain logic

Wystarczy pooglądać trochę diagramów UML, żeby wiedzieć, że modele (ale modele! nie nakładki na tabele typu ORM albo AR) mogą się obyć bez tego.
pinochet
Cytat(Marq @ 22.10.2008, 18:41:46 ) *
Moje pytanie brzmi, czy prawidłowym szkieletem aplikacji jest:

Moja odpowiedź brzmi: "Nie jest to żaden szkielet aplikacji"

@LBO możesz rozwinąć swoją myśl ? (doprecyzuj zaimki ;-) )
Nie rozumiem w jaki sposób zastosowanie propela miałoby wymuszać tworzenie zestawu klas do realizacji logiki aplikacji? Nawet nie stosując propela ciężko zrealizować logikę w jednej klasie :]
LBO
Tworzyłeś pewnie niejednokrotnie diagramy UML (czyli model logiki, nie BD), prawda? I pewnie zauważyłeś, że najczęściej można uniknąć obiektów czysto serwisowych. Dlaczego, bo bardzo często diagramy te przyjmują strukturę drzewiastą i wtedy logika pozyskiwania niektórych obiektów idzie o jeden poziom wyżej (jak dla mnie naturalne).

Jako, że lubię przykład bloga, wyobraź sobie dwa modele Blog i Artykuł.

I czy artykuły nie lepiej artykuły pozyskiwać poprzez $blogModel->getArticles($page), a nie ArticlesPeer::getArticles($page)? Wydaje mi się to najbardziej naturalne.
Wiem, że w Propelu można takie relacje stworzyć, ale czy ja muszę koniecznie umieszczać tabelę Blog w bazie, skoro mi nie potrzebna (bo na przykład to jest aplikacja dla jednego bloga, a nie wielu)?

Ja nie szkaluje tutaj Propela, ani żadnego innego ORM - po prostu spodobało mi się zaczynanie projektowania od logiki, a nie bazy smile.gif Taki sposób zabiera więcej czasu i niekiedy mocno przesuwa właściwą fazę pisania kodu, ale za to jest przyjemne w użytkowaniu. Miesiąc temu zrobiłem projekcik, gdzie modele (dla treningu) to POPO bez żadnych powiązań z frameworkiem, ani niczym innym. W środku rzecz jasna umieściłem Propela, ale jednak. Teraz mógłbym je przenieść, bez zmian samego API, do projektu w jakimkolwiek innym frameworku, czy to Symfony, czy Kohana, czy nawet Cake (blee).

Pozdrawiam, Alan
pinochet
Teraz rozumiem o co chodzi. Chyba nikt nie twierdzi że propel jest cudowny i najbardziej optymalny tak jak JAVA i C# nie są językami najszybszymi w sensie czasu działania programu, ale są najszybszymi w sensie czasu tworzenia oprogramowania.
Załóżmy, że $blogModel->getArticles($page) zwraca obiekt Article - co robi ta metoda? SQL + wywołanie konstruktora Article ? czy ustawienie wszystkich jego setterów? Czy może obiekt artykuł jest tworzony z parametrem DB i sam sobie szuka informacji aby sie stworzyć?
Tak wiem że prędzej czy później pewne dane w kodzie muszą być wpisane na twardo biggrin.gif a jedne obiekty chociaż częściowo znać strukturę innych.
LBO
Cytat
a jedne obiekty chociaż częściowo znać strukturę innych.

Zgadzam się z Tobą jak najbardziej i... tak właśnie powinno być. Widzisz modele to powinno być czyste API i nie obchodzi nas wtedy co jest warstwą dostępu do danych, czy Propel, czy Doctrine, czy czyste PDO. Dodatkow wraz założeniami DDD modele powinno spełniać dokładnie założenia i wymogi aplikacji. Czyli nawet nie $blogModel->getArticles($page) (które zwraca tablice obiektów Article z poustawianymi wszystkimi wartościami), a raczej $blogModel->getFrontPageArticles($page) (które zwraca tablicę obiektów Article ale bez treści samego artykułu, a tylko zajawką 255 znaków), bo odzwierciedla to czego wymaga aplikacja bloga (przypadek użycia).

Z tym, że wracamy do Tego co zacytowałem - to Ty projektujesz te modele. I TYLKO od Ciebie zależy jak wewnątrz to działa vide czy BlogModel::getFrontPageArticles(int $page) Article[] sama ustawia Artykuły przy jednym zapytaniu SQL, czy pozwala się im sama zainicjalizować.

Cytat
Tak wiem że prędzej czy później pewne dane w kodzie muszą być wpisane na twardo biggrin.gif


Owszem, często podaje za przykład na tym forum pewna aplikację, która była postawiona na bardzo kiepskim serwerze - dosłownie biurowym kompie. Serwer miękł przy Propelu (mało RAMu etc). I co zrobiłem? Bez zmiany API modelów przepisałem Ich bebechy na hardcoded SQL poprzez PDO. Zaczęło pięknie śmigać, a ja nie ruszyłem nic prócz wnętrzności klas odpowiedzialnych za logikę, same akcje widoki, filtry i co tam jest we frameworkach zostawiając jak było.
wlamywacz
Nie wiem czemu ale ja tworząc aplikacje cały czas wale zapytania sql przez PDO i nie przeszkadza mi to w niczym smile.gif
wrzasq
@wlamywacz: No zapytania SQL z zasady należy wykonywać przez PDO, bądź inne rozszerzenie, bo przez GD na przykład ciężko winksmiley.jpg. A na poważnie - nie przeszkadza ci, dopóki twoja aplikacja w pełni działa na tym, co sam napiszesz. Ale co, jeśli byś nagle musiał się przesiąść? Na przykład z zewnętrznego źródła pobierać notki? Chociażby RSS - coś kompletnie odmiennego od SQL, czyli nie mówimy tutaj nawet o innej strukturze bazy danych, ale o innym rodzaju tej bazy. Albo skonsolidować kilka takich źródeł. W takim wypadku przepisywanie aplikacji będzie udręką, a przecież to nadal ta sama "logika biznesowa" (uwielbiam to sformułowanie... tongue.gif), a wystarczy pobieranie treści zmienić.

(Jasne, że zazwyczaj po prostu ściąga się dane z RSSa i zapisuje w bazie danych lokalnej, ale chciałem zobrazować smile.gif ).
wlamywacz
Przepisze model a nie aplikacje. Kontroler i widok w chcą tylko dane, nie ważne w jaki sposób ja je pobiorę.
LBO
Cytat(wlamywacz @ 4.01.2009, 14:19:19 ) *
Przepisze model a nie aplikacje. Kontroler i widok w chcą tylko dane, nie ważne w jaki sposób ja je pobiorę.


To ja się teraz dziwię Twojemu pytaniu odnośnie PDO.
W swoich modelach stosuje Propela, ponieważ jest to zwyczajnie szybsze rozwiązanie, oszczędzające mi klepania zapytań. Idzie za tym pewien narzut i czasowy jak i fizyczny - czasami bywa tak, że ten narzut jest nie do przyjęcia, wtedy zmieniam bebechy, ale API modeli zostaje.

Teraz spójrz jeszcze z innej perspektywy. Używasz PDO i:
1. Posiada ono ograniczoną liczbę obsługiwanych baz.
2. Obsługuje tylko bazy.
A co jeżeli bazę danych mam udostępnioną tylko poprzez SOAP (wbrew pozorom jest to częste rozwiązanie - sam miałem do czynienia kilka razy z SyBase gdzie ten fjuczer jest zbudowany). Chcąc teraz dostosować do Tego istniejącą już aplikację na przykład w Symfony skończysz na tym, że wymieniasz całe bebechy akcji, piszesz własne klasy Routingu i dosłownie hackujesz framework smile.gif

Taka jest według mnie wyższość prawdziwych modeli od tych "modeli" ORMowych - takich problemów nie ma.

Żeby nie było - bardzo sobie ostatnio zacząłem cenić Symfony. Wspaniały framework, a szybkość tworzenia gotowego projektu zatrważająca smile.gif Stosowanie ORMów z pominięciem warstwy modelu ma mnóstwo plusów to muszę przyznać, bo o ile samym modelom nie nada się jakiegoś stabilnego (przenośnego pomiędzy projektami) API to takich rzeczy jak generatory formularzy, czy CRUD nie zaznamy.

Czyli trzeba wybierać smile.gif
wlamywacz
Powiedzmy sobie tak: mam swój (taka to ostatnio modna nazwa) framwework który tak naprawdę jest zbiorem luźnych klas. Do aktualnego projektu nie potrzebne mi inne rozwiązanie, gdyż znam swoje środowisko pracy - serwer dedykowany z którym robię co zechce i pgsql, tego się trzymam. Jeśli dostane wyraźne info że np. korzystanie wiąże się np. z użycia czasem SOAP-u lub różnych typów baz, wtedy zastosuje np. Twoje rozwiązanie. Kolejną rzeczą jest to że aplikacja musi być bardzo wydajna - będzie pracować pod dużym obciążeniem więc wydaje mi się że czyste klepanie zapytań jest najbardziej optymalne.

P.S. Równie dobrze mogę korzystać z mysql_query i też będzie działało.
LBO
Czuli klepiesz zapytania w bezpośrednio w akcjach?
wlamywacz
A skąd Ci to przyszło na myśl ? sciana.gif Przecież wcześniej napisałem że mam modele, bo wykorzystuje wzorzec MCV.
LBO
Cytat(wlamywacz @ 4.01.2009, 19:57:21 ) *
sciana.gif


eeeee,, po tym?
Cytat
[..] wtedy zastosuje np. Twoje rozwiązanie[...]
pinochet
hmm cały czas piszecie o tym co napisałem w poprzednim poście :] Propel tudzież wszelkie inne frameworki a nawet zastosowanie samych klas zmniejsza czas tworzenia oprogramowania oraz zmniejsza wydajność tego oprogramowania. Rozwój techniki idzie w tą stronę gdyż czas programisty jest dużo droższy niż czas procesora - nawet dedykowanego.

Przy dużych projektach w C++ lub C# jeżeli zależy nam na wydajności robi się analizę i sprawdza które moduły/funkcje są wykonywane najczęściej i zżerają najwięcej mocy - te części aplikacji niejednokrotnie przepisuje się np na asembler.

wrzasku wlamywacz chyba nie przelicza swojej pracy na $ i dlatego klepie zapytania w PDO koszt stworzenia takiej aplikacji jest pewnie kilkukrotnie większy niż tej tworzonej przez LBO biggrin.gif
Dziękuję.
I teraz jeszcze pytanie (stwierdziłem że dotyczy ono topicu) Jak wygląda wasze API modelu ? Co brac pod uwagę przy jego tworzeniu ?

edited: sorki za pomyłkę co do nicka sad.gif głupio mi i łyso sad.gif
wlamywacz
Mam pytanko, czy mógłbyś pokazać przykład jak wyglądało by odpowiednik mojego kodu w `Twoim` Propel-u ?

  1. <?php
  2. return registry::get('db')->fetch("SELECT GROUP_CONCAT(`users`.`user` SEPARATOR ', ') AS `users`, `groups`.`name` AS `groupname`, `jobs`.`id` AS job_id, `jobs`.*, `ujobs`.*, `users`.* FROM `jobs` LEFT JOIN `ujobs` ON `jobs`.`id` = `ujobs`.`id_work` LEFT JOIN `users` ON `users`.`id` = `ujobs`.`user` LEFT JOIN `groups` ON `groups`.`id` = `jobs`.`group` WHERE `jobs`.`id` = $this->jobid GROUP BY `jobs`.`id`");
  3. ?>
pinochet
Oczywiście Propel ma też słabe strony :] i nimi właśnie są JOINY. Nie chce mi się analizować twojej bd na podstawie zapytania SQL :] Moze zeczywiscie troche się uniosłem ... przypomianam jednak że w `Twoim` postgresql są widoki ;-)
Peace
wlamywacz
Jest coś takiego jak projektowanie i wersja testowa aplikacji. Na widoki przyjdzie odpowiednia pora gdyż struktura bazy ciągle się zmienia.
mike
Cytat(wlamywacz @ 4.01.2009, 20:43:24 ) *
Jest coś takiego jak projektowanie (...) struktura bazy ciągle się zmienia.
Nie to, że się czepiam ale jak Ci w czasie implementacji struktura bazy często się zmienia to marnie zaprojektowałeś aplikację tongue.gif
wlamywacz
Szczerze? Pracuje bez specyfikacji, firma mi nadsyła swoje myśli na bieżąco. Więc muszę się do nich dostosować ale opłaca się $.
LBO
Cytat(pinochet @ 4.01.2009, 20:08:37 ) *
I teraz jeszcze pytanie (stwierdziłem że dotyczy ono topicu) Jak wygląda wasze API modelu ? Co brac pod uwagę przy jego tworzeniu ?


Spróbuję jeszcze raz napisać.

U mnie modele prócz getterów i setterów i możliwości inicjalizacji w miejscu - kiedy podaje id do metody inicjalizującej model sam pobiera swoje dane - posiadają metody odpowiadające logice aplikacji.

Kilka miesięcy temu pisałem projekt dla Służby Zdrowia. Wymagał on pełnego zarządzania różnymi jednostkami lecznictwa tj. ZOZY, praktyki lekarskie, pielęgniarskie, grupowe, indywidualne itd.
Prócz cech wspólnych takich jak REGON, adres, data rozpoczęcia działalności miały też tylko sobie przypisane: ZOZY na przykład tzw numer księgi, który był unikalny (w przeciwieństwie do Regonu), a grupowe praktyki były podpięte pod jakiś ZOZ.
Posiadałem już modele na wzór powyższych blogowych typu HealthCareInstitution (ZOZ), IndividualPrivatePractice, GroupPrivatePractise itd.
Zaznaczam, że już na tym etapie modele nie odzwierciedlały tabel w bazie 1:1 tj.
reporting_units(id, name, regon, address, zip_code, city, unit_type, ...) - główna tabela, trzymająca wspólne dane dla wszystkich typów jednostek.
health_care_institutions (id, book_number, opened_at, closed_at, reporting_unit_id, ...) - tabela ZOZów trzyma unikalne dla nich dane.
medical_practises (id, type, suspended_at, unsuspended_at, ...) - tabela praktyk_medycznych trzyma unikalne dla nich dane.
itd.
I tak model RepostingUnitModel (reporting, bo aplikacja służyła do mielenia danych ze specjalnych okresowych formularzy/raportów) miał kilka metod pozyskiwania:
ReportingUnitModel::getActiveUnits() - pobierane są tylko aktywne jednostki, a aktywność była liczona na podstawie zupełnie innych danych dla ZOZów i praktyk.
ReportingUnitModel::getMZ06RepostrByUnitId($id) - pobiera konkretny formularz, wypełniony przez jednostkę.
itd.
Ponieważ zrobiłem wywiad środowiskowy - wiedziałem, że nikt nie będzie korzystał z wielkich formularzy po 15 pól i nie będzie szukał po adresie na przykład - chciałem po goglowemu. I tak powstał problem szukajki - bo szukać można na podstawie kilku rodzajów danych w różnych tabelach np. po regonie w tabeli głównej, ale po numerze księgi w tabeli ZOZów - po wpisaniu ich w jedno pole (plus kilka checkboxów zaznaczających jaki rodzaj jednostki sie chce szukać).

Stworzyłem kolejny model, zupełnie oderwany od struktury bazy, SearchModel - był za to w moich oczach zgodny z logiką. Kilka setterów i metoda search(). W bebechach drzemał nadal poczciwy Propel i przy użyciu buildera szukałem po tabelach. Rozwiązanie działało, może nie do końca elegancko, ale banglało.

Czyli już teraz widać, że model nie reprezentuje tabeli DB 1:1. W dodatku niekoniecznie musi posiadać metody do wszystkiego, a tylko do Tego co potrzeba.

Co mogę jeszcze napisać. Że przy tym trybie projektowania nie boję się błędów oraz powyższa szukajka chodzi, na dzień dzisiejszy, na Zend_Searchu smile.gif Dopiero teraz jest eleganckie smile.gif Zmiana nastąpiła bez ani jednej linijki kodu w akcjach i widokach. Podmieniłem tylko model.
Całkiem niedawno miałem ciekawą rozmowę z kolegą i doszliśmy do wniosku, że gdyby się zawziąć mógłbym w modelach nawet zrezygnować z Zenda i działać bezpośrednio na Lucene poprzez java bridge.

Pozdrawiam, Alan

edit:
Cytat(wlamywacz @ 4.01.2009, 20:28:45 ) *
Mam pytanko, czy mógłbyś pokazać przykład jak wyglądało by odpowiednik mojego kodu w `Twoim` Propel-u ? [...]


To niczego nie dowodzi, trudno zrobić skomplikowaną aplikację gdzie nie zdarzy się zapytanie wklepane z palca. Normalka.
Natomiast wszelkie INSERTy, DELETEy, UPDATE są bajeczne, bo obiektowe.
wlamywacz
Cytat(LBO @ 4.01.2009, 21:52:10 ) *
To niczego nie dowodzi, trudno zrobić skomplikowaną aplikację gdzie nie zdarzy się zapytanie wklepane z palca. Normalka.
Natomiast wszelkie INSERTy, DELETEy, UPDATE są bajeczne, bo obiektowe.


A kto powiedział że u mnie nie ?

  1. <?php
  2. public function addPage($array) {
  3.    
  4.    return registry::get('db')->insert('subpages', $array);
  5.  
  6. }
  7.  
  8.  
  9. $pgs->addPage(array(
  10.            'id' => '',
  11.            'content' => $_POST['pageContent'],
  12.            'title' => $_POST['title'],
  13.            'url' => $url,
  14.            'save' => 0,
  15.            'editor' => $_POST['editor']
  16.            ))
  17. ?>
LBO
Cytat(wlamywacz @ 4.01.2009, 22:05:43 ) *
A kto powiedział że u mnie nie ?

  1. <?php
  2. public function addPage($array) {
  3.    
  4.    return registry::get('db')->insert('subpages', $array);
  5.  
  6. }
  7. $pgs->addPage(array(
  8.            'id' => '',
  9.            'content' => $_POST['pageContent'],
  10.            'title' => $_POST['title'],
  11.            'url' => $url,
  12.            'save' => 0,
  13.            'editor' => $_POST['editor']
  14.            ))
  15. ?>


To ja nie widze powodu dla którego się czepiasz tego jak Propel wykona to
  1. SELECT GROUP_CONCAT(`users`.`user` SEPARATOR ', ') AS `users`, `groups`.`name` AS `groupname`, `jobs`.`id` AS job_id, `jobs`.*, `ujobs`.*, `users`.* FROM `jobs` LEFT JOIN `ujobs` ON `jobs`.`id` = `ujobs`.`id_work` LEFT JOIN `users` ON `users`.`id` = `ujobs`.`user` LEFT JOIN `groups` ON `groups`.`id` = `jobs`.`group` WHERE `jobs`.`id` = $this->jobid GROUP BY `jobs`.`id`

zapytanie.. sciana.gif
wlamywacz
Cytat
To niczego nie dowodzi, trudno zrobić skomplikowaną aplikację gdzie nie zdarzy się zapytanie wklepane z palca. Normalka.
Natomiast wszelkie INSERTy, DELETEy, UPDATE są bajeczne, bo obiektowe.


To po cholerę używać czegoś tak dużego jak propel, skoro można to zrobić małą wydajną klasą ?
LBO
Cytat(wlamywacz @ 4.01.2009, 22:35:38 ) *
To po cholerę używać czegoś tak dużego jak propel, skoro można to zrobić małą wydajną klasą ?


@wlamywacz

Jeden, zasadniczy argument: Bo nie muszę żadnych klas pisać. A przy tym mam szereg przetestowanych i stabilnych fjuczerów, których okodowywać by mi się po prostu nie chciało.
wlamywacz
Cytat
Bo nie muszę żadnych klas pisać

Ale przecież w jakiś sposób trzeba mu zapodać strukturę tabel itd ?

Dobra LBO zakończmy te w sumie trochę przepychankę i dokończmy to na PM, GG itp. bo zaśmiecamy temat. snitch.gif
wrzasq
@pinochet: a mozesz mi pokazac fragment w ktorym powiedzialem, ze "klepie zapytania w PDO"? uzywam PDO jako klasy do komunikacji z baza jesli o to pytasz, ale to chyba raczej nie zbrodnia w PHP, chociaż mogę się mylić.

-edit-

nie ma sprawy winksmiley.jpg
pinochet
wstydnis.gif Sorki za pomyłkę nicka - post edytowany.

Z tego co napisał LBO odnośnie API modelu to widze, że też trzeba zachować zdrowy rozsądek i np wyszukiwarke zrobić właśnie w "modelu". Pytanko praktyczne: Muszę sobie zalogować użyszkodnika ...
  1. <?php //5.3
  2. $usr = ModelPeerUser::GetUserForLogin(ModelWejscie::Odebrane('login'),  ModelWejscie::Odebrane('haslo'));
  3. $this->model->setLoggedUser($usr);
  4. // czy...
  5. $this->model->Login(ModelWejscie::Odebrane('login'),  ModelWejscie::Odebrane('haslo'));
  6. ?>
Pierwsze rozwiązanie wydaje się ładniejsze ale w świetle mojej walki z referencjami (złe? nawyki z C++) to nie wiem czy jest ono dobre?
mike
Cytat(pinochet @ 5.01.2009, 14:10:02 ) *
Pierwsze rozwiązanie wydaje się ładniejsze ale w świetle mojej walki z referencjami (złe? nawyki z C++) to nie wiem czy jest ono dobre?
Co masz na myśli pisząc o referencjach?
LBO
Cytat(pinochet @ 5.01.2009, 14:10:02 ) *
$usr = ModelPeerUser::GetUserForLogin(ModelWejscie::Odebrane('login'),  ModelWejscie::Odebrane('haslo'));
$this->model->setLoggedUser($usr);
// czy...
$this->model->Login(ModelWejscie::Odebrane('login'),  ModelWejscie::Odebrane('haslo'));
?>[/php]Pierwsze rozwiązanie wydaje się ładniejsze ale w świetle mojej walki z referencjami (złe? nawyki z C++) to nie wiem czy jest ono dobre?


Jedno ale - nie zamykaj I/O w modelach. Na zdrowy rozsądek aplikacja powinna mieć dostępne specjalne w miarę ustandaryzowane obiekty Request/Response.

orass

Co widzisz złego w żonglowaniu obiektami? To jest bardzo eleganckie i niekiedy wydajne (vide wyciąganie obiektów Propela z routera w Symfony).
pinochet
Wydawało mi się to bardzo logiczne aby wszystkie dane wejściowe były w modelu. Jakie są przeciwskazania? Przeciez Request i Response mogą być w modelu... hipotetycznie(i pseudokodowo):
AkcjaResponse{
Header(Model::GetResponse());
}
Chociaż nie czytałem nic o Request/Response - musze się dokształcić jak to działa w dużych framweorkach.
@mike oczywiście Referencje w sensie C++ Nigdy nie jestem pewien kiedy obiekt jest kopiowany a kiedy przekazywany przez "referenje"
referencja => C++
"referencja" => PHP
LBO
Ej, ej, nie dajmy się zwariować - nie można wszystkiego podciągać pod modele - semantyka (to jak nazywasz obiekty) też jest bardzo ważna smile.gifsmile.gif

Pozdrawiam
mike
Cytat(pinochet @ 5.01.2009, 14:57:55 ) *
@mike oczywiście Referencje w sensie C++ Nigdy nie jestem pewien kiedy obiekt jest kopiowany a kiedy przekazywany przez "referenje"
referencja => C++
"referencja" => PHP
Nie wiem czego Ty tu nie wiesz. W PHP obiekty są zawsze przekazywane przez referencję i kropka.
W PHP5 oczywiście. No chyba, że jesteś msochistą i piszesz w PHP4.
pinochet
hehe musze to posprawdzać - co do tego przekazywania cos mi destruktory nie działały ale pewnie to moja wina.
Co do Semantyki napisałem - ale forum ucieło - nazwa obiektów jest w namespace: a wiec w moich powyzszych postach powinno byc np: $usr = \Model\PeerUser::GetUserForLogin(\Model\Wejscie::Odebrane('login'), \Model\Wejscie::Odebrane('haslo'));
wrzasq
@pinochet: imho na dodatek chyba nieodpowiednio uzywasz przestrzeni nazw. przestrzenie nazw powinny zaiwerac zbior ROZNYCH klas odnoszacych sie do JEDNEGO PRZYPADKU a nie ROZNYCH PRZYPADKOW tej samej KLASY. czyli nie robisz sobie przestrzeni Model i nie wrzucasz tam wszystkich modeli, tylko robisz przestrzen dla kazdego modelu i tam wrzucasz wszystkie zalezne klasy.

Cytat
hehe musze to posprawdzać - co do tego przekazywania cos mi destruktory nie działały

no tutaj wlasnie musisz na referencje uwazac tongue.gif. destruktory sa wykonywane po usunieciu WSZYSTKICH referencji do danego obiektu. wiec jesli przekazujesz gdzies obiekt (de facto przez referencje) to po zakonczeniu wykonywania danej funkcji nie zostanie wywolany jego destruktor (chyba ze to bylo jedyne jego miejsce istnienia).
pinochet
Co do sposobu wykorzystania przestrzeni to chyba leży w gestii programisty. Pierwszy styl nasunął mi się automatycznie i długo się nie zastanawiając zacząłem pisać. Na to twoje rozwiązanie wpadłem dopiero potem. Ale wydaje mi się że np model nie powinien Odwoływać się do innych warstw więc zamknięcie tego w namespace jest OK. np. mam
\Model\User
\Model\PeerUser
\Model\Item
\Model\PeerItem
\Controler\Action
a nie
\Item\Model
\Item\Action
\Item\View
\User\Model ...
Bo gdy potem w kontrolerze będziesz wykonywał Akcje to w której przestrzeni zaimplementujesz klase abstrakcyjną Akction questionmark.gif

edit: Dodatkowa zaleta jest taka ze mozna łatwo zmienic np cały model robiąc:
use MyProject\Model2 as MainModel;
wrzasq
osobiscie uzylbym system::common (czy tam MojaNazwa\Common), ale niewatpliwie - to kwestia programisty i jego podejscia smile.gif. dobra o czym to my... e... proponuje skonczyc ten offtopic winksmiley.jpg.
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-2025 Invision Power Services, Inc.