Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP][OOP]Walidacja
Forum PHP.pl > Forum > PHP > Object-oriented programming
Stron: 1, 2
MateuszS
Witam, zaczynam z OOP i napisałem prostą klasę walidującą dla małych formularzy, napiszcie co można poprawić, miałem ją zaopatrzyć w MVC i prezentować błędy widokiem ale to już na kiedy indziej.

  1. //wlasciwa klasa w moim nastepnym poscie w tym temacie ;P
Crozin
Dlaczego ten kod się do niczego nie nadaje?

1) Powiedzmy, że mamy pole "treść" w dwóch formularzach: edycji sygnaturki oraz tworzenia nowego wpisu do np. bloga. Chcę by pole "treść" w sygnaturce miało pomiędzy 10, a 255 znaków oraz by nie można było podać w nim żadnego adresu URL. Natomiast "treść" w formularzu nowego wpisu ma mieć długość co najmniej 50 znaków.
W tym momencie to "narzędzie" poległo (czyt.: to w jaki sposób co sprawdzać nie powinno być zawarte w klasie).

2) Powiedzmy, że ponownie mam dwa pola "treść". Chcę by treść komunikatu w przypadku gdy podano za mało znaków w formularzu edycji sygnaturki była następująca: "ABC Sygnaturka musi mieć co najmniej 10 znaków", a w formularzu dodania nowego wpisu: "DEF +50 znaków proszę."
Ponownie Twój kod się nie spisuje (czyt.: treści komunikatów również poza klasą lub możliwość ich dowolnej modyfikacji)

3) Chcę dodać sobie nowy walidator - np. sprawdzający czy nr NIP jest poprawny. Muszę przebudowywać pół klasy? (czyt.: walidatory powinny być osobnymi obiektami implementującymi jakiś interfejs)

To tak na początek.
MateuszS
To tak:
1. Miałem tragiczny błąd w metodzie, nie pozamieniałem z powrotem $this->zmienna na $zmienna, już to poprawiłem.
2. Dodałem metodę za pomocą której możesz bez przerabiania klasy dodawać nowe typy pól, np. ów nip.
3. Mógłbyś wytłumaczyć o co chodzi z

Cytat
walidatory powinny być osobnymi obiektami implementującymi jakiś interfejs


  1. <?
  2.  
  3. class Walidacja
  4. {
  5.  
  6. /* Dozwolone opcje:
  7.   * min_znakow - minimalna ilosc znakow
  8. * max_znakow - maksymalna ilosc znakow
  9. * wzor - wzor wg ktorego ma byc walidowana zmienna
  10.  
  11. Wywolanie obiektu
  12. $ob = new Walidacja;
  13. $ob->waliduj(zmienna, kryterium);
  14.  
  15. Mozna swobodnie dodawac nowe pola/typy pol do ustawien, np
  16. kod pocztowy i zaopatrzyc go w atrybut wzor w ktorym bylby
  17. wzor w postaci wyrazen regularnych, ktory mialby za zadanie
  18. sprawdzac zmienna czy ma poprawny format.
  19. */
  20.  
  21. public $ustawienia = array(
  22. "login" => array(
  23. "min_znakow" => 3,
  24. "max_znakow" => 25
  25. ),
  26. "haslo" => array(
  27. "min_znakow" => 5,
  28. "max_znakow" => 15
  29. ),
  30. "email" => array(
  31. "wzor" => "^[_.0-9a-z-]+@([0-9a-z][0-9a-z-]+.)+[a-z]{2,4}$",
  32. ),
  33. "zabronione" => array()
  34.  
  35. );
  36.  
  37. private $bledy = array();
  38.  
  39. public function dodaj($typ, $min = null, $max = null, $wzor = null)
  40. {
  41. if(!isset($min) and !isset($max) and !isset($wzor)) {
  42. throw new Exception("Musisz podac dodatkowy atrybut(maksymalna ilosc znakow, minimalna lub wzor");
  43. } else {
  44. $typ = (string)$typ;
  45. if(isset($min)) $this->ustawienia[$typ]["min_znakow"] = (int)$min;
  46. if(isset($max)) $this->ustawienia[$typ]["max_znakow"] = (int)$max;
  47. if(isset($wzor)) $this->ustawienia[$typ]["wzor"] = $wzor;
  48. }
  49.  
  50. }
  51.  
  52. public function zabron($zabronione)
  53. {
  54. if(!is_array($zabronione)) {
  55. array_push($this->ustawienia["zabronione"], $zabronione);
  56. } else {
  57. foreach($zabronione as $wartosc)
  58. array_push($this->ustawienia["zabronione"], $wartosc);
  59. }
  60. }
  61.  
  62. public function waliduj($zmienna, $typ = null)
  63. {
  64.  
  65. if(is_array($zmienna)) {
  66. throw new Exception("Weryfikowana zmienna nie moze byc tablica. Bo tak.");
  67. }
  68.  
  69. $zmienna = trim(strip_tags($zmienna));
  70.  
  71. foreach($this->ustawienia["zabronione"] as $zabronione) {
  72. if(preg_match("#".$zabronione."#", $zmienna)) {
  73. $this->bledy[] = "Formularz zawiera niedozwolone znaki/slowa: ".$zabronione.".";
  74. break;
  75. }
  76. }
  77.  
  78. if(isset($typ)) {
  79. if(isset($this->ustawienia[$typ])) {
  80. $ustawienia = $this->ustawienia;
  81.  
  82. foreach($this->ustawienia[$typ] as $klucz => $wartosc) {
  83. if($klucz == "min_znakow") {
  84. if(strlen($zmienna) < $this->ustawienia[$typ]["min_znakow"])
  85. $this->bledy[] = ucfirst($typ)." musi miec conajmniej ".$this->ustawienia[$typ]["min_znakow"]." znakow.";
  86. }
  87. if($klucz == "max_znakow"){
  88. if(strlen($zmienna) > $this->ustawienia[$typ]["max_znakow"])
  89. $this->bledy[] = ucfirst($typ)." moze miec conajwyzej ".$this->ustawienia[$typ]["max_znakow"]." znakow.";
  90. }
  91. if($klucz == "wzor"){
  92. if(!preg_match("#".$this->ustawienia[$typ]["wzor"]."#", $zmienna))
  93. $this->bledy[] = ucfirst($typ)." ma niepoprawny format.";
  94. }
  95.  
  96. }
  97. }
  98. }
  99.  
  100.  
  101. return $zmienna;
  102. }
  103.  
  104. public function pokazbledy()
  105. {
  106. return $this->bledy;
  107. }
  108. }
  109.  
  110. try {
  111.  
  112.  
  113. $obWaliduj = new Walidacja();
  114. $obWaliduj->dodaj("tresc", 1, 10);
  115. $obWaliduj->waliduj("hesdsadsaddads", "tresc");
  116. $obWaliduj->dodaj("tresc", 4, 7);
  117. $obWaliduj->waliduj("aa", "tresc");
  118. $bledy = $obWaliduj->pokazbledy();
  119. foreach($bledy as $blad) {
  120. echo $blad."<br />";
  121. }
  122.  
  123. } catch(Exception $e) {
  124. echo "Blad: ".$e->getMessage();
  125. }
Spawnm
destruct masakryczny , echo w modelu ?
Dodatkowo lepiej by wyglądała całość gdybyś dał angielskie nazwy.
rozbił bym walidację na metody np valid::mail('adf@dsaf.pl');
Wicepsik
Walidacja NIPu nie polega tylko na policzeniu jego długości. http://pl.wikipedia.org/wiki/NIP/Implementacja
MateuszS
Spawnm, tak wiem, to nie jest problem zamienić na ang, echo dałem bo jak napisałem, nie mam klasy widoku, więc na szybko wyświetlam błędy,

Wicepsik, gdybyś zobaczył kod, byś wiedział że można dodać do metody 4 parametr którym jest wyrażenie regularne. W każdym razie jeżeli wyrażenia nie wystarczą to dodam jeszcze metodę sprawdzającą nip.

edit:: dodałem prostą metodę zwracania błędów.
Crozin
Cytat
3. Mógłbyś wytłumaczyć o co chodzi z
Cytat
walidatory powinny być osobnymi obiektami implementującymi jakiś interfejs
Chodzi o to, że walidatory powinny być osobnymi obiektami wyspecjalizowanymi w walidacji konkretnego rodzaju. Daje to możliwość, bym utworzył sobie swój własny walidator, niewpisany na sztywno w kod Twojej klasy - innymi słowy: zwiększamy elastyczność narzędzia.

Tutaj masz przykładowy kod, który ilustruje jak mogło by to wyglądać (oczywiście brak tutaj dziesiątek rzeczy (ustawienia czytelnej treści błędu, wielu walidatorów dla jednego elementu, przedefiniowanych opcji dla walidatorów i całej masy innych rzeczy)):
  1. <?php
  2.  
  3. namespace Crozin\Validation {
  4. interface Validatable {
  5. public function configure(array $params = array());
  6. public function getOptions();
  7. public function getOption($key);
  8.  
  9. public function isValid($subject);
  10. }
  11.  
  12. class Validator {
  13. protected $subjects = array();
  14. protected $validators = array();
  15.  
  16. protected $errors = array();
  17. protected $data = array();
  18.  
  19. public function isValid() {
  20. $isValid = true;
  21.  
  22. foreach ($this->subjects as $key => $subject) {
  23. $result = $this->validators[$key]->isValid($subject);
  24.  
  25. if ($result instanceof Response) {
  26. $isValid = false;
  27. $this->errors[$key] = $result;
  28. } else {
  29. $this->data[$key] = $result;
  30. }
  31. }
  32.  
  33. return $isValid;
  34. }
  35.  
  36. public function addSubjects(array $subjects) {
  37. foreach ($subjects as $key => $subject) {
  38. $this->addSubject($key, $subject);
  39. }
  40. }
  41.  
  42. public function addSubject($key, $subject) {
  43. $this->subjects[$key] = $subject;
  44. }
  45.  
  46. public function addValidators(array $validators) {
  47. foreach ($validators as $key => $validator) {
  48. $this->addValidator($key, $validator);
  49. }
  50. }
  51.  
  52. public function addValidator($key, Validatable $validator) {
  53. $this->validators[$key] = $validator;
  54. }
  55.  
  56. public function getData() {
  57. return $this->data;
  58. }
  59.  
  60. public function getErrors() {
  61. return $this->errors;
  62. }
  63. }
  64.  
  65. class Response {
  66. protected $msg;
  67. protected $subject;
  68. protected $validator;
  69.  
  70. public function __construct($msg, $subject, Validatable $validator) {
  71. $this->msg = $msg;
  72. $this->subject = $subject;
  73. $this->validator = $validator;
  74. }
  75.  
  76. public function getMsg() {
  77. return $this->msg;
  78. }
  79.  
  80. public function getSubject() {
  81. return $this->subject;
  82. }
  83.  
  84. public function getValidator() {
  85. return $this->validator;
  86. }
  87. }
  88. }
  89.  
  90. namespace Crozin\Validation\Validator {
  91. use \Crozin\Validation\Validatable;
  92. use \Crozin\Validation\Response;
  93.  
  94. abstract class Base {
  95. protected $options = array();
  96.  
  97. public function __construct(array $options = array()) {
  98. $this->configure($options);
  99. }
  100.  
  101. public function configure(array $options = array()) {
  102. $this->options = $options;
  103. }
  104.  
  105. public function getOptions() {
  106. return $this->options;
  107. }
  108.  
  109. public function getOption($key) {
  110. return isset($this->options[$key]) ? $this->options[$key] : null;
  111. }
  112. }
  113.  
  114. class NIP extends Base implements Validatable {
  115. public function isValid($subject) {
  116. /* NIP ma nieprawidłowy format (np. podano litery) */
  117. if (1 == mt_rand(1, 2)) {
  118. return new Response('format', $subject, $this);
  119. }
  120.  
  121. /* NIP ma nieprawidłową sumę kontrolną */
  122. if (1 == mt_rand(1, 2)) {
  123. return new Response('control.sum', $subject, $this);
  124. }
  125.  
  126. return $subject;
  127. }
  128. }
  129.  
  130. class String extends Base implements Validatable {
  131. public function isValid($subject) {
  132. if ($this->getOption('trim')) {
  133. $subject = trim($subject);
  134. }
  135.  
  136. if (($min = $this->getOption('min')) && strlen($subject) < $min) {
  137. return new Response('min', $subject, $this);
  138. }
  139.  
  140. if (($max = $this->getOption('max')) && strlen($subject) > $max) {
  141. return new Response('max', $subject, $this);
  142. }
  143.  
  144. return $subject;
  145. }
  146. }
  147.  
  148. class Email extends Base implements Validatable {
  149. public function isValid($subject) {
  150. /* Nieprawidłowy format */
  151. if (1 == mt_rand(1, 2)) {
  152. return new Response('format', $subject, $this);
  153. }
  154.  
  155. /* Jeżeli wpisy MX nie są dobre */
  156. if (1 == mt_rand(1, 2)) {
  157. return new Response('mx', $subject, $this);
  158. }
  159.  
  160. return $subject;
  161. }
  162. }
  163.  
  164. class NumberRange extends Base implements Validatable {
  165. public function isValid($subject) {
  166. if ($this->getOption('natural.only') && false == ctype_digit($subject)) {
  167. return new Response('not.natural', $subject, $this);
  168. }
  169.  
  170. if ($subject < $this->getOption('min') || $subject > $this->getOption('max')) {
  171. return new Response('out.of.range', $subject, $this);
  172. }
  173.  
  174. return $subject;
  175. }
  176. }
  177. }
  178.  
  179. namespace {
  180. use \Crozin\Validation\Validator;
  181. use \Crozin\Validation;
  182.  
  183. error_reporting(E_ALL | E_STRICT);
  184.  
  185. $_POST = array(
  186. 'firstname' => ' Krzysiek ',
  187. 'nickname' => 'Crozin',
  188. 'age' => '7',
  189. 'mail' => 'k...s@gmail.com'
  190. );
  191.  
  192. $v = new Validator();
  193. $v->addSubjects($_POST);
  194. $v->addValidators(array(
  195. 'firstname' => new Validator\String(array('trim' => true, 'min' => 6)),
  196. 'nickname' => new Validator\String(),
  197. 'age' => new Validator\NumberRange(array('natural.only' => true, 'min' => 9, 'max' => 99)),
  198. 'mail' => new Validator\Email()
  199. ));
  200.  
  201. if ($v->isValid()) {
  202. var_dump('Poprawne dane:');
  203. var_dump($v->getData());
  204. } else {
  205. foreach ($v->getErrors() as $error) {
  206. var_dump(sprintf('%s - %s - %s - %s', get_class($error->getValidator()), $error->getSubject(), $error->getMsg(), print_r($error->getValidator()->getOptions(), 1)));
  207. }
  208. }
  209. }
Ale dlaczego to rozwiązanie jest o niebo lepsze? Chcę utworzyć nowy walidator... wystarczy mi obiekt dowolny, który implementuje interfejs \Crozin\Validation\Validatable; Chcę spersonalizować dany walidator? Podaję mu parametr. Itp. itd.

PS. Całość pisana "z palca" bez zastanowienia (jedynie sprawdziłem czy w ogóle działa) - więc jest to na pewno kod do poprawki (przemyślenia sposobu działania, przerobienia), ale daje jako taki zarys pewnej elastyczności.
MateuszS
Niestety jest to dla mnie czarna magia co napisałeś, te interfejsy, klasy abstrakcyjne, nigdy nie mogłem załapać o co w nich chodzi, nie sądziłem że mi się prędko przydadzą.
Poza tym wywala błędy z tym namespace, nieoczekiwany string, no i czy ten system waliduje chociaż pod kątem tagów HTML w polach? Bo widzę setki dziwnych rzeczy ale nigdy bym nie zgadł że to ma coś wspólnego z walidacją.
Crozin
Cytat
Poza tym wywala błędy z tym namespace
Widocznie masz PHP w wersji poniżej 5.3 - ale przestrzenie nazw można stąd wywalić na dobrą sprawę.
Cytat
no i czy ten system waliduje chociaż pod kątem tagów HTML w polach
Nie, dlaczego miałby to robić? A jeżeli jest Ci takie coś potrzebne wystarczy zrobić sobie walidator, który będzie sprawdzać coś takiego.

Cytat
Niestety jest to dla mnie czarna magia co napisałeś, te interfejsy, klasy abstrakcyjne, nigdy nie mogłem załapać o co w nich chodzi, nie sądziłem że mi się prędko przydadzą.
W takim razie radzę sobie na chwilę obecną darować jakiekolwiek pisanie i skupić się wyłącznie na teorii i możliwościach języka (swoją drogą bardzo ograniczonych pod względem OOP).
MateuszS
Cytat
Widocznie masz PHP w wersji poniżej 5.3 - ale przestrzenie nazw można stąd wywalić na dobrą sprawę.

No mam PHP 5.2.6.

Cytat
Nie, dlaczego miałby to robić? A jeżeli jest Ci takie coś potrzebne wystarczy zrobić sobie walidator, który będzie sprawdzać coś takiego.

No ja przecież napisałem skrypt który ma za zadanie walidować dane z formularza, myślałem że twój też to robi tongue.gif Bo o to mi głównie chodzi, napisać dobrą klasę która sprawdza takie rzeczy

Cytat
W takim razie radzę sobie na chwilę obecną darować jakiekolwiek pisanie i skupić się wyłącznie na teorii i możliwościach języka (swoją drogą bardzo ograniczonych pod względem OOP).

Nie no wiesz, logiczne że najpierw teoria, ale w mojej książce są zaledwie 2 strony o interfejsach i brak jakichkolwiek przykładów praktycznego zastosowania, stąd mój brak wiedzy na ten temat... a podobno najlepsza jest praktyka.

Wiele osób radzi żebym zajął się jakimś frameworkiem, ale co mi po frameworku jak wg was nie umiem napisać najprostrzej rzeczy w OOP

Crozin
Cytat
No ja przecież napisałem skrypt który ma za zadanie walidować dane z formularza, myślałem że twój też to robi
Jeżeli mu rozkażesz to i to będzie Ci robić tylko dlaczego miałby z automatu blokować elementy HTMLa w tych polach? Że niby nie mogę w treści wątku wpisać <p> to akapit w HTMLu? Takimi rzeczami jak wyświetlanie wprowadzonych przez użytkowników danych (by uniemożliwić wstawienie własnego kodu HTML itp.) też trzeba się zająć - ale to nie jest robota dla walidatora.
Cytat
Nie no wiesz, logiczne że najpierw teoria, ale w mojej książce są zaledwie 2 strony o interfejsach
No na dobrą sprawę to o interfejsach nie ma się co wiele rozpisywać. Nie jest to jakaś skomplikowana część języka.
Cytat
i brak jakichkolwiek przykładów praktycznego zastosowania
No to to co Ci podałem możesz potraktować jako konkretny przykład zastosowania interfejsów - metoda \Crozin\Validation\Validator::addValidator() wymaga by drugi parametr był obiektem implementującym interfejs \Crozin\Validation\Validatable. Wymaga tego ponieważ później wykonywane są operacje na tych walidatorach (a dokładnie to wywoływanie metody isValid()). Dzięki temu mamy pewność, że to co przekazujemy jako walidator jest ów walidatorem, albo przynajmniej potrafi się zachowywać jak takowy.

Cytat
Wiele osób radzi żebym zajął się jakimś frameworkiem, ale co mi po frameworku jak wg was nie umiem napisać najprostrzej rzeczy w OOP
Również uważam, że branie się za FW w momencie gdy nie zna się nawet elementów języka jest głupotą.
MateuszS
Cytat
Również uważam, że branie się za FW w momencie gdy nie zna się nawet elementów języka jest głupotą.


No nie przesadzajmy, podstawy pewne mam
Crozin
Cytat
No nie przesadzajmy, podstawy pewne mam
Tego negować nie będę, ale jeżeli nie znasz elementów języka (interfejsy, klasy abstrakcyjne, przestrzenie nazw, Type Hinting (nie wiem jaka można by to po polsku nazwać) itp) nie wspominając o podstawowych paradygmatach obiektówki (abstrakcja bytów, polimorfizm, hermetyzacja itp.) to ciężko jest mówić o tym, że ma się opanowane podstawowe założenia programowania obiektowego.

Zacznij od manuala - przebrnij przez paskudnych kilkanaście stron teorii - potem polecałbym poczytać nieco artykułów dotyczących OOP (nie koniecznie nastawionego na PHP, gdyż te idee są wspólne dla wszystkich języków obiektowych).
Czasami dobrze też zacząć pisanie od końca czyli od interfejsu (chodzi tutaj o to w jaki sposób będziesz danego narzędzia używać (czyli to co jest w liniach 192 - 199 mojego kodu)). Tworzysz sobie jak najbardziej skomplikowane przykłady użycia danego narzędzia, a dopiero potem bierzesz się za implementację.
zzeus
@Crozin mógłbyś rzucić kilka linków do artykułów które dotyczą OOP ? Chodzi mi o artykuły które są sprawdzone i dobre według Ciebie.
marcio
Cytat
@Crozin mógłbyś rzucić kilka linków do artykułów które dotyczą OOP ? Chodzi mi o artykuły które są sprawdzone i dobre według Ciebie.

Nie ma zlych artykulow sa tylko dobre i bardzo dobre :]
Zamiast czytania art'ow polecam manual zeby zapoznac sie z kontrukcjami OOP w PHP 5-6.
Potem sciagnac jakis gotowy Fw/klasy przeanalizowac je i probowac pisac samemu od jakis prostych systemow.
Dawac kod na forum i pytac co mozna lepiej napisac.
Kursy zabardzo nie pomagaja przyklady ktore zawieraja sa troche kulawe jak i te zawarte w ksiazkach jednak do teorii sa OK.

P.S @MateuszS zajzyj tutaj: Temat: klasaphp Validation masz tam az 3 klasy do tego
MateuszS
Crozin, to że nie użyłem w mojej klasie np. dziedziczenia to nie znaczy że nie wiem jak to używać smile.gif
marcio, potiwerdzam, kursy nie mają dobrych przykładów, w książki też trochę zainwestowałem ale chyba nie bardzo się opłacało. Manual może i jest dobry, ale trzeba znać perfekcyjnie angielski ja puki co jeszcze się kształcę w tym kierunku.

Crozin
Cytat
Crozin, to że nie użyłem w mojej klasie np. dziedziczenia to nie znaczy że nie wiem jak to używać
Wszystkie te moje wywody opieram na:
Cytat
Niestety jest to dla mnie czarna magia co napisałeś, te interfejsy, klasy abstrakcyjne, nigdy nie mogłem załapać o co w nich chodzi, nie sądziłem że mi się prędko przydadzą.


Cytat
Manual może i jest dobry, ale trzeba znać perfekcyjnie angielski ja puki co jeszcze się kształcę w tym kierunku.
A bzdura...! Wcale nie musisz znać dobrze tego angielskiego. Fakt - pewnie co jakiś czas trafi Ci się słówko, którego nie znasz, ale jeżeli jako tako potrafisz czytać w tym języku to nie widzę powodów, dla których nie miałbyś właśnie z tego źródła korzystać.
MateuszS
No a jak wy się OOP uczyliście? Też mieliście takie problemy? ;/ Może ponaśladuję tongue.gif
marcio
Cytat(MateuszS @ 22.04.2010, 20:31:53 ) *
No a jak wy się OOP uczyliście? Też mieliście takie problemy? ;/ Może ponaśladuję tongue.gif

Ja czytalem kursy,ksiazki i zadawalem glupie pytania i brali mnie za debila i tlumaczyli jak debilowi az wkoncu posciagalem pare fw, roznych klas poprobowalem sam cos pokodzic az weszlo mi w krew oop.
MateuszS
He czyli głupie pytania kluczem do sukcesu biggrin.gif To kolejne:

Jaka jest praktyczna różnica pomiędzy dziedziczeniem a wywołaniem obiektu klasy w klasie, przykład:

  1. class a
  2. {
  3. public $c = 1;
  4. }
  5. class b extends a
  6. {
  7. function bla()
  8. {
  9. echo $this->c;
  10. }
  11.  
  12. }


a

  1. class a
  2. {
  3. public $aa=1;
  4. }
  5.  
  6. class b
  7. {
  8. function __construct()
  9. {
  10. $this->ob = new a;
  11. echo $this->ob->aa;
  12. }
  13. }
marcio
Taka ze dziedziczysz po klasie ktora rozszerza klase po ktorej dziedziczysz(rotfl ale misz masz wyszedl) jesli maja ze soba cos wspolnego.
Jakis praktyczny przyklad masz komponent News i pokolei klase News_Model,News_View i News_Controller.Klasa obsluguje Crud'a news'ow czyli krotko mowiac ma takie operacje jak create,read,update,delete.
Jako ze uzywamy MVC to jesli model komponentu News bedzie dziedziczyl po klasie do obslugi np mysql'a to bedzie ok czyli:
  1. class News_Model extends MySqlDb { }

A dlaczego jest ok bo klasa News_Model nie wprowadza zadnych nowych funkcjonalnosci ktore zmienia przeznaczenie klasy tylko operuje na bazie tak jak to robi klasa MySqlDb z tym ze rozszerza je o funkcje pobierania news'ow,usuwania ich,edytowania i dodawania operujac zawsze na bazie.
Instancje klasy uzywasz wtedy gdy potrzebujesz uzyc jakas klase w srodku innej jednak te w klasy nie maja ze soba nic wspolnego, wiec dziedziczenie nie wchodzi w gre.
To znaczy nie mozesz zrobic takie potwora z czarnobyla:
  1. class Controller extends View { }

Bo to nie ma najmniejszego sensu te klasy jako tako ze soba nie maja nic wspolnego.
Wiec robisz:
  1. class Controller {
  2.  
  3. protected $view;
  4.  
  5. public function __construct() {
  6.  
  7. $this -> view = new View();
  8.  
  9. }
  10.  
  11. }

Wtedy i tak w klasie kontrolera mozesz uzywac klasy View bez niepotrzebnego,bezsensownego dziedziczenia.
Nie wiem czy jasno napisalem moze przyklady jakies takie nie takie ale na szybko podalem przyklad.

  1. class Auto { }
  2.  
  3. class Fiat extends Auto { } // jako tako ma sens bo kazdy pojazd robi to samo tylko jeden moze miec wiecej ficzerow niz drugi
  4.  
  5. class Bmw extends Auto { } // ok
  6.  
  7. class Motor extends Auto { } // nie bardzo pasuje

Ogolnie trudno jest mi to opisac na tyle to opanowalem ze jak pisze to wiem kiedy mam uzyc dziedziczenia a kiedy uzyc instancje przekazywana do jakiejs metody lub ustawiona juz w srodku kontruktora.
Kiedy mi sa potrzebne interfejsy i klasy abstrakcyjne ktore jako tako duzo sie nie roznia tez wiem kiedys lepiej jest uzyc jednego a kiedy drugiego.
Tak jak ci juz mowilem przeanalizuj kilka klas ktore nie zostaly napisane przez amatorow wtedy sie nauczysz.

P.S to tak jakby kontroler komponentu news dziedziczyl po acl by sprawdzac czy dany user moze usunac news'a IMHO bezsens nie uwazasz?
MateuszS
IMHO nie rozszyfrowałem skrótu acl. Mądrze piszesz i trochę mi rozjaśniłeś ale czy jest sens żeby taka przykładowa klasa News_Model dziedziczyła po MYSQLDB ? Przecież można przekazać zwyczajnie obiekt, po co każda lepsza klasa potem ma dziedziczyć z Mysqla. Jeżeli ta klasa będzie musiała dziedziczyć z innej przy okazji? PHP nie obsługuje z tego co wiem dziedziczenia z kilku klas.

Analizuję przykłady ale o ilę rozumiem kod to nie bardzo rozumiem filozofii, po co ten ktoś akurat utworzył taką metodę, zamiast cośtam innego, pewnie masz rację i z czasem się naumiem, ale jeszcze nic mi tak topornie nie szło jak obiektówka ;/
Crozin
Cytat
Jakis praktyczny przyklad masz komponent News i pokolei klase News_Model,News_View i News_Controller
To teraz tak:
1) Czym jest w tym przykładzie News, czym jest News Model, czym News View oraz czym jest News Controller? Skoro mają dziedziczyć po tym samym (News) sugeruje to, że wszystkie te klasy są Newsem, jednakże ciężko jest mi wyobrazić sobie jakąkolwiek zależność (na tej płaszczyźnie) pomiędzy takimi klasami.

Cytat
Jako ze uzywamy MVC to jesli model komponentu News bedzie dziedziczyl po klasie do obslugi np mysql'a to bedzie ok czyli:
  1. class News_Model extends MySqlDb { }
Dlaczego co widzę pytanie mające najmniejszy związek z OOP/projektowaniem aplikacji zaraz wplata się wątek MVC? Biedne MVC jest już chyba tym dla PHP co AJAX dla JS.

Nie wprowadzaj w ogóle pojęcia tego wzorca na tę chwilę bo jest to zbyteczne. Jedynie mieszasz autorowi wprowadzając kolejne "dziwne nazwy" których nie rumie (Ty zresztą chyba też o czym za chwilę - bez urazy tongue.gif).

Dlaczego NewsModel (czyli jak rozumiem coś co ma pełnić rolę Modelu dla zasobu: News) ma dziedziczyć po klasie obsługi bazy danych? W jaki sposób News rozszerza dostęp do bazy danych? Czy News jest dostępem do bazy danych? Tak absurdalne pytania tworzy Twój pomysł. Ten obiekt (NewsModel) co najwyżej może wykorzystywać obiekt MysqlDB. Może nie oznacza, że musi, bo Newsy mogą być pobierane np. z pliku XML czy z innego portalu - wtedy nie ma potrzeby by NewsModel w ogóle miał możliwość zrobienia czegokolwiek w bazie danych.

Cytat
A dlaczego jest ok bo klasa News_Model nie wprowadza zadnych nowych funkcjonalnosci ktore zmienia przeznaczenie klasy tylko operuje na bazie tak jak to robi klasa MySqlDb z tym ze rozszerza je o funkcje pobierania news'ow,usuwania ich,edytowania i dodawania operujac zawsze na bazie.
Wybacz, ale: Google "dziedziczenie programowanie obiektowe", "kompozycja programowanie obiektowe". Nie rozpoznajesz relacji jest (is a) oraz ma (has a).

Cytat
Instancje klasy uzywasz wtedy gdy potrzebujesz uzyc jakas klase w srodku innej jednak te w klasy nie maja ze soba nic wspolnego
No tutaj to już pojechałeś... Jeżeli dwa obiekty nie mają ze sobą nic wspólnego to jakim cudem mogą współpracować?

Cytat
To znaczy nie mozesz zrobic takie potwora z czarnobyla:
  1. class Controller extends View { }
Tak swoją drogą... tutaj piszesz o Czarnobylach, a wcześniej sam takiego zrobiłeś (NewsModel dziedziczy po MySQLDB)

Cytat
Nie wiem czy jasno napisalem moze przyklady jakies takie nie takie ale na szybko podalem przyklad.
  1. class Auto { }
  2.  
  3. class Fiat extends Auto { } // jako tako ma sens bo kazdy pojazd robi to samo tylko jeden moze miec wiecej ficzerow niz drugi
  4.  
  5. class Bmw extends Auto { } // ok
  6.  
  7. class Motor extends Auto { } // nie bardzo pasuje
Przykład z dupy... znaczy się: kompletnie niepoprawny. Jakim cudem BMW może być rozszerzeniem samochodu? Przecież to jest marka samochodu (BMW czyli marka mogłoby być właściwością obiektu).

Przykład korygujący:
  1. abstract class Pojazd {} // Może się poruszać, przyśpieszać, hamować
  2. abstract class PojazdKołowy extends Pojazd {} // To co Pojazd + Ma koła, może skręcać
  3. class Rower extends PojazdKołowy {} // to co PojazdKołowy + Ma łańcuch
  4. class Smaochód extends PojazdKołowy {} // to co PojazdKołowy + Ma silnik, zbiornik paliwa, nadpisuje "przyśpieszać" (by spalało paliwo), może tankować


Kod
IMHO nie rozszyfrowałem skrótu acl.
ACL = Access Control List
Kod
Mądrze piszesz i trochę mi rozjaśniłeś ale czy jest sens żeby taka przykładowa klasa News_Model dziedziczyła po MYSQLDB ?
Jak już napisałem - nie ma. Co więcej jest to błąd.
btw: Nie ulegaj złudzeniu, że jak ktoś się posługuje zwrotami, których nie znasz, to automatycznie oznacza, że się zna. smile.gif
Kod
Przecież można przekazać zwyczajnie obiekt, po co każda lepsza klasa potem ma dziedziczyć z Mysqla. Jeżeli ta klasa będzie musiała dziedziczyć z innej przy okazji?
Dokładnie tak to powinno być rozwiązane.
Kod
PHP nie obsługuje z tego co wiem dziedziczenia z kilku klas.
Tak jest. ...i całe szczęście.

Kod
Jaka jest praktyczna różnica pomiędzy dziedziczeniem a wywołaniem obiektu klasy w klasie, przykład:
Dziedziczenie oznacza właśnie relację typu: jest. Jeżeli Jabłko dziedziczy po Owoc to możemy powiedzieć, że Jabłko jest Owocem - przy czym Jabłko to jakiś bardziej wyspecjalizowany byt niż Owoc. To drugie to tzw. kompozycja i jest to relacja typu: ma. Samochód ma silnik (ma również Lusterko, Fotel, Kierownicę), ale o samochodzie nie powiesz, że jest to silnik.

Kiedy używamy kompozycji? W momencie, kiedy dany obiekt, potrzebuje wykonywać jakieś operacje na innym.

Przykład:
  1. class Silnik {
  2. public $moc;
  3. public $spalanie;
  4. }
  5.  
  6. class Samochód {
  7. public $silnik;
  8. public $paliwo;
  9.  
  10. public function możeJechać($km) {
  11. return $this->paliwo > $this->slinik->spalanie * $km;
  12. }
  13.  
  14. public function jedź($km) {
  15. $this->paliwo -= $this->slinik->spalanie * $km;
  16. }
  17. }
  18.  
  19.  
  20. $silnik = new Silnik();
  21. $silnik->moc = 75;
  22. $silnik->spalanie = 0.5;
  23.  
  24. $samochód = new Samochód();
  25. $samochód->silnik = $silnik;
  26. $samochód->paliwo = 50;
  27.  
  28. while ($samochód->możeJechać(50)) {
  29. echo 'Jadę 50 km';
  30. $samochód->jedź(50);
  31. }
Ten przykład łamie wiele paradygmatów obiektówki (np. hermetyzację danych), ale powinno dać Ci to jako taki obraz na współpracę obiektów.
marcio
Jak pisalem przyklady wymyslilem w momencie.
Cytat
1) Czym jest w tym przykładzie News, czym jest News Model, czym News View oraz czym jest News Controller? Skoro mają dziedziczyć po tym samym (News) sugeruje to, że wszystkie te klasy są Newsem, jednakże ciężko jest mi wyobrazić sobie jakąkolwiek zależność (na tej płaszczyźnie) pomiędzy takimi klasami.

Przeczytaj moj post kilka razy to zrozumiesz.
Cytat
Nie wprowadzaj w ogóle pojęcia tego wzorca na tę chwilę bo jest to zbyteczne. Jedynie mieszasz autorowi wprowadzając kolejne "dziwne nazwy" których nie rumie (Ty zresztą chyba też o czym za chwilę - bez urazy tongue.gif).

Prosze cie bardzo, nie badz smieszny.
Cytat
Dlaczego NewsModel (czyli jak rozumiem coś co ma pełnić rolę Modelu dla zasobu: News) ma dziedziczyć po klasie obsługi bazy danych?

Ogolnie napisalem tylko dla przykladu ze model komponentu news powinien dziedziczyc po klasie mysqlDb u mnie dziedzizy po klasie Model ktora moze obslugiwac jaka kolwiek baze a jak bedzie potrzeba to nawet rss'a innego serwisu.
http://docs.kohanaphp.com/general/models jak widac nie jest to bledne podejscie inne fw zreszta tez tak robia :]
Cytat
No tutaj to już pojechałeś... Jeżeli dwa obiekty nie mają ze sobą nic wspólnego to jakim cudem mogą współpracować?

Wspolnego to znaczy ze sa na tylle inne od siebie ze dziedziczenie nie wchodzo w gre jesli w kontrolerze musze sprawdzic uprawnienia user'a przed wykonaniem jakiejs akcji to przeciez nie zrobie:
  1. class controller extends acl { }

Bo to sie ma jak piesc do nosa, i wtedy klasa kontrolera udostepnia mi wszystkie obiekty jakie potrzebuje.
Cytat
Tak swoją drogą... tutaj piszesz o Czarnobylach, a wcześniej sam takiego zrobiłeś (NewsModel dziedziczy po MySQLDB)

Nvm
Cytat
Przykład z dupy... znaczy się: kompletnie niepoprawny. Jakim cudem BMW może być rozszerzeniem samochodu? Przecież to jest marka samochodu (BMW czyli marka mogłoby być właściwością obiektu).

Jedyna rzecz do jakiej mozesz sie przyczepic, bo faktycznie przyklad do bani.
Cytat
btw: Nie ulegaj złudzeniu, że jak ktoś się posługuje zwrotami, których nie znasz, to automatycznie oznacza, że się zna.

Pamietaj ze nawet jesli odp na moj post bo cos ci nie pasowalo nie znaczy ze masz racje snitch.gif
Cytat
Dziedziczenie oznacza właśnie relację typu: jest. Jeżeli Jabłko dziedziczy po Owoc to możemy powiedzieć, że Jabłko jest Owocem - przy czym Jabłko to jakiś bardziej wyspecjalizowany byt niż Owoc

Kurde no to ja chyba czegos nie rozumiem skoro mamy:
  1. class news_Model extends model { }

To news_model tez jest modelem w tym ze specjalizuje sie w pobieraniu news'ow z bazy/xml czy czegokolwiek.
MateuszS
Czyli jednak moja intuicja jeszcze ma się całkiem dobrze. Zostawiając wzorzec MVC i wracając do mojej klasy walidacji, która jest gdzieś na początku tego topicu, z tego co gdzieś wcześniej Crozin pisał, powinienem tak ją zaprojektować, żeby programista mógł *decydować z którego pola mają być usuwane spacje, tagi HTML itp? Kiedyś też tak myślałem ale doszedłem wtedy do wniosku że w takim razie klasa walidacji jest zupełnie niepotrzebna. Jeżeli dodać by tę *funkcjonalność to ta klasa nie była by chyba aż tak tragiczna, w końcu widziałem podobne na tym forum, choćby z linków marcio, gdzie było wszystko podobnie skonstruowane, też można by się przeczepić do czegoś, mimo to nikt zastrzeżeń nie miał... ;>
Crozin
Cytat
Kiedyś też tak myślałem ale doszedłem wtedy do wniosku że w takim razie klasa walidacji jest zupełnie niepotrzebna.
Dlaczego miała by być niepotrzebna? To w jaki sposób coś ma być sprawdzane/filtrowane powinno zależeć wyłącznie od woli programisty. W dodatku takie, nazwijmy to sobie narzędzie (bo składa się ono docelowo z wielu obiektów) załatwia za Ciebie multum spraw (jak chociażby obsługę błędów, powiadomień, implementacji danych walidatorów itp. itd.) - Ty decydujesz jedynie co ma być czym potraktowane.
Dodatkowo jeżeli utworzysz sobie jakiś walidator (powiedzmy: String), który będzie miał kilka opcji: trim (true/false), striphtml (true/false), minlength, maxlength, gdzie dwa pierwsze domyślnie ustawione są na tak (w przykładzie podanym przeze mnie nie ma zaimplementowanej opcji predefiniowanych ustawień dla walidatora), a dwa ostatnie domyślnie są nie podane (są tak jakby wyłączone) już zyskujesz właściwie automatyczne walidację na takich zasadach jakie opisałeś. Więc jaki jest sens takiego dodatkowego komplikowania kodu? Bo jak będę miał dwa pola do wpisania treści, np.: krótka treść newsa, pełna treść newsa to dla pierwszego ustawiam sobie jedynie min/max na 20/200, a dla drugiego striphtml na false (dzięki czemu mogę podpiąć z poziomu panelu administracyjnego wstawić dowolnie wyglądającego newsa) oraz minlength na np. 50.
Zyskujesz wiele na elastyczności.


No i teraz muszę jeszcze odpowiedzieć na post Marcio... winksmiley.jpg
Cytat
Jak pisalem przyklady wymyslilem w momencie.
W takim razie zastanów się przed napisaniem tego co Ci przyjdzie na język, bo to co piszesz może być bez sensu lub może źle oddawać to co chciałeś przekazać. Taka luźna rada. ;]

Cytat
Przeczytaj moj post kilka razy to zrozumiesz.
Cytat
Jakis praktyczny przyklad masz komponent News i pokolei klase News_Model,News_View i News_Controller.
Ehh... 02:00 to już trochę późno... zrozumiałem, że każda z News_XXX dziedziczy po jakimś News. Tak więc moje zarzuty względem tego fragmentu cofam.

Cytat
Prosze cie bardzo, nie badz smieszny.
Cóż... proszę rozwiń dlaczego uznałeś, że byłem w tamtym momencie śmieszy. Dlaczego sugerowanie by nie polecać czegoś co wymaga wiedzy dot. OOP oraz relatywnie rozbudowanej implementacji osobie, która jak widać jest na etapie poznawania elementów/paradygmatów obiektówki jest śmieszne.

Cytat
Ogolnie napisalem tylko dla przykladu ze model komponentu news powinien dziedziczyc po klasie mysqlDb u mnie dziedzizy po klasie Model ktora moze obslugiwac jaka kolwiek baze a jak bedzie potrzeba to nawet rss'a innego serwisu.
OK, fajnie że u Ciebie nie jest to zrypane (jak rozumiem klasa Model jest już na szczycie hierarchii dziedziczenia), ale w przykładzie, który podałeś było... i to bardzo - więc nie dziw się mojemu komentarzowi.

Cytat
http://docs.kohanaphp.com/general/models jak widac nie jest to bledne podejscie inne fw zreszta tez tak robia :]

To że ktoś "wielki" tak robi nie oznacza, że coś jest dobrze zrobione. O ile mi dobrze wiadomo na chwilę obecną żadnej z popularnych PHP-owych FW nie obsługuje poprawnie MVC (przez co owe FW wiele tracą) - doszły mnie jedynie słuchy, że ponoć YII coś w tym aspekcie zmienia, ale jeszcze nie miałem osobiście możliwości sprawdzenia tego, więc tutaj się wypowiadać nie chcę.

Cytat
Pamietaj ze nawet jesli odp na moj post bo cos ci nie pasowalo nie znaczy ze masz racje
Nie próbuj zrobić ze mnie hipokryty. To co napisałem tyczy się każdej wypowiedzi, czy mojej, czy Twojej - to była ogólna wskazówka dla każdego czytającego dowolną informację.

Cytat
Kurde no to ja chyba czegos nie rozumiem skoro mamy:
  1. class news_Model extends model { }
To news_model tez jest modelem w tym ze specjalizuje sie w pobieraniu news'ow z bazy/xml czy czegokolwiek.
Super... tutaj się zgadzam... ale w Twoim poprzednim poście było coś zupełnie innego...
MateuszS
Crozin, dało by się jakoś uprościć twoje klasy? Tak żeby śmigały pod PHP 5.2, drugi dzień to analizuje, czytam manual ale zrozumiałem może 60% kodu a 10% filozofii wciąż nie mam pojęcia skąd, dlaczego i po co, jakby powycinać parę rzeczy albo chociaż dodać komentarze w kodzie, dokładnie opisujące akcję. sciana.gif
Crozin
Nie chcę już zaśmiecać wątku kolejnym długim listingiem kodu: http://wklej.to/ktOt (dodałem komentarze, zmieniłem nazwę jednej klasy na bardziej odpowiednią)
Cytat
a 10% filozofii wciąż nie mam pojęcia skąd, dlaczego i po co
Napisz więc z czym problem.

Co do stworzenia wersji kompaktybilnej z PHP < 5.3. Wystarczy usunąć przestrzenie nazw i powinno działać. Żeby nazwy klas miały jakiś sens wypadałoby wtedy przenieść pełną nazwę klasy (z nazwą przestrzeni nazw) do samej nazwy klasy, tj.:
Kod
\Crozin\Validation\Validatable -> Crozin_Validation_Validatable
W komentarzach masz cały czas podaną "bezwzględną" nazwę klasy więc nie powinno to być trudne.
marcio
Cytat
Cóż... proszę rozwiń dlaczego uznałeś, że byłem w tamtym momencie śmieszy. Dlaczego sugerowanie by nie polecać czegoś co wymaga wiedzy dot. OOP oraz relatywnie rozbudowanej implementacji osobie, która jak widać jest na etapie poznawania elementów/paradygmatów obiektówki jest śmieszne.

Sugerowalem sie tym co napisales:
Cytat
Jedynie mieszasz autorowi wprowadzając kolejne "dziwne nazwy" których nie rumie (Ty zresztą chyba też o czym za chwilę - bez urazy

Nie urazilem sie tylko skomentowalem :]
Cytat
Ehh... 02:00 to już trochę późno... zrozumiałem, że każda z News_XXX dziedziczy po jakimś News. Tak więc moje zarzuty względem tego fragmentu cofam.

Spoko tez czasami o 2-3 sie zagalopuje.
Cytat
OK, fajnie że u Ciebie nie jest to zrypane (jak rozumiem klasa Model jest już na szczycie hierarchii dziedziczenia), ale w przykładzie, który podałeś było... i to bardzo - więc nie dziw się mojemu komentarzowi.

Fakt zagalopowalem sie miala dziedziczyc po klasie Model ale mozna bylo sie domyslec snitch.gif

Klasa od @Crozin'a jest lepiej napisana a moze bardziej precyzujac jest lepiej zaprojektowana jednak:
http://forum.php.pl/index.php?s=&showt...st&p=728452
Tutaj kod klasy: http://3paste.com/s/1601
U mnie tez mozna latwo dodac nowe funkcje walidacji wystarczy napisac nowa funkcje walidujaca i dodac jej "dzialanie"
do switch'a i tyle wszystko hula, masz nawet wypluwanie bledow w kilku jezykach jesli chcesz.
MateuszS
Wielkie dzięki Crozin! To teraz pytania:
1.
  1. protected $validators = array();
  2. ....
  3. $result = $this->validators[$key]->isValid($subject);

Ale tu przecież $validators to tablica a nie obiekt, więc jak można odwołać się do metody tablicy?

2. Dlaczego użyłeś w klasie klasy abstrakcyjnej Base?

  1. Stanowi bazę dla wszystkich walidatorów TUTAJ ZDEFINIOWANYCH. Po prostu żebyśmy nie musieli pisać obsługi
  2. * konfiguracji dla każdego z walidatorów.


Nie za bardzo rozumiem co chciałeś przez to powiedzieć i do czego ona służy w tym skrypcie?

marcio, dzięki za link do tej klasy, potem też ją przestudiuje dokładnie,
Pozdrawiam
smentek
Tablice mają to do siebie, że przechowują dane różnych typów w tym wypadku obiekty walidatorów tak jak w kodzie Crozina.

Twój pierwotny kod zawiera jeden duży mankament. Twój walidator zwraca błędy (do tego poprzez wyjątek który jest zupełnie nie potrzebny w tym miejscu)

Walidator powinien zwrócić wartość:

true - zwalidowano poprawnie lub,
false - niepowodzenie walidacji.

I to wszystko. Na podstawie wartości zwracanej przez walidator kontynuujesz operacje albo zwracasz informacje o błędach na ekran.

czyli wywołanie powinno wyglądać:

  1. if( $x->isValide() )
  2. {
  3. //zapis do bazy lub cokolwiek
  4. }
  5. else
  6. {
  7. //informacja o błędach
  8. }
Crozin
Cytat
Twój walidator zwraca błędy (do tego poprzez wyjątek który jest zupełnie nie potrzebny w tym miejscu)
1) Wyjątków tam nie użyłem - ale w sumie lepiej by było z nich skorzystać przy obecnej wersji.
2) No tak... nazwy z serii "isXXX" same w sobie sugerują, że metoda będzie zwracać true/false. I tu też mogłoby tak być. Wtedy tylko zmieniło by się na:
  1. $result = $....->isValid($subject);
  2.  
  3. if (true == $result) {
  4. $this->data[..] = $...->getData();
  5. } else {
  6. $this->errors[...] = $...->getError();
  7. }
Kod pisałem z miejsca - nie jest on w żaden dogłęby sposób przemyślany, więc takich kruczków spodziewałbym się więcej. W każdym bądź razie dzięki za zwrócenie uwagi.

Cytat
Tablice mają to do siebie, że przechowują dane różnych typów w tym wypadku obiekty walidatorów tak jak w kodzie Crozina.
Tutaj tak tylko od siebie dodam, że to zdanie tyczy się tablic w PHP. Tablice w swojej znanej z "normalnych" języków formie przechowują dokładnie X elementów danego typu.
marcio
Cytat
Tablice w swojej znanej z "normalnych" języków formie przechowują dokładnie X elementów danego typu.

Czyli chciales powiedziec ze takie ograniczenie znajdziemy w jezykach statycznie typowanych i tych z silna typizacja, bo ogolnie chyba wszystkie jezyki interpretowane sa dynamicznie typowane.Np PHP,Python i Ruby pozwalaja na takie mieszanie typow w "tablicach"

Chcialem tylko dodac ze takie cos wedlug mnie jest prymitywne jednak statyczna i silna typizacja pozwala latwo debugowac kod error'y mamy juz w czasie kompilacji a nie jak to jest np w php w czasie dzialania "programu".A o ile mi sie wydaje nawet niektore IDE przez kompilacja potrafie wylapac takie error'y jak dodanie stringa do int :]
MateuszS
Hmm... to ja może od nowa spróbuję napisać klasę, przeanalizowałem dogłębnie obie (większość kodu rozumiem, nie licząć filozofii 'po co, dlaczego' w klasie Crozina winksmiley.jpg ). To podsumowując, co powinna posiadać dobra klasa walidująca? Bo jak sam Crozin napisałeś, twojej coś tam brakuje, postaram się w mojej to uwzględnić.
Crozin
Skoro "filozofii", któregoś kawałka nie rozumiesz to śmiało pytaj - od tego jest forum.

Tak, masz rację w moim kodzie zabrakło kilku rzeczy typu: wiele walidatorów + operacje logiczne na tych walidatorach (przykładowo, pole "nazwa użytkownika" w formularzu rejestracji może mieć dwa walidatory: 1-szy który sprawdza czy format jest prawidłowy (min 5 znaków, jedynie znaki alfanumeryczne) oraz 2-gi, który sprawdza (w bazie danych) czy podana nazwa użytkownika jest unikalna), obsługa treści komunikatów błędów (czyli jak zwróciło nam błąd: "min" z walidatora String z param.: min = 5 dla pola "nazwa użytkownika" to by wyświetliło: "Nazwa użytkownika musi mieć minimum 5 znaków.")
MateuszS
Ok to tak, nie bardzo rozumiem jeszcze czemu używasz tyle walidatorów (obiektów), przekazujesz je jako wartość tablicy, dlaczego to rozwiązanie ma przewagę nad pojedynczym obiektem. I chciałbym też aby w mojej klasie nie było tych przestrzeni nazw, nie bardzo jeszcze je opanowałem. No i czy konieczna przy tym jest klasa abstrakcyjna/interfejs?
Crozin
To zacznijmy od najłatwiejszego - przestrzeni nazw. Już pisałem jak się ich pozbyć - wystarczy usunąć namespace xxx { ... } no i pozmieniać nazwy klas, by sama nazwa oddawała lepiej charakter obiektu (teraz masz na przykład klasę: Error - i to jest ok, bo przestrzeń nazw (..\Validation) wskazuje, że jest to obiekt błędu walidacji. Bez przestrzeni nazw powinieneś to nazwać jakoś w stylu: ValidationError).

Cytat
nie bardzo rozumiem jeszcze czemu używasz tyle walidatorów (obiektów)
Dwa powody:
1) Każdy obiekt powinien zajmować się jednym, wyspecjalizowanym zadaniem. Konkretne rodzaje walidacji to już zadanie na tyle złożone, że jeden obiekt nie powinien wykonywać wszystkich rodzajów walidacji (poza tym, że musi on posiadać jakąś metodę, sprawdzającą to może on jeszcze mieć masę wewnętrznych metod (z których korzystają te sprawdzające))
2) Elastyczność... Zawsze możesz utworzyć sobie nową klasę (wystarczy, że będzie ona implementować dany interfejs) i użyć ją jako walidatora... nie jesteś ograniczony do tych kilku walidatorów napisanych pierwotnie. Możesz również utworzyć nowy walidator, który będzie rozszerzeniem innego, już istniejącego. Zauważ, że teraz walidator Email sprawdza jedynie czy format emaila jest prawidłowy. Mógłbyś sobie napisać nowy walidator, który dziedziczy po nim, ale wykonuje jeszcze jedną operację... sprawdza czy domena, którą podano w mailu nie znajduje się przypadkiem na jakieś blacklist-cie.

Teraz mógłbyś napisać... "A to jak chcę sobie dodać jakąś dodatkową opcję do walidatora maila to czemu sobie nie mam zmodyfikować istniejącego już walidatora?"
I to jest generalnie prawda - mógłbyś, ale:
1) Jeżeli projekt jest już duży mogłoby to doprowadzić do zerwania kompatybilności
2) Z czasem możesz zrobić syf w kodzie, ciągle coś tam zmieniając

Cytat
przekazujesz je jako wartość tablicy
Cóż... obiekt Walidator (ten, do którego dodajesz walidatory) musi w swoim wnętrzu wiedzieć jakie wartości jakim walidacjom poddać. Struktura: [nazwa pola] => obiekt walidatora, jest dosyć dobrą strukturą... ma swoje ograniczenia w postaci: jedno pole, jeden walidator - co na pewno musiałbyś rozbudować.

Cytat
dlaczego to rozwiązanie ma przewagę nad pojedynczym obiektem
To już opisałem - przede wszystkim czytelność i elastyczność.
Cytat
No i czy konieczna przy tym jest klasa abstrakcyjna/interfejs?
Zacznijmy od interfejsu...
No w teorii nie jest on do niczego potrzebny (PHP nie jest językiem silnie typowanym jak na przykład Java, w której raczej ciężko byłoby w ogóle skompilować kod bez użycia takiego interfejsu), ale zapewnia on zarówno polepszoną czytelność kodu (wszystkie IDE wyświetlając listę metod dostępnych dla obiektu od razu wskażą Ci, że ten parametr ma być obiektem implementującym coś tam, jak również i Ty sam czytając kod szybciej go załapiesz (po kilku tyg. nie patrzenia w niego) jak i zapewnia tego podstawową ochronę przed wrzuceniem czegoś niepoprawnego jako walidatora, przykładowo:
  1. <?php
  2.  
  3. # Jeżeli metoda Validator::addValidator() ma tak zapisane parametery: $key, $validator, wtedy:
  4.  
  5. $v->addValidator('firstname', new PDO()));
  6.  
  7. # Zadziała... tzn. kod się uruchomi a Ty dostaniesz błąd, że nie można wykonać metody isValid na tym obiekcie.
  8. # Jeżeli zaś metoda AddValidator() ma tak zapisane parametry: $key, Validatable $validator
  9.  
  10. $v->addValidator('firstname', new PDO()));
  11.  
  12. # To wywali Ci dużo przyjaźniejszy błąd: obiekt z drugiego parametru musi implementować interfejs Validatable - a nei robi tego
Na dobrą sprawę interfejsy w PHP nie są aż tak istotne (da się bez nich żyć) - tutaj wątek dot. sensu ich stosowania: Temat: Interfejsy - ale warto jest z nich korzystać.

Co do klasy abstrakcyjnej - jedyny powód, dla którego powstała to ten interfejs... wymusza on by obiekt go implementujący miał nie tylko metodę isValid, ale również dodatkowe: configure, getOpion, getOptions. Nie ma sensu bym w każdym walidatorze pisał te metody, skoro mają one robić dokładnie to samo - dlatego właśnie walidatory dziedziczą po Base (ValidatorBase w przypadku braku przestrzeni nazw).
MateuszS
Albo jestem za głupi albo... właściwie to tylko to...
Dla mnie interfejsy, to deklaracje metod które muszą zostać użyte w klasie implementującej. Tak jak ktoś napisał w tym topicu, który podałeś na przykładzie pluginów, które aby działały muszą mieć te metody. No ale to jeden sporadyczny przypadek. I bez tego można się obyć, wystarczy że sprawdzi się jakimś walidatorem pluginów czy zawiera odpowiednie klasy i wypluje ładny błąd. Mimo że przeczytałem Twój post 2 razy nadal nie widzę sensu stosowania interfejsu i klasy abstrakcyjnej w Twoim walidatorze. Rozumiem że on jakby dyktuje co ma się w klasie znaleźć, ale nie wiem czy dobrze rozumiem.
Crozin
JAk już napisałem... w PHP interfejsy nie grają istotnej roli. Ich zadanie to:
Zagwarantowanie, że jeżeli dany obiekt implementuje dany interfejs to można na nim "w ślepo" wykonywać metody zdefiniowane przez ów interfejs. Można nie trzeba. Dlaczego użyłem interfejsu? Ponieważ klasa Validator wywołuje metodę isValid() na dostarczonych jej obiektach-walidatorach. Dzięki temu, że użyto interfejsu mam pewność, że taka metoda jest zdefiniowana w obiekcie-walidatorze. W tym małym, prostym kodzie takie sprawdzanie może wydawać się sztuką dla sztuki (przecież sam piszę, to wiem, że ma, ani nie przekażę czegoś co walidatorem nie jest) i o ile do kodu nie będziesz musiał zaglądać po kilku tygodniowej przerwie, nie zdecydujesz się go "udostępnić" albo nie przyjdzie Ci pracować nad nim w jakimś zespole to będzie sztuką dla sztuki.

Nie chce mi się już powtarzać zastosowań interfejsów, bo o ile dobrze pamiętam zrobiłem to już kiedyś w podlinkowanym wątku.

Co do klasy abstrakcyjnej... Ona definiuje nam metody wspólne dla zamieszczonych tam walidatorów. Możesz się jej pozbyć, ale jeżeli to zrobisz wszystkie walidatory (String, NIP, Email) wyplują Ci błąd: brak metod getOptions, getOption, configure - w interfejsie zadeklarowałeś, że takie metody są, a w kodzie ich nie ma. Tak więc musiałbyś uzupełnić każdy walidator o te trzy metody. Klasa Validator\Base definiuje właśnie te trzy metody, byś nie musiał ich powtarzać w każdym z walidatorów.
MateuszS
Ok, czyli nie wolno pojedynczemu obiektowi w tym przypadku powierzać całe zadanie, skąd wiedzieć ile mam ich użyć w konkretnej klasie? Jest jakaś zasada? Każde pojedyncze zadanie (nawet najmniejsze) to osobny obiekt?
Crozin
Nie ma chyba jednoznacznej granicy. Po prostu... obiekty powinny mieć ściśle zakreślony obszar działania. To jak wyznaczysz ten obszar to już inna bajka. Z doświadczeniem powinieneś zacząć bezproblemowo przewidywać co może urosnąć do miana złożonego zagadnienia, a co nie.
MateuszS
Dzięki Crozin za cierpliwość (wiem że jej zasób już chyba Ci się kończy ph34r.gif) i pomoc, ale po tym wszystkim kompletnie nie mam pojęcia jak się zabrać do napisania własnej klasy. Myślę że posiadam wymaganą wiedzę teoretyczną ale chcę to zrobić podobnie jak ty, tyle że nie wiem od czego zacząć, jak to powinno wyglądać, a szkoda mojego i waszego czasu na analizowanie mojej kolejnej "beznadziejnej" klasy walidacji...
Crozin
Cóż... możesz zawsze wrzucić kod - jeżeli już nikomu się nie będzie chciało "ciągnąć tematu" to po prostu nie uzyskasz odpowiedzi. A tak... zawsze możesz się czegoś nowego dowiedzieć.
marcio
Ogolnie w jezykach takich jak Ruby/Python standartowo nie mamy ani interfejsow ani klas abstrakcyjnych.
Fakt od pythona w wersji > 2.6 bodajze niby mamy ABC(Abstract Base Classer) i trzymamy sie duck typing i jako tako nikt nie narzeka na ich brak czyli w php mozna pisac spokojnie bez nich*.
Na dodatek w Pythonie w przeciwienstwie do Ruby mamy wielokrotne dziedziczenie jak to ma miejsce w Cpp ktore dziala na podobnej zasadzie co Interfejsy.

Narazie pisz bez klas abstr. i interfejsow jesli nie wiesz jak to dziala to znaczy ze jak narazie nie jest ci potrzebna taka wiedza, piszac duzo lini kodu sam dojdziesz do wniosku ze ci sie przydadza takie narzedzia jak Interface i abstract class wtedy dopiero je uzyj, bo sama teoria jest o dupe rozczasc.

Pozdro snitch.gif

[*]Fakt lepiej jest je uzywac jak jest wieksza grupa ludzi,jak sie wroci do kodu po kilku tygodniach/miesiacach latwiej jest sie polapac,jak piszemy system oparty o modulowosc latwo utrzymac spojne api, jednak jak widac nie jest to niezbedne narzedzie.
smentek
Cytat
Tablice w swojej znanej z "normalnych" języków formie przechowują dokładnie X elementów danego typu.


To nie jest prawda. Tablice w większości języków programowania mogą zawierać null który nie będzie już typem X smile.gif. Prawdą natomiast jest że w C czy Java tablica o raz zadeklarowanej wielkości (ilości elementów) nie może ulec powiększeniu lub zmniejszeniu. Ograniczenie to pozwala na większą szybkość operacji i ma owo "wydajnościowe" uzasadnienie jedynie wtedy gdy tablica przechowuje elementu jednakowego typu (stała ilość bitów na każdy typ, dzięki można błyskawicznie przydzielić pamięć). W przypadku języków w których tablica (lub jej odpowiednik dla danego języka) może zawierac elementy różnych typów ograniczanie stałości wielkości tablicy przestaje mieć uzasadnienie co do wydajności.
MateuszS
Jak najlepiej rozwiązać problem dodawania nowych opcji walidacji, np. mam email i mam metodę/fragment kodu odpowiadającą za sprawdzanie preg_matchem czy email ma poprawny format, ale jeżeli potem zechce mi się dodać jeszcze np. maxymalną ilość znaków, dopiszę np.

  1. $obEmail = new Email(array("pattern"=>"#^[_.0-9a-z-]+@([0-9a-z][0-9a-z-]+.)+[a-z]{2,4}$#", "maxlength"=>20));


To musiałbym dopisać nową metodę albo fragment kodu w jakiejś metodzie, jak takie coś najlepiej zrobić?
smentek
Cytat(Crozin @ 24.04.2010, 16:08:03 ) *
1) Wyjątków tam nie użyłem - ale w sumie lepiej by było z nich skorzystać przy obecnej wersji.


Wyjątki służą do obsługi sytuacji nadzwyczajnych. W systemie walidacji 99% przypadków są zbędne czyli złe.

Scenariusz użycia wyjątku w walidatorze:
Twój walidator ma za zadnie sprawdzać czy wpisany numer jest jednym z numerów katalogowych produktu a owe produkty (a więc i ich numery) są przechowywane w zewnętrznej bazie danych, do której czasem może nie być dostępu, pomimo że baza naszej aplikacji działa. Obsługę takiej (NADZWYCZAJNEJ, NIETPYOWEJ, NIESTANDARDOWEJ, ŻADKIEJ, SPECJALNEJ) sytuacji załatawia nam wyrzucany wyjątek który przekieruje wykonanie naszego kodu do innego miejsca w programie, i tam to sobie obsłużymy, nie zaśmiecając walidatora kodem związanym z połączeniami z bazą danych czy czymkolwiek...

Do tego służy wyjątek.

I jeszcze się przyczepię:
Cytat(Crozin @ 24.04.2010, 16:08:03 ) *
if (true == $result) {


Piszmy:
(true === $result) nawet jeśli (true == $result) przechodzi.

MateuszS
No dobra smentek, ale może wypowiedz się na temat, zostawiając analizę czyjegoś kodu, bo nie o to w tym temacie chodzi tongue.gif
Crozin
Cytat
Tablice w większości języków programowania mogą zawierać null który nie będzie już typem X
Tam się NULLa nie czepiaj... na dobrą sprawę prawie wszędzie da się go wepchać. smile.gif

Chciałbym też zwrócić uwagę, że to co w PHP nazywamy tablicą jest w rzeczywistości o ile dobrze pamiętam Hash Table (nie wiem jak to poprawnie po polsku nazwać) w C. Ale tutaj się wypowiadać nie będę bo nie jestem dobrze zorientowany w temacie.

Jeszcze co do używania wyjątków jako zwykłej konstrukcji sterującej. W gruncie rzeczy masz rację - wyjątki są od sytuacji wyjątkowych. Czasami jednak ich cecha jaką jest wyskoczenie z bloku TRY do właściwego bloku CATCH jest czymś bardzo wygodnym i pożądanym. Elementów języka powinno się używać poprawnie, ale nie popadajmy w paranoję - jeżeli coś gdzieś mogłoby się sprawdzić lepiej warto z tego skorzystać.
A co do operatora identyczności to już zupełnie nie rozumiem... jakaś wyższość === nad == w przypadku gdy metoda zwraca true/false - chyba, że to wycinek kodu, gdzie użycie takiego operatora mogłoby mieć sensowne uzasadnienie (zbyt leniwy jestem by teraz sprawdzić skąd wziąłeś ten skrawek :]).


@MateuszS:
Z tym emailem to masz dwa błędy:
1) Takie coś jak wyrażenie to powinno być wewnętrzną sprawą tego obiektu. Swoją drogą, dlaczego nie skorzystasz z filter_var do sprawdzenia poprawności formatu?
2) Maxlength w przypadku tego walidatora jest pozbawione sensu... raz że sam format definiuje maksymalną długość maila, dwa, że od długości tekstu powinien być jakiś walidator od.. tekstu (nie od emaila).
MateuszS
No ok, ale Ty w swojej klasie zamieściłeś coś w stylu właśnie max znaków, min znaków dla konkretnego pola, starałem się robić podobnie. Czy trzeba dla np. walidacji emaila tworzyć osobną klasę? Nie można wszystkiego zamknąć w jednej - walidator? Tyle że obsługę błędów zrobić w osobnej klasie.
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.