Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Ocena stworzony klas.
Forum PHP.pl > Forum > PHP > Object-oriented programming
Pytajka
Witam,

gdyby ktoś mógł poświęcić kilka minut i byłby tak miły, żeby podsunąć mi jakieś ciekawe/ lepsze rozwiązania. Będę bardzo wdzięczny.

Dane waliduje aktualnie w kontrolerach.

Klasa obsługująca w całości bazę danych -

  1. <?php
  2.  
  3. class SQL {
  4.  
  5. private $host = 'localhost';
  6. private $user = 'user';
  7. private $password = 'password';
  8. private $database = 'datebase';
  9.  
  10. static public $query;
  11.  
  12. public function connection(){
  13.  
  14. $connect = mysql_connect( $this->host, $this->user, $this->password);
  15.  
  16. $database = mysql_select_db( $this->database);
  17.  
  18. $set = mysql_query('SET CHARSET utf8');
  19.  
  20.  
  21. }
  22.  
  23. public function disconnection(){
  24.  
  25. mysql_close( $connect);
  26.  
  27. }
  28.  
  29. static public function query( $query){
  30.  
  31. return self::$query = mysql_query( $query);
  32.  
  33. }
  34.  
  35. static public function getRows(){
  36.  
  37. return mysql_num_rows( self::$query);
  38.  
  39. }
  40.  
  41. }
  42.  
  43. ?>


Inna klasa przykładowa -

  1. <?php
  2.  
  3. class XXX {
  4.  
  5. public $test = '0';
  6.  
  7. public function add( $title, ...){
  8.  
  9. if( $query = SQL::query("INSERT INTO ...)")){
  10.  
  11. return true;
  12.  
  13. }
  14. else{
  15.  
  16. return false;
  17.  
  18. }
  19.  
  20. }
  21.  
  22. public function delete( $id, $login){
  23.  
  24. if( $query = SQL::query("DELETE * FROM `...` WHERE `id` = '$id' AND `author` = '$login'")){
  25.  
  26. return true;
  27.  
  28. }
  29. else{
  30.  
  31. return false;
  32.  
  33. }
  34.  
  35. }
  36.  
  37. public function load_all(){
  38.  
  39. if( $query = SQL::query("SELECT * FROM `...` WHERE `test` = '$this->test' ORDER BY `id` DESC")){
  40.  
  41. if( SQL::getRows() > 0){
  42.  
  43. return $query;
  44.  
  45. }
  46.  
  47. }
  48. else{
  49.  
  50. return false;
  51.  
  52. }
  53.  
  54. }
  55.  
  56. public function load_id( $id){
  57.  
  58. if( $query = SQL::query("SELECT * FROM `...` INNER JOIN `user` ON `...`.author = `user`.id WHERE `...`.id = '$id'")){
  59.  
  60. if( SQL::getRows() > 0){
  61.  
  62. return $query;
  63.  
  64. }
  65.  
  66. }
  67. else{
  68.  
  69. return false;
  70.  
  71. }
  72.  
  73. }
  74.  
  75. public function load_top(){
  76.  
  77. return $query = SQL::query("SELECT * FROM `...` ORDER BY rand() DESC LIMIT 0,5");
  78.  
  79. }
  80.  
  81.  
  82. }
  83.  
  84. ?>
adamec
Ja bym nie tworzył
  1. function connection()


tylko

  1. function __construct()


to samo z destruktorem
CuteOne
  1. function connection(){
  2.  
  3. $connect = mysql_connect( $host, $user, $password);
  4.  
  5. $database = mysql_select_db( $database);
  6.  
  7. $set = mysql_query('SET CHARSET utf8');
  8. }
  9.  
  10. function disconnection(){
  11.  
  12. mysql_close( $connect);
  13. }
  14.  
  15. function query( $query){
  16.  
  17. return mysql_query( $query);
  18.  
  19. }


Tak wygląda Twój kod pisany strukturalnie... różnica między Twoim a moim kodem to użycie $this i static. To chyba za mało jak na OOP.

Zapraszam do lektury dokumentacji Zenda(marna ale zawsze coś), PDO i podstawy OOP
Pytajka
A może ktoś podrzuciłby mi jakiś prosty CMS OOP z MVC, jednak nie pisany w żadnym frameworku?
Fifi209
adamec a ja bym użył singletonu
greycoffey
Fifi209, a nagle zachodzi potrzeba użycia dwóch połączeń do bazy danych - bo jedna zawiera ogromne ilości danych roboczych, a w drugiej są już te dane przetworzone. Jak byś zrobił taką przetwarzarkę, kiedy połączenie z baża danych było tylko jedno? Singleton to antywzorzec, w 99.99% przypadków NIE JEST potrzebny.

Co do autora, kod obiektowy to to nie jest, tylko opakowany w klasy. Connection, to mogłaby być zmienna przechowująca uchwyt do bazy, funkcja to connect() lub disconnect(). Disconnection nie może istnieć, bo jest niepoprawne gramatycznie. Ktoś dobrze napisał, żeby zrobić to w metodach __construct() i __destruct(). Nie rozumiem stosowania pól statycznych, jak już robisz typową klasę uczących się programowania obiektowego, czyli sterownik do bazy danych, fajnie byłoby gdyby sterownik do bazy danych komunikował się z managerem baz danych. Raz stosujesz mojaFunkcja() a raz moja_funkcja() - zdecyduj się na jedno nazewnictwo, bo potem obudzisz się z ręką w nocniku jak przyjdzie więcej kodu.

Skrobnąłem Ci przykładowy kod, w którym możesz zobaczyć zarys projektowy (?) przykładowej klasy do obsługi bazy danych. Pisałem z palca i nie testowałem, więc może są jakieś drobne błędy, które sobie poprawisz. Już teraz widzę, że rozszyfrowywaniem $dsn powinien się zajmować DatabaseManager, drivery powinny być przechowywane w tablicy DatabaseManeger::$drivers jako np. 'mysql'=>$mysqlDriver, a DatabaseManager powinien sprawdzać czy istnieje tam system bazodanowy z dsn i na podstawie tego wybrać sterownik lub zwrócić wyjątek, że nie znaleziono pasującego sterownika. To także zostawiam dla Ciebie wink.gif

  1. <?php
  2.  
  3. interface DatabaseDriver
  4. {
  5. public function __construct($dsn);
  6. public function __destruct();
  7. public function executeQuery($query);
  8. public function getNumberOfRows($result);
  9. public function getOneRow($result);
  10. // public function getAllRows($result); // to potem można zaimplementować, mi się nie chciało już
  11. }
  12.  
  13. class DatabaseDriver_MySQL implements DatabaseDriver
  14. {
  15. protected $resource;
  16.  
  17. public function __construct($dsn)
  18. {
  19. // jakoś rozbijamy $dsn na $hostname, $username, $password, $databasename
  20. $this->resource = mysql_connect($hostname, $username, $password);
  21. if(!$this->resource)
  22. throw new Exception("Coś poszło źle!");
  23. if(!mysql_select_db($databasename, $this->resource))
  24. throw new Exception("Baza danych '$databasename' nie istnieje!");
  25. }
  26.  
  27. public function __destruct()
  28. {
  29. mysql_close($this->resource);
  30. }
  31.  
  32. public function executeQuery($query)
  33. {
  34. $result = mysql_query($query);
  35. if(!$result)
  36. throw new Exception("Houston, mamy problem!");
  37. return $result;
  38. }
  39.  
  40. public function getNumberOfRows($result)
  41. {
  42. return mysql_num_rows($result); //tu mozna jakas walidacje dorobic jeszcze
  43. }
  44.  
  45. public function getOneRow($result)
  46. {
  47. return mysql_fetch_assoc($result); //tu tez^^
  48. }
  49. }
  50.  
  51. class DatabaseDriver_PostgreSQL implements DatabaseDriver
  52. {
  53. protected $resource;
  54.  
  55. public function __construct($dsn)
  56. {
  57. // jakoś rozbijamy $dsn na $hostname, $username, $password, $databasename
  58. $this->resource = pg_connect("host=$hostname dbname=$databasename user=$username password=$password");
  59. if(!$this->resource)
  60. throw new Exception("Coś poszło źle!");
  61. }
  62.  
  63. public function __destruct()
  64. {
  65. pg_close($this->resource);
  66. }
  67.  
  68. public function executeQuery($query)
  69. {
  70. $result = pg_query($query);
  71. if(!$result)
  72. throw new Exception("Houston, mamy problem!");
  73. return $result;
  74. }
  75.  
  76. public function getNumberOfRows($result)
  77. {
  78. return pg_num_rows($result); //tu mozna jakas walidacje dorobic jeszcze
  79. }
  80.  
  81. public function getOneRow($result)
  82. {
  83. return pg_fetch_assoc($result); //tu tez^^
  84. }
  85. }
  86.  
  87. class DatabaseManager
  88. {
  89. protected $driver;
  90.  
  91. public function attachDriver(DatabaseDriver $driver)
  92. {
  93. $this->driver = $driver;
  94. }
  95.  
  96. public function __call($methodName, $arguments)
  97. {
  98. return call_user_func_array(array($this->driver, $methodName), $arguments);
  99. }
  100. }
  101.  
  102. $dm = new DatabaseManager;
  103. $dd = new DatabaseDriver_PostgreSQL("pgsql:host=myhost;dbname=mydatabasename;user=myuser;password=mypass");
  104. $dm->attachDriver($dd);
  105. $result = $dm->executeQuery("SELECT * FROM tabela");
  106. if($dm->getNumberOfRows($result)>0)
  107. {
  108. while($row = $dm->getOneRow($result))
  109. {
  110. // wyswietlasz dane ;)
  111. }
  112. }
Pytajka
Oby więcej takich osób, dzięki wielkie.

Co do metod statycznych, tylko w taki sposób mogę wywoływać zapytania do bazy za pomocą tych metod w innej klasie.
greycoffey
Kilka możliwości:
  • Odradzany już wcześniej Singleton
  • wzorzec Registry (statyczna klasa przechowująca obiekty)
  • wzorzec Dependency Injection (po prostu przekazujesz potrzebne instancje konstruktorowi klasy, która ich wymaga)


Generalnie metody statyczne przydają się w serwowaniu innych obiektów lub w wypadku, gdy chcemy upchać kilka związanych ze sobą tematycznie acz autonomicznych funkcji w ładną klasę.
Fifi209
Cytat(greycoffey @ 19.04.2012, 15:31:38 ) *
Fifi209, a nagle zachodzi potrzeba użycia dwóch połączeń do bazy danych

Odwołuje się do kodu kolegi, nie zakładał w swoim więcej niż jednego połączenia.

Co do Twojego kodu, jeżeli już tak to ja bym użył wzorca Factory, i tak dla każdego sterownika podajesz host, usera, hasło, etc.
ew. po argumencie określającym sterownik, żądać konkretnych danych.
Crozin
@greycoffey: Jeżeli do metody jakiegoś obiektu musisz przekazać w argumencie źródło/strukturę danych dzięki, którym metoda będzie mogła wykonać swoje podstawowe zadanie to niemal na pewno coś jest zrypane. To tak apropos DatabaseDriver::getNumberOfRows/getOneRow/getAllRows(). Dodatkowo jeżeli jakiś obiekt musi polegać na innym, którego obecność jest bezwarunkowo konieczna, powinien on zostać wstrzyknięty już w konstruktorze. To tak apropos DatabaseManager::attachDriver().
Pytajka
To ja tak z innej beczki, jak odwołać się do metody z tej klasy, w innej klasie?
Crozin
Chyba chciałeś zapytać o to jak z poziomu obiektu odwołać się do metody innego obiektu, który został utworzony wcześniej w zupełnie innym miejscu? No to musisz ten obiekt przekazać do obiektu, który będzie miał wykonywać na nim jakiejś metody. Przekazać obiekt możesz jako argument konstruktora.
greycoffey
Cytat(Crozin @ 19.04.2012, 23:12:13 ) *
@greycoffey: Jeżeli do metody jakiegoś obiektu musisz przekazać w argumencie źródło/strukturę danych dzięki, którym metoda będzie mogła wykonać swoje podstawowe zadanie to niemal na pewno coś jest zrypane. To tak apropos DatabaseDriver::getNumberOfRows/getOneRow/getAllRows(). Dodatkowo jeżeli jakiś obiekt musi polegać na innym, którego obecność jest bezwarunkowo konieczna, powinien on zostać wstrzyknięty już w konstruktorze. To tak apropos DatabaseManager::attachDriver().


Tak, widzę, chyba właściwie te metody trzebaby oddelegować do obiektu powiedzmy DatabaseResult_SystemBazodanowy. Co do DatabaseManager::attachDriver() to napisałem trochę wyżej, że DatabaseManager powinien sam wybierać i utworzyć instancję sterownika danego systemu bazodanowego na podstawie DSN. Niemniej jednak dzięki za rady, masz u mnie pewien autorytet, bo zawsze piszesz mądrze i na temat, zresztą kiedy zobaczyłem kilka miesięcy temu twój kod walidatora gdzieś w tym dziale, zrozumiałem dlaczego to OOP jest takie fajne wink.gif
tomsol
a ja se kiedys takie cos napisalem - config z xmla
  1. Class MySQLDriver
  2. {
  3. private $host;
  4. private $login;
  5. private $password;
  6. private $database;
  7.  
  8. private $setup;
  9. private $errors;
  10. private $access;
  11.  
  12. private $xml;
  13. private $configXML;
  14.  
  15. /**
  16. * @param
  17. * 0 - config pierwszy z pliku xml;
  18. * 1 - config drugi z pliku xml;
  19. * 2 - config trzeci z xml;
  20. * X - config X z pliku xml;
  21. */
  22. public function __construct($setup)
  23. {
  24. // parametry konstruktora
  25.  
  26. //$this->host = $host;
  27. //$this->login = $login;
  28. //$this->password = $passwd;
  29. //$this->database = $database;
  30.  
  31. $this->setup = $setup;
  32. $this->errors = array();
  33. $this->access = 0;
  34. $root = $_SERVER['DOCUMENT_ROOT'];
  35.  
  36. $this->xml = $root.'/sciezka/setup/config.database.xml';
  37. $this->configXML = simplexml_load_file($this->xml);
  38. }
  39.  
  40.  
  41. /**
  42. * @return nazwe hosta;
  43. */
  44. private function getHost()
  45. {
  46. return $this->host = $this->configXML->config[$this->setup]->host;
  47. }
  48. /**
  49. * @return login do bazy;
  50. */
  51. private function getLogin()
  52. {
  53. return $this->login = $this->configXML->config[$this->setup]->login;
  54. }
  55. /**
  56. * @return haselko do bazy;
  57. */
  58. private function getPasswd()
  59. {
  60. return $this->passwd = $this->configXML->config[$this->setup]->passwd;
  61. }
  62. /**
  63. * @return baze ktora uzywamy;
  64. */
  65. private function getDatabase()
  66. {
  67. return $this->database = $this->configXML->config[$this->setup]->database;
  68. }
  69.  
  70. /**
  71. * @return true or false
  72. * @todoSprawdzamy polaczenie do bazy danych czy poprawny login, haslo, localhost
  73. */
  74. private function isConnect()
  75. {
  76. $c = mysql_connect($this->getHost(), $this->getLogin(), $this->getPasswd());
  77. return ($c)? $c : 0;
  78. }
  79. /**
  80. * @return true or false
  81. * @todoSprawdzamy czy jest taka baza do wyboru
  82. */
  83. private function isDbSelect()
  84. {
  85. $s = mysql_select_db($this->getDatabase(), $this->isConnect());
  86. return ($s)? 1 : 0;
  87. }
  88. /**
  89. * @return true or false
  90. * @todoSprawdzamy kodowanie bazy danych
  91. * @param utf-8
  92. */
  93. private function isDbEncode()
  94. {
  95. return mysql_query("set names utf8;")? 1 : 0;
  96. }
  97.  
  98.  
  99. /* Sprawdzamy nasze polaczenie czy poprawne paramsy*/
  100. private function verifyConnection()
  101. {
  102. try
  103. {
  104. if(!$this->isConnect())
  105. throw new Exception("Błąd połączenia bazy danych.");
  106. if(!$this->isDbSelect())
  107. throw new Exception("Wybrana baza danych nie istnieje.");
  108. if(!$this->isDbEncode())
  109. throw new Exception("Błąd w zapisie kodowania bazy fanych.");
  110. $this->access = 1;
  111. }
  112. catch (Exception $err)
  113. {
  114. $this->errors[] = $err->getMessage();
  115. }
  116. }
  117.  
  118.  
  119. public function accessToDb()
  120. {
  121. $this->verifyConnection();
  122. return $this->access;
  123. }
  124.  
  125. public function showError()
  126. {
  127. foreach ($this->errors as $er=>$value)
  128. {
  129. echo '<div class="db_error">' . $value . '</div>';
  130. }
  131. }
  132.  
  133. }
greycoffey
@tomsol, nie chcę cię nziechęcać, ale klasa jest daremnie napisania. PhpDoc bardziej uttaj utrudnai niż pomaga - zawierasz opis funkcji w @todo, ustawiasz @param gdzie nie ma żadnych parametrów, true or false to boolean, w @param zreszta nie podajesz zmiennej. Sztywno podajesz ścieżki do pliku konfiguracyjnego, btw. konfiguracja powinna być zewnętrzną klasą. Metoda getDatabase() nie opisuje zawartości - pobierasz NAZWĘ bazy, nie ją całą. Metody is* u Ciebie wykonują akcję, powinny zwracać boolean i jedynie sprawdzać stan czegoś. Lączyć powinna się metoda connect(), nie isConnect() [btw. powinno być isConnected() - powtarzająć błąd , lub connectAndReturnHandleToDatabase()] - za każdym razem tworzysz nowy uchwyt do bazy, zamiast przechować go w prywatnej zmiennej klasy. Metody verifyConnection() i accessToDb() można złączyć jedno, a showError() to już zadanie dla innej klasy.

Odkąd przeczytałem "Clean Code", zauważyłem jak można uprosić sobie pracę, opisując dobrze funkcje i zmienne - tak, by nie mieć zastrzeżen co do ich użycia, a długich nazw, jeśli wyjdą takie, nie trzeba wpisywać całych, bo IDE nam podpowie.
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.