Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: MVC - klasy modelu - pojedyncze obiekty i listy
Forum PHP.pl > Forum > PHP > Object-oriented programming
marcini82
Witam!

Pytanie dotyczy klas modelu w rozumieniu wzorca MVC, a ma zastosowanie chyba do prawie kazdego rodzaju danych przechowywanych w bazie. Otoz jesli mamy jakiekolwiek obiekty (uzytkownikow, ogloszenia, wiadomosci, itp.), to zazwyczaj istnieje potrzeba przeprowadzania operacji na pojedynczym obiekcie (dodanie, usuniecie, edycja itp.) oraz na grupie obiektow, spelniajacych okreslone kryteria (np. wyswietlenie listy uzytkownikow, ogloszen itp.). Jaka tworzycie tutaj strukture klas?
Czy robicie dwie klasy, z ktorych jedna reprezentuje pojedynczy obiekt a druga liste takich obiektow (np. Uzytkownik i ListaUzytkownikow)? A moze jedna klasa (np. Uzytkownik) posiada metody obslugujace obie mozliwosci, np. pobierz($id) dla pojedynczego obiektu i pobierzListe() dla calej listy obiektow? A moze jeszcze inaczej?

EDIT: Wlasciwie to ten temat powinienem dac do PHP > Programowanie obiektowe. Moze moderator zechcialby go przeniesc?
strife
Przenoszę do PHP > Programowanie obiektowe
Strzałek
Zobacz sobie model w Cake, poczytaj o ActiveRecord.

Ja mam klasę NewsContainer, z metodami findById, findByTitle, findAll etc.
Athlan
Strzałek dobrze prawi imho... model users powinien obsługiwać metody takie jak findAll, get($mId, $bByName = false), setEmail(), setParams( jakies dane usera ) itp itd dotyczącego usera. Mogę zarzucić przykład z mojego modelu obsługującego filmiki w bazie danych (skomny, ale przedstawi działanie):

  1. <?php
  2.  
  3. class movies_Vmodel extends Vmodel
  4. {
  5. public function getLastModified($iTime)
  6. {
  7. $oDb = $this->getSource();
  8.  
  9. $oDb->setOrder("movie_time_edit ASC");
  10. $oDb->setWhere("movie_time_edit > " . (time() - $iTime));
  11.  
  12. $rResult = $oDb->dbSelect('movies', true, array('users' => 'movie_owner = user_id'));
  13.  
  14. $oDb->clear();
  15.  
  16. return $oDb->fetchAll($rResult);
  17. }
  18.  
  19. function getUserMovies($iUser)
  20. {
  21. $oDb = $this->getSource();
  22.  
  23. $oDb->setWhere("movie_owner = " . (int)$iUser);
  24.  
  25. $rResult = $oDb->dbSelect('movies', true);
  26.  
  27. $oDb->clear();
  28.  
  29. return $oDb->fetchAll($rResult);
  30. }
  31.  
  32. function setMovie($iUser, $sDescription, $sCode)
  33. {
  34. $oDb = $this->getSource();
  35.  
  36. $oDb->movie_owner = $iUser;
  37. $oDb->movie_text = $sDescription;
  38. $oDb->movie_code = $sCode;
  39. $oDb->movie_time = time();
  40. $oDb->movie_time_edit = time();
  41.  
  42. $rResult = $oDb->dbInsert('movies');
  43.  
  44. if($rResult)
  45. $oDb->Execute("UPDATE users SET user_count_movies = user_count_movies + 1 WHERE user_id = " . $iUser);
  46.  
  47. $oDb->clear();
  48. }
  49.  
  50. function getMovie($iId)
  51. {
  52. $oDb = $this->getSource();
  53.  
  54. $oDb->setWhere("movie_id = " . (int)$iId);
  55.  
  56. $rResult = $oDb->dbSelect('movies', true, array('users' => 'movie_owner = user_id'));
  57.  
  58. $oDb->clear();
  59.  
  60. return $oDb->fetch(1, $rResult);
  61. }
  62.  
  63. function unMovie($iId, $aData)
  64. {
  65. $oDb = $this->getSource();
  66.  
  67. $oDb->setWhere("movie_id = " . (int)$iId);
  68.  
  69. $rResult = $oDb->dbDelete('movies');
  70.  
  71. $oDb->clear();
  72.  
  73. $oDb->Execute("UPDATE users SET user_count_movies = user_count_movies - 1 WHERE user_id = " . $aData['movie_owner']);
  74.  
  75. return $rResult;
  76. }
  77.  
  78. function setMovieDesc($iId, $sDesc)
  79. {
  80. $oDb = $this->getSource();
  81.  
  82. $oDb->setWhere("movie_id = " . (int)$iId);
  83. $oDb->movie_text = substr($sDesc, 0, 40);
  84. $oDb->movie_time_edit = time();
  85.  
  86. $rResult = $oDb->dbUpdate('movies');
  87.  
  88. $oDb->clear();
  89.  
  90. return $rResult;
  91. }
  92. }
  93.  
  94. ?>
Strzałek
Trochę mało wygodnie to masz. Przy tabelach które nie potrzebują joinów u mnie wystarczy

  1. <?php
  2.  
  3.  class NewsContainer extends DatabaseMapper {
  4.  public $table = "news";
  5.  }
  6.  
  7. ?>


I juz mam wszystko czego potrzebuję

  1. <?php
  2.  
  3. $news = new NewsContainer();
  4.  
  5. $news -> findAll();
  6. //lub
  7. $fields = array('id', 'title', 'time', 'comments', 'intro');
  8. $criteria = array('sort_by' => 'id', 'sort_type' => 'DESC', 'limit' => '10');
  9.  
  10. $news -> findAll($fields, $criteria);
  11.  
  12.  
  13. $news -> findById($id);
  14. $news -> findByTitle('Tytuł newsa');
  15.  
  16. $news -> deleteById($id);
  17.  
  18. $nowyNews = array('title' => 'Super tutuł', 'text' => 'tresc', 'time' => time());
  19.  
  20. $news -> save($nowyNews);
  21.  
  22. ?>


Proste prawda smile.gif ?

Do każdej rzeczy będziesz teraz pisał findAll, findBy(.*), ja u siebie mam abstrakcyjna klasę modelu, która mam podstawowe metody - find, save, update, delete + dodatkowo, __call które obsługuje findByTitle odpalając metodę find z odpowiednimi argumentami. Taki sposobem powstał mały DatabaseMapper. Bez żadnych wodotrysków i zapytań z joinami. Jeżeli potrzebuję do NewsContainer'a łaczyć tabele, dopisuje sobie wtedy metodę i recznie piszę zapytanie.

No chyba że mam tego więcej. Wtedy mamy Propel lub PHP Doctrine
Athlan
@Strzałek, owszem, przyznam się, że obsługa bazy danych to kicha u mnie tongue.gif mi tam wygodnie jest, ale w przykladze chodzilo głownie o to, aby pokazać możliwości modelu (filmy to filmy itp).

Co do tej obsługi... w Rapide budowana jest całkiem nowa już smile.gif
marcini82
Dzieki za odpowiedzi. Jak sobie czytam o ActiveRecord to widze, ze to jest mniej wiecej to, co od jakiegos czasu zazwyczaj stosowalem, nie nazywajac tego w ten sposob smile.gif

@Strzalek - ciekawa koncepcja tego DatabaseMapper. Wlasnie mysle, czy da sie stworzyc cos tak uniwersalnego dla projektu nad ktorym siedze :-)
Turgon
Strzałek: Mam kilka wątpliwości. Otóż też napisałem takiego podobnego DatabaseMappera, który ładuje na podstawie takiej:
load(fieldName,fieldValue);
I tak sie zastanawiam, czy to ma sens w takiej formie ? Bo wtedy Obiekt, który należy do grupy TurActiveRecord i jest odpalany przez fabrykę. Ma tą zaletę, że nie trzeba się bawić w rozdział mapperów dla baz?
Strzałek
Nie wiem o co Ci chodzi. Za dużo filozofii i sztuki dla sztuki widzę u Ciebie. Mam Controller w którym jak potrzebuję to tworzę sobie nowy obiekt np. NewsContainer. Jak nie potrzeba to nie tworzę i tyle.
Turgon
O może tak. Mam taki problem, że u mnie Database Mapper działa tylko dla jednego obiektu. Jak proponujesz to przerobić by pracowało z wieloma rekordami? Jak zrobić hasOne i hasMany ? Tylko tyle mi potrzeba smile.gif?
Strzałek
Aaaa, hehe. Napisałem wyżej że ominąłem relacje. Można je napisać i nie jest to wcale trudne. Po prostu przy find(.*) musisz dodatkowo obsłużyć relacje. Dodajesz właściwości $hasMany, $hasOne np.:

  1. <?php
  2.  class NewsContainer extends DatabaseMapper {
  3.  public $table = 'news';
  4.  public $hasOne = 'author';
  5.  }
  6. ?>


Dalej automatycznie się wszystko dzieje.
Turgon
Eeee... Poprosiłbym jakieś dokładniejsze przykłady ? Dla mnie trzeba łopatologicznie. Najchętniej bym poczytał coś w stylu artykułów o DBObject Hwaka smile.gif
marcini82
Nie do konca Turgon rozumiem, w czym tkwi twoj problem, i co ma oznaczac to $hasOne i $hasMany... Jesli NewsContainer ma okreslona tabele news to w DatabaseMapper budujesz uniwersalne zapytanie w oparciu o te tabele i dostajesz zestaw rekordow...
Moze gdybys wytlumaczyl tak lopatologicznie, na jakims przykladzie, to ktos bylby w stanie ci pomoc.
Strzałek
Chodzi o relacje. Tabele buduje się relacyjnie. Np.

News
id
title
text
author

Comments
id
news_id
text

Users
id
login
pass

I w tabeli news trzymamy w polu author, numer id usera, natomiast w comments trzymamy komentarze gdzie w news id znajduje się id newsa do którego dodano komentarz. Żeby coś takiego uzyskać są potrzebne relacje has.... To tak w telegraficznym skrócie. Ale to już wyższa szkoła jazdy. W swoim czasie się dowiesz o co chodzi.
marcini82
Cytat
Chodzi o relacje.

A no to w takim razie ja bym zrobil cos takiego:
  1. <?php
  2. class NewsContainer extends DatabaseMapper {
  3.  public $table = 'news';
  4.  public $joins = Array('author' => 'Users.id');
  5.  }
  6. ?>

Jak mamy w $joins okreslone kolumny na ktorych robimy zlaczenia to mozemy to wykorzystac w uniwersalnej metodzie DatabaseMappera. A jak trzeba cos bardziej wyrafinowanego wyciagnac niz proste zlaczenie 2-3 tabel to lepiej w NewsContainer nadpisac metode z DatabaseMapper i wklepac nowe zapytanie.
Wlasciwie to ja bym tego nie nazwal wyzsza szkola jazdy bo to raczej podstawy relacyjnych baz danych snitch.gif
Napisanie dobrej i uniwersalnej struktury klas to juz co innego...

Sposobu praktycznego wykorzystania tego $hasOne nadal nie zalapalem, ale to moze nie jest takie istotne...
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.