Bartosz Ratajczyk

Bartosz Ratajczyk MS SQL Developer

Temat: Modele

Jak implementujecie modele, w których dane są oparte o kilka tabel?

Przykładowo: jest post na blogu, który ma swoje tagi. Czy tworzycie kilka modeli, np.

class BlogPost {}
class Tags {}
class BlogPostTags {}

$tags = $oBlogPostTags->getTags($oBlogPost);

czy np. w klasie BlogPost tworzycie metodę, która zwróci tagi związane z Postem?, np:

class BlogPost {
public function getTags() {}
}

$tags = $oBlogPost->getTags();

Obecnie przeważnie stosuję to drugie podejście podejście z metodą wewnątrz modelu, czasem robię klasy pośrednie (przykładowe BlogPostTags), ale nie do końca wiem w którą stronę będzie lepiej się zdecydować.

Znacie jakieś źródła, które można poczytać w temacie modeli ZF? Standardowo znalazłem jedna klasa = odwzorowanie jednej tabeli, plus informacje o mapach referencji, ale przykładów jak najwygodniej używać modeli, które zahaczają o więcej niż jedną tabelę nie znalazłem.

konto usunięte

Bartosz Ratajczyk

Bartosz Ratajczyk MS SQL Developer

Temat: Modele

Tak, to były jedne z pierwszych wyników wyszukiwania jakie przejrzałem zanim napisałem na grupę, ale szukam czegoś więcej.

Chodzi mi o coś w rodzaju zestawu dobrych praktyk dotyczących modeli zahaczających o kilka tabel, chętnie z przykładami. W quickstarcie do modelu wykorzystują np. wzorzec DataMapper. Nie jestem oblatany we wzorcach i może są jakieś, które można w takich przypadkach wykorzystać.

A jak Ty tworzysz takie modele?

Temat: Modele

zrob sobie widok w ktorym zapisujesz zapytanie z joinami
w ormie wtedy nie interesuje cie co z czym sie laczy
chyba ze chcesz robic tak:
$obiekt->getTabelkaA()->getRekord(1)->getRelacja('inna_tabelka')->getRekord()
Bartosz Ratajczyk

Bartosz Ratajczyk MS SQL Developer

Temat: Modele

Nie chcę widoku. Pytam o budowanie klas modeli opartych o kilka tabel.
Jak wyciągać podawane w przykładzie tagi dla postu na blogu. Czy pisać klasę pośrednią, czy robić metodę pobierania tagów w klasie postu, czy oprzeć jednak na relacjach?
Powiedzmy, że potem do tego będę chciał dorzucić kategorie postu. Czy dorzucać metodę pobierania kategorii w klasie postu, czy robić oddzielną klasę i przez nią to obsługiwać?
Jak potem wyciągać posty przypisane do kategorii? Czy pisać metodę w klasie Kategoria(), czy pośredniej (np. BlogPostKategorie())

Potem będę chciał dorzucić np. flagi, potem jeszcze coś i jeszcze coś. Które podejście jest najbardziej optymalne, jakie stosować praktyki, żeby potem się nie okazało że utrzymanie kodu będzie ciężkie itd.

Jeśli przykład z blogiem przeszkadza, to pomyślcie np. o studentach, przedmiotach, wykładowcach. Czy do pobrania jacy studenci uczą się u jakiego wykładowcy powinno być w klasie Wykładowcy(), czy jakiejś pośredniej, etc.

Szukam opracowań pod tym kątem.
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Modele

A może przestań myśleć o modelach w kontekście table w bazie danych i zacznij jak o czymś co w kodzie odwzorowuje rzeczywistość, a zapis w bazie danych jest szczegółem implementacji obsługiwanym przez jakąś inną warstwę/klasę.
Bartosz Ratajczyk

Bartosz Ratajczyk MS SQL Developer

Temat: Modele

Wojciech Soczyński:
A może przestań myśleć o modelach w kontekście table w bazie danych i zacznij jak o czymś co w kodzie odwzorowuje rzeczywistość, a zapis w bazie danych jest szczegółem implementacji obsługiwanym przez jakąś inną warstwę/klasę.

Tak właśnie robię. Sposób zapisu zostawiam na boku. Chodzi mi o podejście do projektowania klas reprezentujących logikę biznesową. Czy z perspektywy późniejszego rozwoju lepiej np wyciągać te blogowe tagi z poziomu klasy postu, czy z innej klasy pośredniczącej. To, że oprócz wyciągania muszę te dane gdzieś zapisać zwalam na odrębną klasę dziedziczącą po Zend_Db_Table i do niej się odwołuję z klasy wyżej.

To może jeszcze inaczej zapytam. Jakie znacie warte uwagi opracowania dotyczące modelowania klas i jak to wykorzystać pod kątem ZF? [do komunikacji z bazami wykorzystuję Zend_Db_*, a nie Doctrine czy Propel (głównie dlatego że nie miałem dotąd potrzeby)]
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Modele

Bartosz Ratajczyk:
Wojciech Soczyński:
A może przestań myśleć o modelach w kontekście table w bazie danych i zacznij jak o czymś co w kodzie odwzorowuje rzeczywistość, a zapis w bazie danych jest szczegółem implementacji obsługiwanym przez jakąś inną warstwę/klasę.

Tak właśnie robię. Sposób zapisu zostawiam na boku. Chodzi mi o podejście do projektowania klas reprezentujących logikę biznesową. Czy z perspektywy późniejszego rozwoju lepiej np wyciągać te blogowe tagi z poziomu klasy postu, czy z innej klasy pośredniczącej. To, że oprócz wyciągania muszę te dane gdzieś zapisać zwalam na odrębną klasę dziedziczącą po Zend_Db_Table i do niej się odwołuję z klasy wyżej.

To może jeszcze inaczej zapytam. Jakie znacie warte uwagi opracowania dotyczące modelowania klas i jak to wykorzystać pod kątem ZF? [do komunikacji z bazami wykorzystuję Zend_Db_*, a nie Doctrine czy Propel (głównie dlatego że nie miałem dotąd potrzeby)]

Może zainteresuj się DDD - domain driven design, napisałem trochę o tym na swoim blogu
Artur Świerc

Artur Świerc Programista PHP/Java

Temat: Modele

Wojciech Soczyński:
Bartosz Ratajczyk:
Wojciech Soczyński:
A może przestań myśleć o modelach w kontekście table w bazie danych i zacznij jak o czymś co w kodzie odwzorowuje rzeczywistość, a zapis w bazie danych jest szczegółem implementacji obsługiwanym przez jakąś inną warstwę/klasę.

Tak właśnie robię. Sposób zapisu zostawiam na boku. Chodzi mi o podejście do projektowania klas reprezentujących logikę biznesową. Czy z perspektywy późniejszego rozwoju lepiej np wyciągać te blogowe tagi z poziomu klasy postu, czy z innej klasy pośredniczącej. To, że oprócz wyciągania muszę te dane gdzieś zapisać zwalam na odrębną klasę dziedziczącą po Zend_Db_Table i do niej się odwołuję z klasy wyżej.

To może jeszcze inaczej zapytam. Jakie znacie warte uwagi opracowania dotyczące modelowania klas i jak to wykorzystać pod kątem ZF? [do komunikacji z bazami wykorzystuję Zend_Db_*, a nie Doctrine czy Propel (głównie dlatego że nie miałem dotąd potrzeby)]

Może zainteresuj się DDD - domain driven design, napisałem trochę o tym na swoim blogu


Ja kiedyś znalazłem coś takiego - http://blog.fedecarg.com/2009/03/22/domain-driven-desi...

Widzę tam encje, dao i ogólnie wykorzystanie wzorca DataMapper, ale nie wiem czy to jest rozwiązanie problemu jaki zgłaszał Bartek. Z jednej strony wszystko fajnie czytelnie, ale z drugiej boję się o wydajność i też co z joinami?

Czy przy tak zaprojektowanym modelu można jakoś wydajnie łączyć tabele pomijając widoki w bazie? Załóżmy że chcę jeszcze wyciągać dla Usera adresy albo komentarze, wtedy tworzę metodę w najwyższej warstwie modelu, która łączy mi usera z komentarzami - ale czy wtedy nie idą dwa zapytania zamiast jednego zwykłego join'a?
Bartosz Ratajczyk

Bartosz Ratajczyk MS SQL Developer

Temat: Modele

Wojciech Soczyński:
Może zainteresuj się DDD - domain driven design, napisałem trochę o tym na swoim blogu

Dzięki za link. Poczytałem trochę o DDD u Ciebie jak i na Fedecarg (dzięki Artur). Wychodzi mi po tym, że parafrazując - przez cały czas nie wiedziałem że mówię prozą. Właściwie to co tam jest opisane znam od jakiegoś czasu, kiedy wychodzi mi w praniu jakieś projektowanie. Niemniej pozwala uporządkować posiadaną wiedzę i dzięki linkom wiem gdzie szukać dalej.

Nadal jednak nie jestem pewien, które podejście może być lepsze. Niby na Fedecarg jest opis, który można przełożyć na $oBlog->getTags(), czyli pod encję (?) Bloga daję metodę wyciągania tagów. Tylko potem trochę mi się to kłóci z DataMapperem, ale może czegoś jeszcze nie dostrzegam.

A może jak Waszym zdaniem byłoby lepiej podejść do tematu? Jak byście zaprojektowali modele dla przykładowego wyciągania tagów i np. kategorii dla wpisu na blogu pod ZF?

Na razie sam się skłaniam do stosowanego rozwiązania $oBlog->getTags().
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Modele

Myślę, że BlogPost->getTags() będzie ok, natomiast jeżeli chodzi o DDD to nie powinno się traktować Encji jako tylko pojemnika na gettery i settery. Jeżeli oczywiście chcesz tworzyć zgodnie z tą metodyką.

Jest jeszcze kwestia taka, że podejście generalnie zależy od skali problemu, myślę, że jeżeli ma to być prosty blog bez jakiegoś wielkiego biznesowego skomplikowania to takie podejście wystarczy, natomiast jak coś więcej to już się trzeba głębiej zastanowić :>Wojciech Soczyński edytował(a) ten post dnia 12.08.10 o godzinie 09:33
Bartosz Ratajczyk

Bartosz Ratajczyk MS SQL Developer

Temat: Modele

Wojciech Soczyński:
Myślę, że BlogPost->getTags() będzie ok, natomiast jeżeli chodzi o DDD to nie powinno się traktować Encji jako tylko pojemnika na gettery i settery. Jeżeli oczywiście chcesz tworzyć zgodnie z tą metodyką.

Im więcej czytam o DDD, Domain Model etc tym bardziej się ku temu skłaniam (zresztą tak staram się robić). Mam mieszane odczucia co do anemicznego modelu domeny (może jeszcze nie do końca rozumiem wzorce i mi się mieszają).

O co chodzi - patrząc na quickstart ZF (http://framework.zend.com/manual/en/learning.quickstar... model danych opiera się o DataMapper, który korzysta z modelu mającego same gettery i settery. Dla mnie jest to potraktowanie klasy jako odwzorowania wiersza tabeli danych, którym manipuluje inna klasa. Według DDD powinienem metody z DataMappera wrzucić do obiektu odwzorowującego wiersz tabeli?
Jest jeszcze kwestia taka, że podejście generalnie zależy od skali problemu, myślę, że jeżeli ma to być prosty blog bez jakiegoś wielkiego biznesowego skomplikowania to takie podejście wystarczy, natomiast jak coś więcej to już się trzeba głębiej zastanowić :>

Blog to tylko przykład na omówienie zagadnienia. Może być równie dobrze pobieranie kluczowych klientów banku, gdzie Klient() to jedna klasa, a za flagowanie klientów odpowiada klasa Atrybuty(). Czy coś w ten deseń.
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Modele

Bartosz Ratajczyk:
Wojciech Soczyński:
Myślę, że BlogPost->getTags() będzie ok, natomiast jeżeli chodzi o DDD to nie powinno się traktować Encji jako tylko pojemnika na gettery i settery. Jeżeli oczywiście chcesz tworzyć zgodnie z tą metodyką.

Im więcej czytam o DDD, Domain Model etc tym bardziej się ku temu skłaniam (zresztą tak staram się robić). Mam mieszane odczucia co do anemicznego modelu domeny (może jeszcze nie do końca rozumiem wzorce i mi się mieszają).

O co chodzi - patrząc na quickstart ZF (http://framework.zend.com/manual/en/learning.quickstar... model danych opiera się o DataMapper, który korzysta z modelu mającego same gettery i settery. Dla mnie jest to potraktowanie klasy jako odwzorowania wiersza tabeli danych, którym manipuluje inna klasa. Według DDD powinienem metody z DataMappera wrzucić do obiektu odwzorowującego wiersz tabeli?
Jest jeszcze kwestia taka, że podejście generalnie zależy od skali problemu, myślę, że jeżeli ma to być prosty blog bez jakiegoś wielkiego biznesowego skomplikowania to takie podejście wystarczy, natomiast jak coś więcej to już się trzeba głębiej zastanowić :>

Blog to tylko przykład na omówienie zagadnienia. Może być równie dobrze pobieranie kluczowych klientów banku, gdzie Klient() to jedna klasa, a za flagowanie klientów odpowiada klasa Atrybuty(). Czy coś w ten deseń.

Anemiczny model domeny to sytuacja kiedy nasze Encje zawierają tylko gettery i settery - są tylko pojemnikami na dane, natomiast nie zawierają żadnych metod biznesowych. Metody biznesowe są to konkretne funkcjonalności, które ma realizować nasza aplikacja.

W Zendowym tutorialu wszystko w zasadzie jest ok:
mamy datamapper, który zajmuje się "technicznymi" kwestiami związanymi z obsługą Encji. Natomiast w Encjach z tutoriala brakuje mi konkretnych metod biznesowych, które będą coś konkretnego realizować. Dlatego ja bym ten tutorial potraktował jako dobry punkt wyjścia do dalszego rozwijania modelu i następnym kroku po tutorialu zaczął bym dodawać owe biznesowe metody.

Tak dla przykładu załóżmy, że mam w jakiejś aplikacji obiekt Użytkownika, klient zażyczył sobie, żeby użytkownik mógł mieć możliwość zmiany swojego hasła, oraz żeby go można było aktywować za pomocą hasha który otrzymuje się w mailu i deaktywować, te trzy funkcjonalności związane z użytkownikiem będą więc biznesowymi metodami klasy User.


class User {
function changePassword($newPassword);
function activate($hash);
function deactivate();
}
Wojciech Soczyński edytował(a) ten post dnia 12.08.10 o godzinie 13:38
Piotr Krajewski

Piotr Krajewski web application
programmer

Temat: Modele

Witajcie,

od niedawna korzystam z ZF i mam ten sam problem co kolega Bartosz Ratajczyk. Po przeczytaniu tego wątku pomału zaczyna się wszytsko klarować.

z tymże:

Tak dla przykładu załóżmy, że mam w jakiejś aplikacji obiekt Użytkownika, klient zażyczył sobie, żeby użytkownik mógł mieć możliwość zmiany swojego hasła, oraz żeby go można było aktywować za pomocą hasha który otrzymuje się w mailu i deaktywować, te trzy funkcjonalności związane z użytkownikiem będą więc biznesowymi metodami klasy User.


czy owe metody nie powinny być w konkretnym kontrolerze User a nie w modelu?
Artur Świerc

Artur Świerc Programista PHP/Java

Temat: Modele

Wojciech Soczyński:
Anemiczny model domeny to sytuacja kiedy nasze Encje zawierają tylko gettery i settery (...)

class User {
function changePassword($newPassword);
function activate($hash);
function deactivate();
}
Wojciech Soczyński edytował(a) ten post dnia 12.08.10 o godzinie 13:38

Też staram się pojąc czym jest do końca Anemiczny Model i dlaczego powinno się tego unikać.
Zawsze myślałem, że encja User ma posiadać tylko gettery/settery, a metody biznesowe umieszczamy w innej warstwie - coś jak UserRepository w linku przeze mnie podanym (?).
Bartosz Ratajczyk

Bartosz Ratajczyk MS SQL Developer

Temat: Modele

Piotr Krajewski:
>
czy owe metody nie powinny być w konkretnym kontrolerze User a nie w modelu?

Nie. To są metody operujące na użytkowniku i powinny się znajdować w modelu. Kontroler powinien tylko wywołać tą metodę.
Wojciech Soczyński


class User {
function changePassword($newPassword);
function activate($hash);
function deactivate();
}

Czyli powiedzmy z jednej strony UserMapper(), który będzie wyszukiwał i zapisywał dane, np.

class UserMapper {
function findById($id);
function save(User $user);
(...)
}


który operuje na klasie User(), a w klasie User() oprócz getterów/setterów kilka dodatkowych funkcji (niepowiązanych z samą bazą) manipulujących właściwościami obiektu klasy User?
Co do przykładowych powyższych metod to akurat chyba niespecjalnie odbiegają od getterów/setterów, ale powiedzmy pobranie komentarzy użytkownika w serwisie do klasy User można podpiąć? Wtedy powiedzmy mamy ten UserMapper() i User():


class User {
protected $_id;
protected $_login;
protected $_password;
protected $_comments;
...

public function getLogin()
{
return $this->_login;
}
...

public function getComments()
{
$comments = new Comments();
$this->comments = $comments->getComments($this->getId());
}
}


W dobrą stronę kombinuję?
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Modele

Bartosz Ratajczyk:
Piotr Krajewski:
>
czy owe metody nie powinny być w konkretnym kontrolerze User a nie w modelu?

Nie. To są metody operujące na użytkowniku i powinny się znajdować w modelu. Kontroler powinien tylko wywołać tą metodę.
Wojciech Soczyński


class User {
function changePassword($newPassword);
function activate($hash);
function deactivate();
}

Czyli powiedzmy z jednej strony UserMapper(), który będzie wyszukiwał i zapisywał dane, np.

class UserMapper {
function findById($id);
function save(User $user);
(...)
}


który operuje na klasie User(), a w klasie User() oprócz getterów/setterów kilka dodatkowych funkcji (niepowiązanych z samą bazą) manipulujących właściwościami obiektu klasy User?
Co do przykładowych powyższych metod to akurat chyba niespecjalnie odbiegają od getterów/setterów, ale powiedzmy pobranie komentarzy użytkownika w serwisie do klasy User można podpiąć? Wtedy powiedzmy mamy ten UserMapper() i User():


class User {
protected $_id;
protected $_login;
protected $_password;
protected $_comments;
...

public function getLogin()
{
return $this->_login;
}
...

public function getComments()
{
$comments = new Comments();
$this->comments = $comments->getComments($this->getId());
}
}


W dobrą stronę kombinuję?

Tak kombinujesz w dobrą stronę, żeby rozjaśnić nazewnictwo - UserMapper to repozytorium - odpowiada za utrwalanie i wyszukiwanie encji.

User to Encja - rozróżnialny byt, który ma w sobie metody biznesowe, które odnoszą się do tego bytu, nie koniecznie muszą operować na jego właściwościach. Wchodząc w jakąś głębszą abstrakcję jak projektowalibyśmy grę polegającą na kopaniu rowów to taką metodą biznesową klasy Worker/User była by np. metoda "kop" - która w zasadzie nie modyfikuje, żadnych pól obiektu klasy User, natomiast jest do niego przynależna bo jest działaniem które ten User wykonuje.

Metody, które nie da się logicznie powiązać z żadną Encją grupujemy w Usługi (services). Jedną z takich usług może być np usługa raportowania, czy tworzenia wykresów, masowy mailing etc.

A co do anemicznego modelu to powinno się go unikać ponieważ tworząc model anemiczny tak naprawdę nie programujemy obiektowo tylko proceduralnie. Podstawą programowania obiektowego jest powiązanie struktur danych z metodami na tych danych operującymi. Natomiast jeżeli Encja posiadała by tylko gettery i settery to defakto była by tylko strukturą danych, a klasa usługowa tak naprawdę składała by się z statycznych metod - była by tylko namespace'em dla powiązanych ze sobą funkcji.Wojciech Soczyński edytował(a) ten post dnia 12.08.10 o godzinie 14:54
Bartosz Ratajczyk

Bartosz Ratajczyk MS SQL Developer

Temat: Modele

Dzięki wielkie. Sporo mi się rozjaśniło.
Wojciech Soczyński

Wojciech Soczyński Programista
eksplorator -
blog.wsoczynski.pl

Temat: Modele

Dla większej jasności polecam wgłębić się w linki, które u siebie umieściłem, jest tam jeszcze szerzej wszystko wyjaśnione i są przykładowe aplikacje.

konto usunięte

Temat: Modele

Do niedawna w ZF były rozwijane elementy Zend_Entity & Zend_Db_Mapper, które by prawdopodobnie sprostały twoim wymaganiom, ale zostały wycofane, bo zaczęli integrować Doctrine 2.0.
Zobaczymy co ZF 2.0 przyniesie:)
Pozdrawiam.Yuriy Kisil edytował(a) ten post dnia 12.08.10 o godzinie 15:32



Wyślij zaproszenie do