Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Początki z programowaniem obiektowym - klasa artykuł
Forum PHP.pl > Inne > Oceny
czarek1986
Witam

Od jakiegoś czasu interesuje się programowaniem obiektowym, ale od niedawna zdecydowałem się stworzyć coś własnego. Będzie to mój pierwszy obiektowy (przynajmniej w części) CMS. chciałbym przedstawić wam klasę artykułu do oceny. Prosze o konstruktywną krytykę smile.gif

  1. <?php
  2.  
  3. class Artykul {
  4.  
  5. //zawiera unikalny numer identyfikujący artykuł w bazie danych
  6. private $id;
  7. //zawiera nazwę autora tworzacego artykuł
  8. private $autor;
  9. //zawiera nazwę kategorii do której przyporządkowany jest artykuł
  10. private $kategoria;
  11. //zaiwera tytuł artykułu
  12. private $tytul;
  13. //zawiera wstępny tekst artykułu
  14. private $naglowek;
  15. //zawiera główną treść artykułu
  16. private $tekst_artykulu;
  17. //zawiera datę utworzenia artykułu !!Unixowy znacznik czasu
  18. private $utworzony;
  19. //zawiera datę ostatniej modyfikacji artykułu !!Unixowy znacznik czasu
  20. private $zmodyfikowany;
  21. //zawiera datę ostatniej publikacji artykułu !!Unixowy znacznik czasu
  22. private $opublikowany;
  23. //określa czy artykuł jest nowo utworzony, czy został wczytany z bazy danych
  24. private $new;
  25.  
  26.  
  27.  
  28. public function __construct($id=NULL)
  29. {
  30. if($id)
  31. {
  32. $this->id_art = $id;
  33. $db = Database::GetInstance();
  34. $sql = "SELECT *
  35. FROM j_artykuly
  36. WHERE id = $id";
  37.  
  38. $art = $db->select($sql);
  39.  
  40. if(!$art)
  41. throw new Exception('Wybrany artykuł nie istnieje');
  42.  
  43. $this->new = false;
  44. $this->set($art[0]);
  45.  
  46. }
  47. else
  48. $this->new = true;
  49.  
  50. }
  51.  
  52. /**
  53. * Metoda zwraca wartość jednego wybranego pola klasy
  54. *
  55. * @param name - nazwa pola w klasie, której wartość chcemy uzyskać
  56. **/
  57.  
  58. public function getOne($name)
  59. {
  60. if(property_exists(get_Class($this), $name))
  61. return $this->$name;
  62. else
  63. throw new Exception('Klasa '.get_class($this).' nie posiada pola '.$name);
  64. }
  65.  
  66. /**
  67. *
  68. * Metoda zwraca wartości wszystkich pól klasy
  69. *
  70. * @return tablica z danymi wszystkich pól;
  71. *
  72. **/
  73.  
  74. public function getAll()
  75. {
  76.  
  77. $arData = array(
  78. 'id' => $this->id,
  79. 'autor' => $this->autor,
  80. 'kategoria' => $this->kategoria,
  81. 'tytul' => $this->tytul,
  82. 'naglowek' => $this->naglowek,
  83. 'tekst_artykulu' => $this->tekst_artykulu,
  84. 'utworzony' => $this->utworzony,
  85. 'zmodyfikowany' => $this->zmodyfikowany,
  86. 'opublikowany' => $this->opublikowany,
  87. );
  88.  
  89. return $arData;
  90. }
  91.  
  92.  
  93. /**
  94. *
  95. * Metoda służy do ustawienia wartości pól w klasie
  96. *
  97. [email="*@Param"]*@Param[/email] arData- tablica przechowująca nazwy pól i odpowiadające im wartości
  98. *
  99. **/
  100.  
  101. public function set(array $arData)
  102. {
  103. foreach($arData as $key => $value)
  104. {
  105. if($key == 'id' && $this->new)
  106. throw new Exception("Wartość pola 'id' może być nadana tylko automatycznie");
  107.  
  108. if(property_exists(get_Class($this), $key))
  109. {
  110. $this->$key = $value;
  111. }
  112. else
  113. throw new Exception('Klasa '.get_class($this).' nie posiada pola '.$key);
  114. }
  115. }
  116.  
  117.  
  118.  
  119. /**
  120. *
  121. * Metoda Tworząca artykuł w bazie danych
  122. *
  123. **/
  124.  
  125. public function createArt()
  126. {
  127. if($this->new)
  128. {
  129. $this->checkRequiredData();
  130.  
  131. $db = Database::getInstance();
  132. if($db->Insert('j_artykuly', $this->getAll()) > 0);
  133. return true;
  134. }
  135.  
  136. throw new Exception('Bierzący artykuł już istnieje w bazie danych. Można go tylko zaktualizować');
  137. }
  138.  
  139.  
  140. /**
  141. *
  142. * Metoda aktualizująca dane artykułu w bazie danych
  143. *
  144. **/
  145.  
  146. public function updateArt()
  147. {
  148. if(!$this->new)
  149. {
  150. $this->checkRequiredData();
  151.  
  152. $db = Database::getInstance();
  153. $arConditions['id']=$this->id;
  154. if($db->Update('j_artykuly', $this->getAll(), $arConditions) > 0);
  155. return true;
  156. }
  157.  
  158. throw new Exception('Musisz najpierw utworzyć artykuł w bazie danych aby móc go edytować');
  159. }
  160.  
  161.  
  162. /**
  163. *
  164. * Metoda usuwająca artykuł z bazy danych
  165. *
  166. **/
  167.  
  168. public function delArt()
  169. {
  170. if(!$this->new)
  171. {
  172. $this->checkRequiredData();
  173.  
  174. $db = Database::getInstance();
  175. $arWhere = array("id = ".$this->id);
  176. if($db->delete('j_artykuly', $arWhere) > 0)
  177. return true;
  178. }
  179.  
  180. throw new Exception('Nie można usunąć bierzącego artykułu, ponieważ nie został jeszcze zapisany w bazie danych.');
  181. }
  182.  
  183.  
  184. /**
  185. *
  186. * Metoda sprawdzająca czy pola, które nie mogą być puste w bazie danych, są wypełnione danymi
  187. *
  188. **/
  189.  
  190. private function checkRequiredData()
  191. {
  192. $arData = array();
  193. $arData = $this->getAll();
  194. $arEmpty = array();
  195. foreach($arData as $key => $value)
  196. {
  197. if($key=='autor' || $key=='kategoria' || $key=='tytul' || $key=='naglowek' || $key=='utworzony')
  198. {
  199. if(!$value)
  200. $arEmpty[]=$key;
  201. }
  202. }
  203.  
  204. if(count($arEmpty) > 0)
  205. {
  206. $empty = implode(', ', $arEmpty);
  207. throw new Exception('Wymagane pola '.$empty.' są puste.');
  208. }
  209.  
  210. return true;
  211. }
  212.  
  213.  
  214. }
  215.  
  216. ?>
golaod
Bezsensowna sprawa jak dla mnie. Po pierwsze gdzie tu przemyślenie i trzymanie się jednego sposobu pisania metod ? Skoro getOne w ładny sposób czyta czy istnieje zmienna którą ktoś podał w parametrze i ją zwraca to w getAll powinna pętle przechodzić po wszystkich zmiennych klasowych i je dodawać do tablicy zamiast ręcznie rozpisujesz - podstawa to konsekwencja. Po drugie co to jest za klasa ? to kontroler, model, jakieś pudełko czy co ? Rzucanie wyjątkami na lewo i prawo jest trochę dziwne w tym wypadku. Moim zdaniem to powinno służyć tylko do wywalania krytycznych błędów lub czegoś nie do obsłużenia. W przypadku braku zmiennej coś takiego wyrzucać ? Poza tym klasa ma możliwość dodania artykułu pod warunkiem, że przy instancjonowaniu podamy produkt już ISTNIEJĄCY. A jak baza jest pusta to nie dodamy produktu bo się nam obiekt nie utworzy ? Można by się jeszcze poczepiać tylko brakuje mi czasu.
czarek1986
Cytat
Poza tym klasa ma możliwość dodania artykułu pod warunkiem, że przy instancjonowaniu podamy produkt już ISTNIEJĄCY. A jak baza jest pusta to nie dodamy produktu bo się nam obiekt nie utworzy ?


Mozna dodawać także i nowe jeszcze nie zapisane do bazy artykuły. Do tego służy metoda CreateArt, która wykona się tylko jeśli wartość pola "new" jest równa true. Jest ono ustawiane własnie podczas tworzenia obiektu w konstruktorze. Jeśli podamy w nim id to obiekt bedzie utworzony na podstawie danych z bazy, a jeśli nie to bedzioe oznaczone własnie jako nowy.

Co do nadmiernych wyjątków, myslisz, że lepiej będzie zwracać po prostu false i dopiero przy uzywaniu takiego obiektu i zwróceniu false odpowiednio wyświetlić komunikat lub wyrzucić wyjątek?
thek
A po co dodatkowy parametr new? ID w zupełności go zastępuje smile.gif W końcu jeśli jest tam NULL, to spełnia on tę samą rolę. Sprawdzasz więc jedynie jaką wartość ma ID i w razie czego podejmujesz decyzję czy robić UPDATE czy INSERT w bazie. To sprawia, że createArt i updateArt możesz zastąpić jedną metodą. Konstruktor w zasadzie działałby podobnie. Brak id -> nowy z wartościami domyślnymi, prawidłowe id -> pobiera artykuł, nieprawidłowe id -> nowy + error. Kwestia funkcji do wyświetlania wszystkiego jest tak naprawdę sporna. Można użyć funkcji getOne, ale używanie funkcji w funkcji to zawsze minimalny narzut czasowy przy wykonywaniu. Więc patrząc od strony wydajności odwołanie się prosto do obiektu pozwala na ominięcie tworzenia zmiennych tymczasowych, odwołania do funkcji, powrotu do funkcji macierzystej, niszczenie zmiennych tymczasowych na stosie itp. Nie jest więc takie głupie jak się może wydawać bo oszczędza nam zarówno czas jak i pamięć serwera. Poza tym popatrz na getOne... Wywołujesz w niej za każdym razem funkcje i warunki. To spowalniałoby getAll za każdym razem.
Zastanawiałbym się także nad kategoria i tagi. Wszystko zależnie od tego jak masz zamiar rozwiązać to od strony bazy. Do przemyślenia jest bowiem choćby to, czy artykuł może być dodany do jednej, czy wielu kategorii. Bo jeśli do wielu to musisz nieco inaczej zapytania popisać. To samo tyczyło by ewentualnego dodania do klasy zmiennej dotyczącej tagów.
czarek1986
Czyli widzę ze zdania podzielone sa co do metody pobierającej wszystkie pola obiektu smile.gif Na razie zostawie ją tak jak jest bo bardziej przemawia do mnie argument, że jest ona szybsza niż jakby miałą wykonywać jeszcze szereg dodatkowych funckji smile.gif

Co do tej zmiennej new to rzeczywiscie mozna to wywalić i zrobić sprawdzanie za pomocą id. Tak zrobie i dzieki za radę. 

A jak z tymi wyjątkami? Nie powinienem umieszczać ich wewnątrz klasy?

thek
Co do wyjątków zdania są podzielone. Puryści uważają, że zostały one stworzone do obsługi błędów i tylko tak je trzeba stosować. Inni używają jak leci, w tym do przekazywania informacji na stronie. Jest to więc tak naprawdę osobista preferencja. Ja osobiście bliższy jestem pierwszej grupie. Informacja o błędach powinna jak najmniej mówić userowi o strukturze serwisu, jego bazach itp, bo to ułatwia ewentualny atak. I dlatego obsługa błędów powinna być używana tylko w miejscach wrażliwych na wystąpienie błędu, by nie dopuścić do sytuacji jaką opisałem w zdaniu poprzednim. W kodzie, który jest pewny można sobie wyjątki darować, bo to w końcu dodatkowa funkcja sprawdzająca efekty wykonywania skryptu i w minimalny sposób go spowalnia.
czarek1986
Troszeczkę przerobniłem moją klasę, według porad oraz własnych pomysłow. Teraz wyszło mi coś takiego.
  1.  
  2. class Artykul {
  3.  
  4. //zawiera unikalny numer identyfikujący artykuł w bazie danych
  5. protected $id;
  6. //zawiera nazwę autora tworzacego artykuł
  7. protected $autor;
  8. //zawiera nazwę kategorii do której przyporządkowany jest artykuł
  9. protected $kategoria;
  10. //zaiwera tytuł artykułu
  11. protected $tytul;
  12. //zawiera wstępny tekst artykułu
  13. protected $naglowek;
  14. //zawiera główną treść artykułu
  15. protected $tekst_artykulu;
  16. //zawiera datę utworzenia artykułu !!Unixowy znacznik czasu
  17. protected $utworzony;
  18. //zawiera datę ostatniej modyfikacji artykułu !!Unixowy znacznik czasu
  19. protected $zmodyfikowany;
  20. //zawiera datę ostatniej publikacji artykułu !!Unixowy znacznik czasu
  21. protected $opublikowany;
  22.  
  23.  
  24. protected $fieldsRequired = array('autor', 'kategoria', 'tytul', 'naglowek', 'utworzony' );
  25.  
  26. protected $dbhandle;
  27.  
  28.  
  29.  
  30. public function __construct($id=NULL)
  31. {
  32. $this->dbhandle = Database::GetInstance();
  33.  
  34. if($id)
  35. {
  36. $this->id_art = $id;
  37.  
  38. $sql = "SELECT *
  39. FROM j_artykuly
  40. WHERE id = $id";
  41.  
  42. $art = $this->dbhandle->select($sql);
  43.  
  44. if(!$art)
  45. throw new Exception('Wybrany artykuł nie istnieje');
  46.  
  47. $this->id = $id;
  48. unset($art[0]['id']);
  49. $this->setAll($art[0]);
  50. }
  51. }
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  /**
  58. * Metoda zwraca wartość jednego wybranego pola klasy
  59. *
  60. * @param name - nazwa pola w klasie, której wartość chcemy uzyskać
  61. **/
  62.  
  63. public function get($name)
  64. {
  65. if(property_exists(get_Class($this), $name))
  66. return $this->$name;
  67. else
  68. throw new Exception('Klasa '.get_class($this).' nie posiada pola '.$name);
  69. }
  70.  
  71. /**
  72. *
  73. * Metoda zwraca wartości wszystkich pól klasy
  74. *
  75. * @return tablica z danymi wszystkich pól;
  76. *
  77. **/
  78.  
  79. public function getAll()
  80. {
  81.  
  82. $arData = array(
  83. 'id' => $this->id,
  84. 'autor' => $this->autor,
  85. 'kategoria' => $this->kategoria,
  86. 'tytul' => $this->tytul,
  87. 'naglowek' => $this->naglowek,
  88. 'tekst_artykulu' => $this->tekst_artykulu,
  89. 'utworzony' => $this->utworzony,
  90. 'zmodyfikowany' => $this->zmodyfikowany,
  91. 'opublikowany' => $this->opublikowany,
  92. );
  93.  
  94. return $arData;
  95. }
  96.  
  97.  
  98. public function set($key, $value)
  99. {
  100. if($key == 'id')
  101. throw new Exception("Wartość pola 'id' może być nadana tylko automatycznie");
  102.  
  103. if(property_exists(get_Class($this), $key))
  104. {
  105. $this->$key = $value;
  106. }
  107. else
  108. throw new Exception('Klasa '.get_class($this).' nie posiada pola '.$key);
  109. }
  110.  
  111.  
  112. /**
  113. *
  114. * Metoda służy do ustawienia wartości pól w klasie
  115. *
  116. *@Param arData- tablica przechowująca nazwy pól i odpowiadające im wartości
  117. *
  118. **/
  119.  
  120. public function setAll(array $arData)
  121. {
  122. foreach($arData as $key => $value)
  123. {
  124. if($key == 'id')
  125. throw new Exception("Wartość pola 'id' może być nadana tylko automatycznie");
  126.  
  127. if(property_exists(get_Class($this), $key))
  128. {
  129. $this->$key = $value;
  130. }
  131. else
  132. throw new Exception('Klasa '.get_class($this).' nie posiada pola '.$key);
  133. }
  134. }
  135.  
  136.  
  137.  
  138. /**
  139. *
  140. * Metoda zapisująca lub tworząca artykuł w bazie danych
  141. *
  142. **/
  143.  
  144. public function saveArt()
  145. {
  146. if(empty($this->id))
  147. {
  148. $this->validateFields();
  149.  
  150. if($this->dbhandle->insert('j_artykuly', $this->getAll()) > 0);
  151. return true;
  152. }
  153. else
  154. {
  155. $this->validateFields();
  156.  
  157. $arConditions['id']=$this->id;
  158. if($this->dbhandle->update('j_artykuly', $this->getAll(), $arConditions) > 0);
  159. return true;
  160. }
  161. }
  162.  
  163.  
  164. /**
  165. *
  166. * Metoda usuwająca artykuł z bazy danych
  167. *
  168. **/
  169.  
  170. public function delArt()
  171. {
  172. if(!empty($this->id))
  173. {
  174. $this->validateFields();
  175.  
  176. $arWhere = array("id = ".$this->id);
  177. if($this->dbhandle->delete('j_artykuly', $arWhere) > 0)
  178. return true;
  179. }
  180.  
  181. throw new Exception('Nie można usunąć bierzącego artykułu, ponieważ nie został jeszcze zapisany w bazie danych.');
  182. }
  183.  
  184.  
  185. /**
  186. *
  187. * Metoda sprawdzająca czy pola, które nie mogą być puste w bazie danych, są wypełnione danymi
  188. *
  189. **/
  190.  
  191.  
  192. public function ValidateFields()
  193. {
  194. $arEmpty = array();
  195. foreach ($this->fieldsRequired as $field)
  196. {
  197. $value = $this->get($field);
  198. if (empty($value))
  199. $arEmpty[] = $field;
  200. }
  201.  
  202. if(count($arEmpty) > 0)
  203. {
  204. $empty = implode(', ', $arEmpty);
  205. throw new Exception('Wymagane pola '.$empty.' są puste.');
  206. }
  207.  
  208. return true;
  209. }
  210.  
  211.  
  212. ?>
  213.  
  214.  


Dodałem pole dbhandle w którym przechowywany będzie obiekt bazy danych. Kolejne dodane pole to $fieldsRequired w którym przechowywana jest tablica z polami wymaganymi do uzupełnienia. Zmodyfikowałem tez funkcję, która ma sprawdzać czy pola te są wypełnione. Na koniec połączyłem metody createArt i UpdateArt w jedną metodę SaveArt i usunełem pole new, a zamiast niego dałem sprawdzanie po id, według wczęsniejszej porady. Jak teraz według was wyglada moja klasa?

Speedy
Bieżący piszemy przez ż z kropką.
Jak komentujesz metody i zmienne, to rób to konsekwentnie i komentuj wszystkie. Zachowaj jakiś standard komentowania - np. phpDocumentator.
Moli
Poczytaj o metodach magicznych.
czarek1986
Cytat(Speedy @ 26.08.2009, 01:15:35 ) *
Bieżący piszemy przez ż z kropką.

Jak komentujesz metody i zmienne, to rób to konsekwentnie i komentuj wszystkie. Zachowaj jakiś standard komentowania - np. phpDocumentator.


Komentarze będą oczywiście przy każdej metodzie, na razie ich nie dodawałem bo nie wiem czy zaraz nie bedzie trzeba i tak tego usuwac smile.gif


ucho
Cytat(czarek1986 @ 26.08.2009, 10:15:09 ) *
Komentarze będą

Nie ma co się oszukiwać - jak nie napiszesz od razu to w 95% przypadków te komentarze już nigdy nie powstaną tongue.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-2025 Invision Power Services, Inc.