Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Początki z obiektowym PHP
Forum PHP.pl > Forum > PHP > Object-oriented programming
marcinpruciak
Dopiero zacząłem przygodę z obiektowym PHP. Uczyłem się z książki 'Zaawansowane tworzenie stron www PHP5 Szybki start'.

Udało mi się wykombinować taki kod:

http://www.wklej.com.pl/show.php?what=20080812201428

Ma to służyć jako miniCMS, to są klasy do zarządzania podstronami.

Chciałbym się zapytać czy to jest wogóle obiektowo, bo mi się wydaje że to są zwykłe funkcje pogrupowane w klasy. 

Nie wiem czy rozumiem ideę obiektowego programowania.
wlamywacz
Niestety jest to tylko kod strukturalny zamknięty w klasach i funkcjach.
dyniaq
No trochę lipa. Utwórz np klasę podstrona i w niej metody, źle to IMO podzieliłeś. I stwórz klasę baza a nie polacz
marcinpruciak
Zmieniłem to w jedną klasę podstrony. 

Nie widzę innej możliwości rozwiązania tego. Według mnie jest wszystko dobrze. Jedna klasa pobiera dane, druga je wyświetla. O to chyba w tym chodzi, nie?
ayeo
Tak jak ktoś wyżej napisał, myślisz funkcjonalnie. Obiekt to nie zbiór funkcji. Obiekt reprezentuje jakąś konkretną "rzecz".

Pozdrawiam!
Cysiaczek
Nie, niestety nie chodzi o to. Przynajmniej nie wprost. Obiekty są po to, aby reprezentowały jakieś rzeczy. W przypadku strony www, może to być obiekt Page (zawsze używaj nazw angielskich - to bardzo ważne), który posiada właściwości i operacje. Nie jest jednak reprezentacją dokumentu HTML, tylko danych, które ta stronę tworzą (wpisy w bazie danych lub w pliku tekstowym). Poczytaj więcej, w bardziej specjalistycznych książkach smile.gif

Pozdrawiam.
qBK
W sumie nie wiem czy rozpoczęcie nauki programowania obiektowego od PHP to najlepsze rozwiązanie. Ja OOP nauczyłem się na C++ i Tobie też to polecam, chociaż na początku może być trudno, ale nie ma nic za darmo winksmiley.jpg Poza tym C++ to kanon, który co prawda bywa czasem trochę upierdliwy, bo wszystko musi się zgadzać i jest mniej elastyczne niż nowsze języki, ale to uczy porządnego programowania.
Bazując na swoim przykładzie, polecam naukę podstaw C++, potem wskaźników i wtedy programowanie obiektowe w C++.

Jeszcze odnośnie Twojego kodu, to zrobiłeś zbiór funkcji, jak poprzednicy pisali; klasy charakteryzują się tym, że obiekty z nich utworzone posiadają 1) zmienne i 2) metody (funkcje) i jedno i drugie jest w równym stopniu ważne, więc tworzenie klas tylko ze zmiennymi albo tylko z funkcjami niewiele nam daje, dopiero połączenie obu pozwala wykorzystać masę funkcjonalności.

Tworzysz klasę i później możesz stworzyć wiele obiektów, które są niezależne od siebie, mają inne dane (zmienne), ale korzystają z tych samych metod.

Trywialny przykład:
  1. <?php
  2. class Prostokat
  3. {
  4. var $szerokosc;
  5. var $wysokosc;
  6.  
  7. public function pole()
  8. {
  9. return $this->szerokosc * $this->wysokosc;
  10. }
  11. }
  12.  
  13. $p1 = new Prostokat;
  14. $p1->szerokosc = 20;
  15. $p1->wysokosc = 10;
  16. echo $p1->pole(); //200
  17.  
  18. $p2 = new Prostokat;
  19. $p2->szerokosc = 65;
  20. $p2->wysokosc = 2;
  21. echo $p2->pole(); // 130
  22. ?>


Coś bardziej zbliżonego do Twojego kodu - obiekt 'Tabela':
  1. <?php
  2. class Tabela
  3. {
  4. var $dane;
  5.  
  6. public function __construct()
  7. {
  8. $this->dane = array();
  9. }
  10. public function addRow($text)
  11. {
  12. $this->dane[] = $text;
  13. }
  14. public function display()
  15. {
  16. echo '<table>';
  17. foreach($this->dane as $row)
  18. {
  19. echo '<tr><td>'.$row.'</td></tr>';
  20. }
  21. echo '</table>';
  22. }
  23. }
  24.  
  25. $produkty = new Tabela;
  26. $produkty->addRow('margaryna');
  27. $produkty->addRow('masło');
  28. $produkty->addRow('chleb');
  29. $produkty->display();
  30. ?>

Oczywiście to też przykład trywialny, jedynie obudowanie zwykłego arraya, ale niewiele zmieniając można już poprawić trochę jakość zwyczajnej tablicy, np. walidować dane wejściowe, czyli w funkcji addRow sprawdzać czy $text jest taki jak nam odpowiada, czy nie jest za długi etc. etc.
Crozin
@qBK w PHP5 nie ma var, jest private, protected, public winksmiley.jpg
marcinpruciak
Pomęczyłem się pół dnia i trochę już łapię. Zrobiłem to tak aby jak najwięcej kodu móc wykorzystać w przyszłości.

Klasa:

http://www.wklej.com.pl/show.php?what=20080813115015

Wykorzystanie klasy:

http://www.wklej.com.pl/show.php?what=20080813115116

Wg tego zrobie potem zarządzanie newsami, menu itd, wszystko tą samą klasą.

Chciałem jeszcze przenieść zapytanie do klasy jakoś, ale nie wiem zabardzo jak i czy warto.
Shili
Jak już się uczysz, to nabieraj od podstaw sensownych przyzwyczajeń - rozdzielaj kod php od html w tym przypadku, w bardziej ogólnym zainteresuj się wzorcem MVC.

Zarządzanie wszystkim tą samą klasą?
Co ma wspólnego menu z newsami? Na zdrowy "chłopski" rozum są to dwa różne obiekty, np do jednego są komentarze, do drugiego ni hu hu, jedno i drugie jest inne. Zapoznaj się z dziedziczeniem, interfejsami (możesz sobie zrobić ogólną klasę (lub interface), faktycznie, ale potem ją implementować, czy rozszerzać do swoich potrzeb nie zawierając w nich zupełnie niepotrzebnych metod czy własności).
qBK
Cytat(Crozin @ 13.08.2008, 14:40:46 ) *
@qBK w PHP5 nie ma var, jest private, protected, public winksmiley.jpg

Hah, coś mi od początku to var nie pasowało do PHP-a, ale wszystko działało więc się nie zorientowałem winksmiley.jpg Przeglądałem pewnie jakiś stary kod i mi się zakorzeniło w głowie. A modyfikatorów nie używałem bo kod private var $x krzaczył mi się i nie wiedziałem o co chodzi biggrin.gif Dzięki za zwrócenie uwagi.

edit
@marcinpruciak:
Jeśli zrobiłeś już klasę Baza, to dodaj na przykład w niej metodę 'query' albo 'execute'

  1. <?php
  2. class Baza
  3. {
  4. ...
  5. public function query($q)
  6. {
  7. return mysqli_query($this->polacz, $q);
  8. }
  9. }
  10. ?>


i zamiast wywoływać
$r=mysqli_query($baza->polacz, $q);
możesz pisać
$r=$Baza->query($q)

Jest trochę krócej i później powiedzmy przechodząc z MySQL na PostgreSQL nie będziesz musiał każdego mysqli_query zamieniać na pg_query.
Crozin
Nie private var $abc, a private $abc winksmiley.jpg
marcinpruciak
Trochę pomyślałem, pokombinowałem i mam. Nie jest to MVC, bo gubię się przy oddzielnych trzech klasach, ale jest oddzielony widok od logiki.

  1. <?php
  2. class Publikuj
  3. {
  4. public $pub;
  5. public $new_pub;
  6. public $id;
  7.  
  8. public function pobierz_id_pub($id, $pub)
  9. {
  10.     $this->id = $id;
  11.     $this->pub = $pub;
  12. }
  13.  
  14. public function pokaz_pub()
  15. {
  16.     if($this->pub==Y) {
  17.         echo 'id.'&pub=N&#092;">TAK';
  18.     } else {
  19.         echo 'id.'&pub=Y&#092;">NIE';
  20.     }
  21. }
  22.  
  23. public function oblicz()
  24. {
  25.     if($_GET['pub']==N) {
  26.         $this->new_pub='N';
  27.     } else {
  28.         $this->new_pub='Y';
  29.     }
  30. }
  31.  
  32. public function zastosuj()
  33. {
  34.     $baza=new Baza;
  35.     if($_GET['pub']) {
  36.         $q=&#092;"UPDATE pages SET publikuj='$this->new_pub' WHERE id=\".$_GET['id'].\"\";
  37.         $r=$baza->query($q);
  38.     }
  39. }
  40.  
  41. public function przekierowanie()
  42. {
  43.         header(&#092;"Locatio: ./pages.php\");
  44. }
  45. }
  46. ?>


I użycie tej klasy w innej klasie: (muszę to jeszcze ulepszyć)

  1. <?php
  2. class Page
  3. {
  4. public $tablica;
  5. public $r;
  6.  
  7. public function query()
  8. {
  9.     $baza= new Baza;
  10.     $q=&#092;"SELECT * FROM `pages` ORDER BY `id`\";
  11.     $this->r = $baza->query($q);
  12. }
  13.  
  14. public function tworz_tabele()
  15. {    
  16.     $pub= new Publikuj;
  17.  
  18.           while($this->tablica = mysqli_fetch_array($this->r)) {
  19.             echo '
  20.                     '.$this->tablica['id'].'
  21.                     Zaznacz
  22.                     '; $pub->pobierz_id_pub($this->tablica['id'], $this->tablica['publikuj']); $pub->pokaz_pub(); $pub->oblicz(); $pub->zastosuj();
  23.                 echo '
  24.                     '.$this->tablica['tytul'].'
  25.                     Edytuj
  26.                     Usun
  27.                  ';
  28.         }
  29.         echo '';
  30.         $pub->przekierowanie();
  31. }
  32. }
  33. ?>




I co o tym myślicie? Nieźle jak na pierwszy kontakt z obiektowością?
Kicok
Cytat
Nieźle jak na pierwszy kontakt z obiektowością?

Źle. Powinieneś mieć logikę wywołującą widok, a nie widok wywołujący logikę.

Czyli najpierw myślisz co trzeba zrobić (np. na podstawie informacji z $_GET)
Potem wykonujesz te swoje działania ($pub->oblicz(), $pub->zastosuj(), $pub->pobierz_id_pub(), a wyniki zapisuj sobie na boku w jakichś zmiennych)
Na końcu, gdy już jesteś pewien co(i czy w ogóle) chcesz wyświetlić, to wstawiasz sobie te zmienne do HTML'a
f1xer
Abstrahując od twojego problemu, jeżeli chodzi o rozdzielenie logiki to poczytaj o MVC sam niedawno się z tym męczyłem i wydawało mi się to wręcz bez sensowne ale później po przemyśleniu i napisaniu pierwszego skryptu MVC zrozumiałem o co biega tak z biegu podam prosty przykład
  1. <?php
  2. class model
  3. {
  4. public function show_news($name)
  5. {
  6. // metoda modelu pobiera dane ze źródła danych i zwraca je do kontrolera
  7. $news=file_get_contents("plik/".$name.".php");
  8. return $news;
  9. }
  10. }
  11. class view
  12. {
  13. public function show_news($news)
  14. {
  15. // metoda widoku formatuje dane pobrane z modelu i wyświetla w przeglądarce
  16. echo "To jest widok newsa:<br/>zawiera html<br/>";
  17. echo "<p>".$news."</p>"
  18. }
  19. }
  20. class kontroler
  21. {
  22. public function __construct()
  23. {
  24. $model = new model;
  25. $widok= new view;
  26. }
  27. public function doPage()
  28. {
  29. // szkielet kontrolera wywoluje odpowiedni widok z odpowiednimi danymi pobranymi z
     modelu, zarządza żądaniami użytkownika
  30. if (isset($_GET['id']))
  31. {
  32. $widok->show_news($model->show_news($_GET['id']));
  33. }
  34. else
  35. {
  36. $widok->show_news($model->show_news(0));
  37. }
  38. }
  39. $strona=new kontroler;
  40. ?>
  41. <html>
  42. <head>
  43. ......
  44. </head>
  45. <body>
  46. <p>......tutaj stala czesc strony ona zawsze pozostanie taka sama biggrin.gif</p>
  47. <div class="tresc">
  48. <?php
  49. $strona->doPage();
  50. ?>
  51. </div>
  52. </body>
  53. </html>


sens powyższego jest żaden, natomiast moim zdaniem dobrze obrazuje idee MVC. Zauważ że dzięki temu, jeżeli np. będziesz chciał dodać obsługę bazy mysql, to zostanie ci do zmiany tylko model, a jeżeli będziesz chciał dać możliwość obejrzenia newsa w pdf to wystarczy dopisać odpowiedni widok.
marcinpruciak
Tylko jak poradzić sobie z pętlą? Wszystkie przykłady co widziałem nie miały takiej pętli. To jedyny sposób jaki znam. 

Bo ważniejsze zmienne nie pobieram z geta tylko z tablicy. I te wszystkie funckje też muszą być w pętli. 
f1xer
jeżeli masz jusz dane w tablicy to tą tablicę przekazujesz do widoku a w widoku wyciągasz wszystkie elementy tablicy i dekorujesz html później wyrzucasz do przeglądarki. Generalnie to jest tak w uproszczeniu

Kontroler - przyjmuje zapytanie i podejmuje decyzje co zrobić, jeżeli potrzebne są jakieś dane ze źródła danych (plik,baza,SOAP etc.) to prosi Model o przygotowanie takich danych.
Model - pobiera dane ze źródła danych i zwraca kontrolerowi (w postaći np. tablicy)
Kontroler dostaje dane i wywołuje odpowiedni widok
Widok - dostaje "surowe" dane i przygotowywuje je do prezentacji (np. dostał tablicę to robi na niek foreach i dla każdego wiersza dodaje tagi html) po czym wyrzuca do przeglądarki.
odnośnie twojego mini CMS (też taki pisze biggrin.gif) to nie potrzeba duzo roboty od frontu model ma np. 1 metode pokaz_strone($id) i wniej wczytujesz z bazy treść strony, widok też ma co najmniej jedną metodę pokaz($strona) dostaje treść strony od modelu i np. wyrzuca to pomiędzy tagami <p></p> a kontroler np. sprawdza czy w $_GET jest podane id jeżeli tak to wywołuje model z tym id a pozniej widok ze zwróconą przez model wartością, jeżeli id nie ma to wczytuje stronę domyślną.
marcinpruciak
Zedytowałem to tak:

  1. <?php
  2. class Page
  3. {
  4.  public $tablica;
  5.  public $id;
  6.  public $tytul;
  7.  public $publikuj;
  8.  public $show;
  9.  public $r;
  10.  
  11.  public function query()
  12.  {
  13.   $baza= new Baza;
  14.   $q="SELECT * FROM `pages` ORDER BY `id`";
  15.   $this->r = $baza->query($q);
  16.  }
  17.  
  18.  public function tworz_tablice()
  19.  {
  20.   $this->tablica = mysqli_fetch_assoc($this->r);
  21.   return $this->tablica;
  22.  } 
  23.  
  24.  public function tworz_strone()
  25.  {  
  26.   $pub= new Publikuj;
  27.   $this->query();
  28.   $this->tworz_tablice();
  29.   $this->id = $this->tablica['id'];
  30.   $this->tytul = $this->tablica['tytul'];
  31.   $this->publikuj = $this->tablica['publikuj'];
  32.   $pub->pobierz_id_pub($this->id, $this->publikuj); 
  33.   $pub->oblicz(); 
  34.   $pub->zastosuj();
  35.   $pub->pokaz_pub();
  36.   $this->show = $pub->show;
  37.   $this->widok();
  38.   $pub->przekierowanie();
  39.  }
  40.  
  41.  public function widok()
  42.  { 
  43.   echo '<table border="1">
  44.   <tr>
  45.   <td width="5%">#</td>
  46.   <td width="5%">Zaznacz</td>
  47.   <td width="5%">Publikuj</td>
  48.   <td width="25%">Tytul</td>
  49.   <td width="15%">Edytuj</td>
  50.   <td width="15%">Usun</td>
  51.   </tr>'; 
  52.    
  53.   while($this->tworz_tablice()) {
  54.   echo '<tr>
  55.   <td width="5%">'.$this->id.'</td>
  56.   <td width="5%">Zaznacz</td>
  57.   <td width="5%">'.$this->show.'</td>
  58.   <td width="25%">'.$this->tytul.'</td>
  59.   <td width="15%"><a href="?id='.$this->id.'">Edytuj</a></td>
  60.   <td width="15%">Usun</td>
  61.   </tr>'; 
  62.   }
  63.   echo '</table>';
  64.  }
  65.  }
  66. ?>


Chyba o to chodzi?

Mam tylko jeden problem. Metoda tworząca tablicę z wyników z bazy dodaje do tablicy tylko pierwszy wiersz z bazy? Nigdzie nie widzę błędu? Co jest nie tak?
kbsucha
Zobacz w manualu opis funkcji mysql_fetch_assoc(), zwróć uwagę na pętle while w przykładzie.

Pozdr
marcinpruciak
Udało się. Dzięki za sugestię.




Dalej ćwiczę obiektowość, stworzyłem teraz klasę dodawania i edycji stron:
http://wklej.com.pl/show.php?what=20080828234035

Jest to zgodne z obiektowością? Strona działa i ma się dobrze tutaj można zobaczyć jak:

http://195.205.202.32:6893/minicms/index.php?page=pages

Proszę o odpowiedź.
Cysiaczek
Dla każdej strony robisz osobny obiekt? Moim zdaniem bezsensu - zobacz lepiej jak wygląda kod jakiegoś fremeworka i praca z nim.
Nazwy klas po angielsku, a nazwy metod po polsku - komicznie to wygląda. Pisz TYLKO po angielsku i nie tłumacz się brakiem znajomości tego języka, bo to absolutna podstawa do nauki programowania.
Zapoznaj się z pojęciem enkapsulacji, bo $_GET wewnątrz klasy od razu włącza u każdego programisty alarm.

a co to za twór?
  1. <?php
  2. class EditPage extends AddPage{}
  3. ?>

Myślisz, że w programista systemu jakiegoś banku mógłby napisać:
  1. <?php
  2. class WyplacPieniadze extends WplacPieniadze{}
  3. ?>
?

To, co pokazujesz ma niewiele wspólnego z obiektowością - Ty jej po prostu nie rozumiesz. Kup książkę i poczytaj.
Pozdrawiam.
marcinpruciak
<P>Tylko że ja już mam książkę. "Zaawansowane tworzenie stron WWW PHP5". Jest tam ponad 100stron o obiektowości. Przeczytałem porobiłem przykłady i wydaje mi się że to co zrobiłem jest troche podobne do tego w ksiązce? </P><P>Mam kupić jeszcze jedną lepszą książkę? Jeśli tak to jaką? Najlepiej całą poświęconą obiektowiści bym chciał, ale nic nie mogłem znaleść innego niż ta co mam. </P><P>Poczytam sobie teraz o jakimś frameworku, jeśli tak radzisz.</P>
wlamywacz
Musisz potraktować taką klasę jako obiekt a nie tylko zbiór funkcji.
-=Peter=-
jak klase można potraktować jako obiekt? Klasa i obiekt to nie to samo. Nie każdego psa traktujesz jak swojego konkretnego Reksia smile.gif

Taka rada, nazwą klasy nie powinna być czasownikiem, tylko rzeczownikiem. Nazwami metod powinny być za to czasowniki. Tak więc w Twoim wypadku addPage i editPage powinny być metodami jakieś tam klasy PageManager. Więc odrazu takie klasy jak: "WyswietlPodstrony", "DodajPodstrone", "UsunPodstrone", "addPage", "editPage" są poprostu bez sensu. Kod strukturalny opakowany w klasy.

Przeglądnąłem przykłady z tej książki którą posiadasz i stwierdzam że tam nie ma kursu do nauki programowania zorientowanego na obiekty. Tam jest kurs składni oraz ogólnej wiedzy na temat obiektów. To że w tej książce nazwy klas, metod itp. są w języku polskim to wina tłumacza, nie powineneś naśladować tłumacza, który zapewne nie ma pojęcia co tłumaczy tongue.gif OOP nauczysz się z innych książek, np. PHP5. Zaawansowane programowanie (nie jest to doskonała książka, ale po przeczytaniu przynajmniej jej połowy powinieneś załapać o co biega)
wlamywacz
Tak jest to łatwiej zrozumieć początkującym smile.gif
marcinpruciak
A co powiecie na to? :

  1. <?php
  2.  
  3. class Form
  4. {
  5. public $element_text;
  6. public $element_textarea;
  7. public $element_submit;
  8. public $data;
  9.  
  10. public function __construct()
  11. {
  12. echo '<form action="'.$_SERVER['PHP_SELF'].'" method="post">';
  13. }
  14.  
  15. public function add_element_text($name, $size, $maxlength, $title, $value)
  16. {
  17. $this->element_text[] = array('name' =>$name, 'size' => $size, 'maxlength' => $maxlength, 'title' => $title, 'value' => $value);
  18. }
  19.  
  20. public function add_element_textarea($name, $rows, $cols, $title, $value)
  21. {
  22. $this->element_textarea[] = array('name' =>$name, 'rows' =>$rows, 'cols' => $cols, 'title' => $title, 'value' => $value);
  23. }
  24.  
  25. public function add_element_submit($value)
  26. {
  27. $this->element_submit[] = array('value' => $value);
  28. }
  29.  
  30. public function show()
  31. {
  32. foreach ($this->element_text as $text)
  33. {
  34. echo $text['title'].'<input type="text" name="'.$text['name'].'" size="'.$text['size'].'" maxlength="'.$text['maxlength'].'"value="'.$text['value'].'"><br />';
  35. }
  36.  
  37. foreach($this->element_textarea as $area)
  38. {
  39. echo $area['title'].'<textarea name="'.$area['name'].'" rows="'.$area['rows'].'" cols="'.$area['cols'].'">'.$area['value'].'</textarea><br />';
  40. }
  41.  
  42. foreach($this->element_submit as $submit)
  43. {
  44. echo '<input type="submit" value="'.$submit['value'].'" name="submit" /></form>';
  45. }
  46. }
  47.  
  48. public function validate()
  49. {  
  50. foreach ($this->element_submit as $submit) {
  51. if($_POST['submit']==$submit['value']) {
  52. foreach($this->element_text as $text) {
  53. if($_POST[$text['name']]) {
  54. $this->data[] = array($text['name'] => $_POST[$text['name']]);
  55. } else {
  56. echo 'Pole '.$text['title'].' zostało puste.<br />';
  57. }
  58. }
  59. foreach ($this->element_textarea as $area) {
  60. if($_POST[$area['name']]) {
  61. $this->data[] = array($area['name'] => $_POST[$area['name']]);
  62. } else {
  63. echo 'Pole '.$area['title'].' zostało puste.<br />';
  64. }
  65. }
  66. }
  67. }
  68. }
  69.  
  70. }
  71.  
  72.  
  73. $form = new Form;
  74. $form->add_element_text('title', '30', '50', 'Tytuł: ', '');
  75. $form->add_element_text('subtitle', '30', '50', 'Podytuł: ', '');
  76. $form->add_element_textarea('content', '30', '50', 'Treść: ', '');
  77. $form->add_element_submit('Zapisz');
  78. $form->show();
  79. $form->validate();
  80. echo $form->data[0]['title'];
  81. echo $form->data[1]['subtitle'];
  82. echo $form->data[2]['content'];
  83. ?>



Klasa tworząca formularz dodawania strony, będę ją mógł użyć potem do edycji strony, dodania newsa, edycji newsa, tak jak klasy QuickForm w Pear. Jeśli o to w tym chodzi znaczy że łapie to powoli. 
wrzasq
jesli chodzi o samo pojecie obiektowosci to juz lepiej. tutaj jaknajbardziej dobrze zrealizowales grupowanie w klase - kazdy obiekt reprezentuje formularz, czyli jakas rzecz i jego metody umozliwiaja ci jego kontrole.

problem jest tylko z realizacja. __construct() nie powinna wypisywac poczatku formularza. powinno sie to stac dopiero na poczatku metody show().

takze jak na poczatek to to wlasnie moze byc dobry zalazek smile.gif.
marcinpruciak
Czemu sądzisz że to dopiero zalążek, myślałem że to już gotowa klasa. Ale cieszę się że załapałem o co chodzi, mam nadzieję że pójdzie teraz z górki.
Faktycznie masz rację z tym konstruktorem, nie wiem dlaczego tak go zrobiłem, już go poprawiam.

Tylko troche mi na końcu nie wyszło tak jak bym chciał, zrobiła się dwuwymiarowa tablica, ale chyba prościej się nie da z tego wyjść. 

Można jeszcze dorobić kilka metod, na przykład lepszą walidację, ale narazie nie jest mi to potrzebne. 
Crozin
Każdy formularz na stronie może być obiektem, ale każde pole w nim również może być obiektem. Przecież operacje na elementach formularza nie ograniczają się do wyswietlenia <input type="xxx" name="xxx" value="xxx" />. Podobnie wyświetlanie - nie ma jednego schematu. Dlaczego najpierw mają być inputy, następnie textarea, a na końcu input submit? Semantyka generowanego XHTML też leży. Nie ma możliwości tworzenia labeli, fieldsetów itp.
marcinpruciak
No tak, wiem o tym, ale ja robiłem klasę przygotowaną do swoich potrzeb i na mojej stronie akurat będzie pasował taki schemat.

Teraz muszę zrobić coś żeby dodać to do bazy.

Kurcze właśnie zobaczyłęm na Helion.pl w zapowiedziach książkę 'PHP programowanie zorientowane obiektowo', nie piszą kiedy będzie, a taka by mi się teraz przydała. To ogólnie polecacie tą: 'PHP5 Zaawansowane programowanie'?
wrzasq
powiem tak - do PHP nie warto inwestowac w ksiazki za duzo, bo niestety czesto sie tutaj wszystko zmienia i ksiazka szybko sie dezaktualizuje. kup jedna tylko po to, zeby zalapac sam poczatek, rozkminic jak to ugryzc, ale mowisz, ze juz to masz za soba. teraz juz ksiazki nie beda ci raczej przydatne, bo ich autorzy czesto sa rozbierzni w zeznaniach - w PHP jest wyjatkowo wiele roznych drog. lepiej po prostu teraz zglebiaj wiedze z jakichs branzowych serwisow/czasopism/czegos co jest bardziej aktualne. ksiazki mozna czytac o C, Javie, ale PHP za szybko ewoluuje. bo jak teraz zaczniej sobie zasmiecac glowe kolejnymi wersjami "wizji obiektowego PHP" to zaraz znowu wejda przestrzenie nazw, zupelnie nowe konstrukcje, a ty utkwisz w tym, co sobie wymyslil jeden autor.

natomiast co do samej klasy - owszem, jesli robisz na wlasny uzytek tylko i wylacznie to nie ma po co dorabiac wodotryskow, jesli ma to tylko spowolnic i powiekszyc system, a ty z tego nigdy nie skorzystasz. ale tak jak zaznaczyl Crozin (i to, na co ja wskazalem poprzednio) - ta klasa sama w sobie i tak wymaga raczej dopracowania. obiekt formularza potraktuj jako kontener na inne obiekty - pola. dopiero pola maja wartosci. mozesz tutaj bardzo ladnie przecwiczyc rozne elementy programowania obiektowego w PHP - interfejsy, klasy abstrakcyjne... nie chce za duzo pisac, ani narzucac ci swojej wersji, ale ja realizuje takie mniej wiecej drzewo:

Kod
                                                       class MemoFieldComponent
                                                                        ^
                                          class PasswordFieldComponent  |
                                                    ^                   |
                                                    |                   |
class FormComponent                       class TextFieldCompoennt -----/
    ^   * public addField(FormFieldInterface) ^       ^
    |                                         |       `-- interface FormFieldInterface
    |                                         |                               |
    |                                         | class SelectFieldComponent <--/
    |                                         |      ^
    `----- abstract class HTMLComponent ------/------/
Fixus
co do tytułu "PHP5: Zaawansowane programowanie" to ja mam pozytywne odczucia. Książka jest dobrze napisana, przykłady są przejrzyste. Jednak dla dobrego zrozumienia czasami trzeba przeczytać coś parę razy - w sumie jak ze wszystkim co do nauki.

Apropo twojego ostatniego przykładu z formularzem. To nie podoba mi się to stwierdzenie:
Cytat
No tak, wiem o tym, ale ja robiłem klasę przygotowaną do swoich potrzeb i na mojej stronie akurat będzie pasował taki schemat.


Skoro to jest miniCMS to może jeszcze go kiedyś wykorzystasz, może wtedy będzie ci potrzebny lable albo coś czego tu nie ma i będzie zonk. Lepiej by było - moim zdaniem - gdybyś zrobił klasę ogólną Form, która, np. tworzy nagłówek, zamyka formularz i klasy które dziedziczą. Tak jak koledzy powiedzieli, każdy input, każdy textarea może być obiektem. Każdy z tych elementów może mieć osobną klasę dla siebie dzięki której będziesz nimi sterował i każda z tych klas może dziedziczyć po klasie Form. To daje ci elastyczność i wygodę pracy teraz i w przyszłości.
Wspominam o tych modyfikacjach między innymi dlatego, że to zasadniczo część filozofii programowania obiektowego. Dzięki temu kod jest łątwy do zrozumienia, łatwy do obsługi i modyfikacji
Crozin
Input dziedziczący po Form? Jakoś mi się to nie widzi. Ja pomiędzy Inputem, a Formem nie widzę żadnego związku - jedyne co je łączy to to, że Inputy znajdują się wewnątrz Form. Zarówno Input jak i Form mogą co najwyżej dziedziczyć po HTMLElement, ponieważ oba są elementami HTMLa. Tutaj może pomocne okazać się JS oraz DOM. Zobacz jak tam to jest rozwiązane. W JS możesz zrobić coś takiego
Kod
var fieldset = document.createElement('fieldset');
var input = document.createElement('input');

fieldset.appendChild(input);
Teraz możemy to przełożyć na PHP:
  1. <?php
  2.  
  3. $fieldset = new Fieldset;
  4. $input = new Input;
  5.  
  6. $fieldset->appendChild($input);
  7.  
  8. //możesz też wykonać inne operacje na tych obiektach
  9. $input->setType('file');
  10.  
  11. ?>
Teraz, jak to będzie wyglądało od strony klasy (a nie obiektu):
  1. <?php
  2.  
  3. class HTML_Elelement{
  4. protected
  5. $childNodes = array();
  6.  
  7. public function appendChild(HTML_Element $element){
  8. $this->childNodes[] = $element;
  9. }
  10. }
  11.  
  12. class Fieldset extends HTML_Element{
  13. public function appendChild(HTML_Element $element){
  14. if($element->getTag == 'form'){ //przyklad rozbudowy metody
  15. throw new Exception('Element <form> nie moze zawierac w sobie innych formylarz');
  16. }
  17.  
  18. parent::appendChild($element);
  19. }
  20. }
  21.  
  22. class Input extends HTML_Element{
  23. public function appendChild(HTML_Element $element){
  24. throw new Exception('Element <input> nie moze zawierac w sobie innych elementów.');
  25. //oczywiscie element Input moglby dziedziczyc z HTML_Element_XXX (nie moge nazwy wymyslec tongue.gif)
  26. //ktory by odpowiadal za elementy typu <imput />, <img />, <hr /> (te, które nie mogą posiadać w sobie innych elementów)
  27. }
  28. }
  29.  
  30. ?>
Generalnie, jeżeli uczysz się OOP napisanie właśnie zestawu klas do obsługi drzewa DOM może okazać się bardzo dobrym pomysłem. Poznasz zasady dziedziczenia, hermetyzacji, dowiesz się co to są/kiedy używać interfejsów, klas/metod abstrakcyjnych i innych zagadnień związanych z OOP.
Fixus
@Crozin - może faktycznie mnie poniosło z tym dziedziczeniem smile.gif Ale chciałem głównie pokazać koledze to, że w prosty sposób można stworzyć ładny duży formularz, bez nadmiernego wysiłku. Chodziło mi o stworzenie wielu obiektów (form, input, lable, textarea) które ułatwią pracę w przyszłości, a napewno nie utrudnią pracy na chwilę obecną. Co jest zgodne z filozofią OOP smile.gif
marcinpruciak
Kod po poprawkach, zrobiłem jedną klasę z danymi, inne po niej dziedziczą. Wszystko zrobiłem w static, nie wiem czy to dobrze

  1. <?php
  2.  
  3. class HTML_Element
  4. {
  5.     protected static $element_input;
  6.  protected static $element_textarea;
  7.  protected static $element_submit='Zapisz';
  8.          
  9.  public function show_start()
  10.  {
  11.      echo '<form method="post" action="'.$_SERVER['PHP_SELF'].'">'; 
  12.  }
  13.  
  14.  public function show_end()
  15.  {
  16.      echo '</form>';
  17.  }
  18. } 
  19.  
  20. class Input extends HTML_Element
  21. { 
  22.  public function add_element_input($type, $name, $size, $maxlength, $title, $value='')
  23.  {
  24.       parent$element_input[]=array('type' => $type, 'name' =>$name, 'size' => $size, 'maxlength' => $maxlength, 'title' => $title, 'value' => $value);
  25.  }
  26.  
  27.  public function show()
  28.  { 
  29.      foreach (parent$element_input as $text)
  30.      { 
  31.          echo $text['title'].'<input type="'.$text['type'].'" name="'.$text['name'].'" size="'.$text['size'].'" maxlength="'.$text['maxlength'].'"value="'.$text['value']; if($_POST['submit'] == parent$element_submit) { echo $_POST[$text['name']];} echo'"><br />'; 
  32.      }
  33.  }
  34. }
  35.  
  36. class Textarea extends HTML_Element
  37. {
  38.  public function add_element_textarea($name, $rows, $cols, $title, $value='')
  39.  {
  40.      parent$element_textarea[] = array('name' =>$name, 'rows' => $rows, 'cols' => $cols, 'title' => $title, 'value' => $value);
  41.  }
  42.  
  43.  public function show()
  44.  { 
  45.      foreach (self$element_textarea as $text)
  46.      {     
  47.          echo $text['title'].'<textarea name="'.$text['name'].'" rows="'.$text['rows'].'" cols="'.$text['cols'].'">'.$text['value'];  if($_POST['submit'] == parent$element_submit) { echo $_POST[$text['name']];} echo '</textarea><br />'; 
  48.      }
  49.  }
  50. }
  51.  
  52. class Submit extends HTML_Element
  53. {
  54.  public function add_element_submit($value)
  55.  {
  56.      parent$element_submit = $value;
  57.  }
  58.  
  59.  public function show()
  60.  {
  61.      echo '<input type="submit" value="'.parent$element_submit.'" name="submit" />'; 
  62.  }
  63. }
  64.  
  65. class Validate extends HTML_Element
  66. {
  67.  protected static $errors=array();
  68.  protected static $data=array();
  69.  
  70.  public function validate()
  71.  { 
  72.      if($_POST['submit'] == parent$element_submit) {
  73.          foreach (parent$element_input as $text) {
  74.              if(empty($_POST[$text['name']])) {
  75.                  self$errors[]=$text['title'];
  76.              } else {
  77.                  self$data[$text['name']] = $_POST[$text['name']];
  78.              } 
  79.          }     
  80.  
  81.          foreach (parent$element_textarea as $text) { 
  82.              if(empty($_POST[$text['name']])) {
  83.                  self$errors[]=$text['title'];
  84.              } else {
  85.                  self$data[$text['name']] = $_POST[$text['name']];
  86.              }
  87.          } 
  88.      }
  89.  }
  90.   
  91.  public function show_error()
  92.  { 
  93.      foreach(self$errors as $error) {
  94.          echo 'Uzupełnij pol: '.$error.'.<br />';
  95.          print_r($validate->data);   
  96.      }
  97.  }
  98.  
  99.  public function return_data()
  100.  {
  101.      return self$data;
  102.  }
  103. }
  104.  
  105. $html = new HTML_Element;
  106. $html->show_start();
  107. $input = new Input;
  108. $input->add_element_input('text', 'title', '30', '50', 'Podtytu: ', '');
  109. $input->add_element_input('text', 'subtitle', '30', '50', 'Podtytu: ', '');
  110. $input->add_element_input('text', 'titje', '30', '50', 'tytu: ', '');
  111. $input->add_element_input('text', 'subtjle', '30', '50', 'Podtytu: ', '');
  112. $input->show();
  113. $text = new Textarea;
  114. $text->add_element_textarea('content', '10', '50', 'Treś: ', '');
  115. $text->show();
  116. $submit = new submit;
  117. $submit->add_element_submit('Zapisz');
  118. $submit->show();
  119. $html->show_end();
  120. $validate =new Validate;
  121. $validate->validate();
  122. $validate->show_error();
  123. $data = $validate->return_data();
  124. echo '<pre>'; print_r($data); echo '</pre>';
  125.  
  126. ?>
Crozin
1) HTML_Element powinno być klasą abstrakcyjną i już NAPEWNO nie powinno mieć nic wspólnego z formularzem. Jak zapewne zauważyłeś HTML_Element powinien być klasą rodzicem dla wszystkich innych elementów, np.: input, form, p, span, a, kbd - i całej masy innych
2) Żadna z powyższych klas nie powinna nic wyświetlać. Co najwyżej metoda show() mogłaby to robić.

  1. <?
  2.  
  3. $form = new FormElement('myAction', 'myMethod');
  4. $form->append(new TextInputElement('myName', 'myValue'));
  5. $form->append(new TextareaElement('myName', 'myValue'));
  6. $form->append(new SelectElement('myName', array(
  7.  'option1' => 'value1',
  8.  'option2' => 'value2',
  9.  'option3' => 'value3',
  10.  'option4' => 'value4'
  11. ), 2));
  12. $htmlForm = $form->toString();
  13.  
  14. echo $htmlForm;
  15. ?>
Powyższe to tylko przykład.

1) Każdy element powinien dziediczyć po HTMLElement, która to dla każdego elementu udostępnia metody typu: setId(), setClass() itp. itd.
2) Elementy, które mogą zawierać w sobie inne elementy (np. <form> może zawierać inne elementy <input>) powinny mieć metodę append(), która wyglądała by mniej-więcej tak:
  1. <?
  2.  
  3. class DivEelement{
  4.  private $childNodes = array();
  5.  
  6.  public function append(HTMLElement $node){
  7.    $this->childNodes[] = $node;
  8.  }
  9. }
  10. ?>

3) Każdy element powinien posiadać methodę toString() która działałaby na takiej zasadzie:
  1. <?php
  2. public function toString(){
  3.  /*createAttributes() powinno robić coś takiego: masz sobie jakas tam tablice
  4. id => mojeID
  5. class => mojaKlasa
  6. action => mojPlik
  7. method => get
  8.   a ona robi z tego: id="mojeID" class="mojaKlasa" action="mojPlik" method="get"*/
  9.  $attributes = $this->createAttributes();
  10.  
  11.  $return = '<form' . $attributes . '>';
  12.  foreach($this->childNodes as $node){
  13.    $return .= $node->toString();
  14.  }
  15.  $return .= '</form>';
  16. }
  17. ?>
marcinpruciak
  1. <?php
  2.  
  3. abstract class HTML_Element
  4. {
  5.     
  6. } 
  7. class FormElement extends HTML_Element
  8. {
  9.  protected $method;
  10.  protected $action;
  11.  protected $class;
  12.  protected $childNodes = array();
  13.   
  14.  public function __construct($method, $action, $class) 
  15.  { 
  16.      $this->method = $method;
  17.      $this->action = $action;
  18.      $this->class = $class; 
  19.  }
  20.  
  21.  public function append(HTML_Element $node)
  22.  {
  23.      $this->childNodes[] = $node;
  24.  }
  25.  
  26.  public function createAttributes()
  27.  {
  28.      return array('id' => $this->id, 'class' => $this->class, 'action' => $this->action, 'method' => $this->method);
  29.  }
  30.  
  31.  public function toString()
  32.  {
  33.      $attributes = $this->createAttributes();
  34.  
  35.      $return = '<form' . $attributes . '>';
  36.      foreach($this->childNodes as $node) {
  37.          $return .= $node->toString();
  38.      }
  39.      $return .= '</form>';
  40.      return $return;
  41.  }
  42. }
  43.  
  44. class TextInputElement extends HTML_Element
  45. { 
  46.  public function __construct($type, $name, $size, $maxlength, $title, $value='', $class)
  47.  {
  48.       $this->type = $type; 
  49.       $this->name = $name;
  50.       $this->size = $size;
  51.       $this->maxlength = $maxlength;
  52.       $this->title = $title;
  53.       $this->value = $value;
  54.       $this->class = $class;
  55.  }
  56.  public function toString()
  57.  {
  58.      return $this->title.' <input type="'.$this->type.'" name="'.$this->name.'" size="'.$this->size.'" maxlendth="'.$this->maxlendth.'" title="'.$this->title.'" value="'.$this->value.'" class="'.$this->class.'">';
  59.  }
  60. }
  61.  
  62. class TextareaElement extends HTML_Element
  63. {
  64.  public function __construct($name, $rows, $cols, $title, $value='', $class)
  65.  {
  66.      $this->name = $name;
  67.      $this->rows = $rows;
  68.      $this->cols = $cols;
  69.      $this->title = $title;
  70.      $this->value = $value;
  71.      $this->class = $class;
  72.  }
  73.  
  74.  public function toString()
  75.  {     
  76.      return $this->title.'<textarea name="'.$this->name.'" rows="'.$this->rows.'" cols="'.$this->cols.'" class="'.$this->class.'">'.$this->value.'</textarea>'; 
  77.  }
  78. }
  79.  
  80. class SubmitElement extends HTML_Element
  81. {
  82.  public function __construct($value, $class)
  83.  {
  84.      $this->value = $value;
  85.      $this->class = $class;
  86.  }
  87.  
  88.  public function toString()
  89.  {
  90.      return '<input type="submit" value="'.$this->value.'" name="submit" class="'.$this->class.'" />'; 
  91.  }
  92. }
  93.  
  94. ?>


Zrobiłem tym sposobem, co pokazałeś, ale mam problem, bo nie wiem jak dodać do tego klasę validate w sposób żeby był zgodny z tym co już mam. 
marcio
W jakim sensie sam sie ucze ale ja bym to dal do interfejsu bo kazda klasa moze implementowac funkcje validate() dla jej danych lub do klasy abtrs. HTML_Element ale nie chce powiedziec czegos glupiego moze zle sie zrozumialem
Shili
Ja mam parę uwag:

W konstruktorach masz stanowczo zbyt dużo.
Form obejdzie się i bez klasy i action - innymi słowy w konstruktorze powinno być tylko i wyłącznie method, bo bez tego obiekt jest bezużyteczny.
Z inputem podobnie. W konstruktorach masz moim zdaniem za dużo zbędnych danych, bez których obiekt poradzi sobie bez żadnego problemu. Konstruktor jest nie po to, żeby upychać wsjo jak leci.

Klasę abstrakcyjną w takiej formie można sobie spokojnie darować.
Aktualnie jest tak, że każdy element dziedziczy po niczym (się zrymowało, hehe). Nie wiem, mistrzem OOP nie jestem i raczej w najbliższej przyszłości nie będę, ale jakoś mi się to nie widzi.

W klasie abstrakcyjnej (tudzież w interfejsie - zależy od kilku rzeczy winksmiley.jpg) mógłbyś zadeklarować na przykład metodę abstrakcyjną toString() którą masz w każdym elemencie. Ale jest to metoda publiczna - innymi słowy jakoś bardziej widzę tutaj interfejs niż klasę abstrakcyjną na razie.
Interfejs dlatego, że w aktualnym stanie te klasy nie mają nic wspólnego - jedynie metodę toString, która dodatkowo w każdej jest inaczej interpretowana. A to już domena interfejsu - są sobie klasy, mają jakieś metody o takich samych nazwach, ale inaczej się je implementuje.

Dla przykładu:
Masz kota i sweter z szorstkiej włóczki. Kot drapie i sweter drapie, prócz tego nie mają cech wspólnych - możesz dla nich obu zrobić interface z metodą drapie() (nie po angielsku, bo podejrzewam, że drapanie przez szorstki sweter nie będzie tak samo jak drapanie przez kota, poza tym to tylko przykład), ale nie ma sensu robić klasy abstrakcyjnej, w której będzie jedynie niezaimplementowana metoda. Przynajmniej moim zdaniem.
No chyba, że ta wspólna metoda jest prywatna lub chroniona, ale to inna bajka winksmiley.jpg
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.