Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: obiekt klasy X powinien wywolac metode z klasy Y bez dziedziczenia
Forum PHP.pl > Forum > PHP
wiewiorek
Jeśli mam klasę o nazwie Polaczenie z prywatnym konstruktorem do połączenia z bazą danych, a chciałbym używać PDO (PDO umożliwia komunikację z wieloma bazami danych) to przez to, że mam prywatny konstruktor to nie mogę dziedziczyć po klasie PDO, bo inaczej dostaję błąd:
Access level to Polaczenie::__construct() must be public (as in class PDO)

A ja chcialbym moc wysylac zapytania do bazy danych przy uzyciu funkcji prepare() pochodzacej z klasy PDO w ten sposob:
  1. $polaczenie = Polaczenie::getInstance();
  2. $zapytanie = $polaczenie->prepare("INSERT INTO ksiazki(tytul, cena) VALUES(:tytul, :cena)");
  3. $zapytanie->bindValue(':tytul', $_POST['tytul'], PDO::PARAM_STR);
  4. $zapytanie->bindValue(':cena', $_POST['cena'], PDO::PARAM_STR);
  5. $zapytanie->execute();


Ale dostaje blad:
Call to undefined method Polaczenie::prepare()

Bo ta metoda jest w klasie PDO. Co mam zrobic ? Moglbym caly powyzszy kod wstawic do specjalnie utworzonej w tym celu metody w klasie Polaczenie i wtedy byloby ok, ale co ja mam tworzyc dla kazdego inserta oddzielna metode w klasie Polaczenie ? To jak mialbym inserty wykonywane na tabelach np. ksiazki, autorzy itd. ladowac je wszystkie do klasy Polaczenie ? Bez sensu. Co powinienem zrobic ?
kfc4
Cytat
Call to undefined method Polaczenie::prepare()
Czyli, że nie ma tej funkcji

Ustaw ten konstruktor na publiczny i już.
wiewiorek
Nie moge ustawic na publiczny w tym problem, bo chce miec singletona smile.gif A obecnie mam cos takiego:

  1. final class Polaczenie {
  2.  
  3. private static $oInstance = false;
  4. private $pdo;
  5.  
  6. private $dsn = 'mysql:host=localhost;dbname=koszyk';
  7. private $uzytkownik = 'root';
  8. private $haslo = '';
  9.  
  10.  
  11. private function __construct() {
  12. try {
  13. $this->pdo = new PDO($this->dsn, $this->uzytkownik, $this->haslo, array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8"));
  14. $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  15.  
  16. }
  17. catch(PDOException $e) {
  18. echo '<p>Polaczenie nie moglo zostac utworzone: '.$e->getMessage().'</p>';
  19. }
  20. }
  21.  
  22. public function __destruct() {
  23.  
  24. }
  25.  
  26.  
  27. public static function getInstance() {
  28. if (self::$oInstance == false) {
  29. self::$oInstance = new klasaPolaczenie();
  30. }
  31. return self::$oInstance;
  32. }
  33.  
  34. }


Czy jest wiec jakies wyjscie ?
erix
  1. rejestr z połączeniem
  2. instancjowanie z podaniem klasy połączenia jako argumentu konstruktora

Chociaż jeśli chodzi o połączenia do DB singleton jest dla mnie trochę mało wygodnym wyjściem, gdyż jeśli zechcesz korzystać z kilku baz naraz (może abstrakcyjne, w praktyce - przydaje się), to już masz trochę twardszy orzech do zgryzienia.
Void
Możesz też dodać do swojej klasy potomnej metody, które wykorzystujesz w PDO o odpowiednich nazwach (np. query, prepare) i wywoływać za pomocą parent:: metody główne w klasie PDO. Np.
  1. class Polaczenie extends PDO
  2. {
  3. //...
  4.  
  5. public function prepare($query)
  6. {
  7. return parent::prepare($query);
  8. }
  9. }

Rozwiązanie o tyle dobre, że można dodać np. zliczanie lub cache'owanie zapytań.
wiewiorek
Erix a mógłbyś rozwinąć myśl, bo nie bardzo rozumiem. "rejestr z połączeniem" - to w ogóle pierwszo słyszę, nie jestem w tym zaawansowany.

Void nie rozumiem - bo przecież nie mogę dziedziczyć po PDO - czy też może to rozwinięcie mysli Erixa, Erix zaproponował jakiś sposób, że będę mógł dziedziczyć po PDO (a którego ja nie zrozumiałem), a Ty kontynuujesz Jego mysl ?
erix
Cytat
to w ogóle pierwszo słyszę, nie jestem w tym zaawansowany.

Wzorzec registry.
Void
Cytat(wiewiorek @ 7.09.2009, 20:23:51 ) *
Void nie rozumiem - bo przecież nie mogę dziedziczyć po PDO - czy też może to rozwinięcie mysli Erixa, Erix zaproponował jakiś sposób, że będę mógł dziedziczyć po PDO (a którego ja nie zrozumiałem), a Ty kontynuujesz Jego mysl ?

Nie, ja przedstawiłem inną myśl smile.gif Tylko masz rację, zapomniałem o tym prywatnym konstruktorze tongue.gif Co nie zmienia faktu, że możesz wykorzystać to, co napisałem, tylko zamiast dziedziczyć po PDO musisz wykorzystać kompozycję (czyli umieszczenie instancji klasy PDO w polu twojej klasy - to co zrobiłeś w swoim drugim poście). Wtedy byłoby tak:
  1. class Polaczenie
  2. {
  3. // Instancja klasy pdo
  4. private $_pdo;
  5. //...
  6.  
  7. public function prepare($query)
  8. {
  9. return $this->_pdo->prepare($query);
  10. }
  11.  
  12. public function query($query
  13. {
  14. return $this->_pdo->query($query);
  15. }
  16.  
  17. // itd.
  18. }

Albo wywal tego singletona, wtedy można zastosować dziedziczenie smile.gif Poza tym, tak jak pisał erix, czasem może się przydać obsługa kilku różnych baz danych.
wiewiorek
Serdecznie dziękuję za pomoc. Więc zgodnie z tym co napisał Erix i na podstawie klasy SingletonRegistry ze strony: http://www.phpfreaks.com/tutorial/design-p...gleton-registry zrobiłem tak:
  1. final class Polaczenie {
  2.  
  3. private static $oInstance = false;
  4. private $pdo;
  5. private $_map; //dla wzorca registry
  6.  
  7.  
  8. private $dsn = 'mysql:host=localhost;dbname=koszyk';
  9. private $uzytkownik = 'root';
  10. private $haslo = '';
  11.  
  12.  
  13.  
  14. private function __construct() {
  15. try {
  16. $this->pdo = new PDO($this->dsn, $this->uzytkownik, $this->haslo, array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8"));
  17. $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  18.  
  19. $this->set('pdo', $this->pdo);
  20. }
  21. catch(PDOException $e) {
  22. echo '<p>Polaczenie nie moglo zostac utworzone: '.$e->getMessage().'</p>';
  23. }
  24. }
  25.  
  26. public function __destruct() {
  27.  
  28. }
  29.  
  30.  
  31. public static function getInstance() {
  32. if (self::$oInstance == false) {
  33. self::$oInstance = new klasaPolaczenie();
  34. }
  35. return self::$oInstance;
  36. }
  37.  
  38.  
  39. public function get($key)
  40. {
  41. return $this->_map[$key];
  42. }
  43.  
  44.  
  45. public function set($key, $object)
  46. {
  47. $this->_map[$key] = $object;
  48. }
  49.  
  50. }



Czyli dodałem:
  1. private $_map;
  2.  
  3. public function get($key)
  4. {
  5. return $this->_map[$key];
  6. }
  7.  
  8.  
  9. public function set($key, $object)
  10. {
  11. $this->_map[$key] = $object;
  12. }
  13.  
  14. //+ dla konstruktora: $this->set('pdo', $this->pdo);


A nastepnie:
  1. $zapytanie = $polaczenie->get('pdo')->prepare("INSERT INTO ksiazki(tytul, cena) VALUES(:tytul, :cena)");
  2. $zapytanie->bindValue(':tytul', $_POST['tytul'], PDO::PARAM_STR);
  3. $zapytanie->bindValue(':cena', $_POST['cena'], PDO::PARAM_STR);
  4. $zapytanie->execute();


I jest ok.

Ale jako ze teraz przeczytalem sposob Voida to skorzystam chyba z Jego sposobu smile.gif Chyba, bo wszedzie pisza (w tym Wy piszecie) zeby nie korzystac z singletona to chcialbym sie zapytac co zamiast singletona gdy na jednej stronie uzywam np. 5. obiektow klas, w ktorych potrzebuje polaczenia z baza danych wiec tworze w ich konstruktorach obiekty klasy Polaczenie przez wywolanie statycznej metody getInstance():
  1. $this->polaczenie = Polaczenie::getInstance();

Czy jesli usune Singletona to czy nie bede miec 5. polaczen do bazy danych zamiast 1. ?
erix
Nie będziesz miał pięciu... Przecież skoro pobierasz uchwyt przez singleton, to zwracasz REFERENCJĘ, a nie klonujesz obiekt.

A tak w ogóle, to w Twoim przypadku może (nie próbowałem, na razie tylko klepane z palucha) byłoby najlepiej zrobić podobnie, jak singleton (ale nie tak samo winksmiley.jpg):

  1. abstract class modelClass{
  2. static protected $connection = null;
  3.  
  4. function __construct(){
  5. if(modelClass::$connection===null){
  6. modelClass::$connection = new PDO(...);
  7. }
  8. }
  9. }
  10.  
  11. class zabawkaModel extends modelClass{
  12.  
  13. function getBy($var){
  14. $q = parent::$connection->query('asdasd');
  15. // ...
  16. }
  17.  
  18. }
  19.  
  20. $model = new zabawkaModel();
  21. $model->getBy('asdasdsad');


I wciąż mamy jedno połączenie, modeli tworzysz ile chcesz. :]
wiewiorek
Erix wielkie dzieki za propozycje, niestety po zastosowaniu Twego sposobu do swojej klasy (i oczywiscie zmodyfikowaniu Twego kodu pod swoje potrzeby) dostaje blad ( tongue.gif ):
Call to a member function pobieranieDanych() on a non-object
u Ciebie to bylaby ta linia:
  1. $q = parent::$connection->query('asdasd');


Jesli chodzi o singleton to wiem, ze zawsze jest jedno polaczenie, chodzilo mi o sytuacje gdybym nie zastosowal wzorca Singleton do klasy Polaczenie,
a inne klasy dziedziczylyby po klasie Polaczenie - przyklad:

  1. class Polaczenie extends PDO{
  2.  
  3. private $pdo;
  4.  
  5. private $dsn = 'mysql:host=localhost;dbname=koszyk';
  6. private $uzytkownik = 'root';
  7. private $haslo = '';
  8.  
  9.  
  10. public function __construct() {
  11. try {
  12. $this->pdo = new PDO($this->dsn, $this->uzytkownik, $this->haslo, array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8"));
  13. $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  14. }
  15. catch(PDOException $e) {
  16. echo '<p>Polaczenie nie moglo zostac utworzone: '.$e->getMessage().'</p>';
  17. }
  18. }
  19.  
  20. public function __destruct() {
  21. }
  22.  
  23.  
  24. public function zapytanie($z) {
  25. try{
  26. $wynik = $this->pdo->query($z);
  27. $wynik_tablica = $wynik->fetchAll();
  28. $wynik -> closeCursor();
  29. return $wynik_tablica;
  30. }
  31. catch(PDOException $e) {
  32. echo '<p>blad w metodzie pobieranieDanych(): '.$e->getMessage().'</p>';
  33. }
  34. }
  35.  
  36. }
  37.  
  38. ------------------------------
  39. class Ksiazki extends Polaczenie
  40. {
  41. private $polaczenie;
  42.  
  43. public function __construct()
  44. {
  45. $this->polaczenie = new Polaczenie();
  46. }
  47.  
  48. public function __destruct()
  49. {
  50. }
  51.  
  52. public function pobierzKsiazki()
  53. {
  54. return $this->polaczenie->zapytanie("SELECT * FROM ksiazki");
  55. }
  56. }
  57.  
  58. --------------------------------------------------------------
  59.  
  60. class Autorzy extends Polaczenie
  61. {
  62. private $polaczenie;
  63.  
  64. public function __construct()
  65. {
  66. $this->polaczenie = new Polaczenie();
  67. }
  68.  
  69. public function __destruct()
  70. {
  71. }
  72.  
  73. public function pobierzAutorow()
  74. {
  75. return $this->polaczenie->zapytanie("SELECT * FROM autorzy");
  76. }
  77. }
  78.  
  79. -------------------------
  80.  
  81. $ksiazki = new Ksiazki();
  82. $rezultat1 = $ksiazki->pobierzKsiazki();
  83.  
  84.  
  85. $autorzy = new Autorzy();
  86. $rezultat2 = $autorzy->pobierzAutorow();
  87.  


To wtedy utworzenie dwoch obiektow oznaczaloby pewnie dwa polaczenia do bazy danych zamiast jednego jak w przypadku zastosowania wzorca singleton do klasy Polaczenie.





Najlepszym wyjsciem jest chyba tworzenie obiektu klasy Polaczenie i przekazywanie go do konstruktorow pozostalych klas:


  1. class Polaczenie extends PDO{
  2.  
  3. private $pdo;
  4.  
  5. private $dsn = 'mysql:host=localhost;dbname=koszyk';
  6. private $uzytkownik = 'root';
  7. private $haslo = '';
  8.  
  9.  
  10. public function __construct() {
  11. try {
  12. $this->pdo = new PDO($this->dsn, $this->uzytkownik, $this->haslo, array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8"));
  13. $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  14. }
  15. catch(PDOException $e) {
  16. echo '<p>Polaczenie nie moglo zostac utworzone: '.$e->getMessage().'</p>';
  17. }
  18. }
  19.  
  20. public function __destruct() {
  21. }
  22.  
  23.  
  24. public function pobieranieDanych($z) {
  25. try{
  26. $wynik = $this->pdo->query($z);
  27. $wynik_tablica = $wynik->fetchAll();
  28. $wynik -> closeCursor();
  29. return $wynik_tablica;
  30. }
  31. catch(PDOException $e) {
  32. echo '<p>blad w metodzie pobieranieDanych(): '.$e->getMessage().'</p>';
  33. }
  34. }
  35.  
  36. }
  37.  
  38. ----------------------------------------------
  39.  
  40. class Ksiazki
  41. {
  42. private $polaczenie;
  43.  
  44. public function __construct($db)
  45. {
  46. $this->polaczenie = $db;
  47. }
  48.  
  49. public function __destruct()
  50. {
  51. }
  52.  
  53. public function pobierzKsiazki()
  54. {
  55. return $this->polaczenie->pobieranieDanych("SELECT * FROM ksiazki");
  56. }
  57. }
  58.  
  59. -----------------------------------------------------------------
  60.  
  61. $polaczenie = new klasaPolaczenie();
  62.  
  63. $ksiazki = new klasaKsiazki($polaczenie);
  64. $rezultat_1 = $ksiazki->pobierzKsiazki();
  65.  
  66. $ksiazki2 = new klasaKsiazki($polaczenie);
  67. $rezultat_2 = $ksiazki->pobierzKsiazki();
erix
Cytat
Call to a member function pobieranieDanych() on a non-object

Pokaż, jak dokładnie wywoływałeś mój schemat; kod, daj var_dump na zmiennej połączenia i upewnij się, że wywołujesz konstruktor rodzica, bo mam obawy, że jednak tego nie robisz.
wiewiorek
No fakt - miałeś rację - zapomniałem, że w php konstruktor klasy nadrzednej trzeba samemu wywolac. Ale to i tak niewiele daje, bo musze pisac w klasie Ksiazki w kazdej metodzie zawierajacej zapytanie caly kod wlacznie z fetchowaniem i zamykaniem kursora oraz blokiem try/catch:

  1. abstract class Polaczenie extends PDO{
  2.  
  3. static protected $pdo = null;
  4.  
  5. private $dsn = 'mysql:host=localhost;dbname=koszyk';
  6. private $uzytkownik = 'root';
  7. private $haslo = '';
  8.  
  9.  
  10. public function __construct() {
  11. if(self::$pdo===null){
  12. try {
  13. self::$pdo = new PDO($this->dsn, $this->uzytkownik, $this->haslo, array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8"));
  14. self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  15. }
  16. catch(PDOException $e) {
  17. echo '<p>Polaczenie nie moglo zostac utworzone: '.$e->getMessage().'</p>';
  18. }
  19. }
  20. }
  21.  
  22.  
  23. public function __destruct() {
  24. }
  25.  
  26.  
  27. public function pobieranieDanych($z) {
  28. try{
  29. $wynik = self::$pdo->query($z);
  30. $wynik_tablica = $wynik->fetchAll();
  31. $wynik -> closeCursor();
  32. return $wynik_tablica;
  33. }
  34. catch(PDOException $e) {
  35. echo '<p>blad w metodzie pobieranieDanych(): '.$e->getMessage().'</p>';
  36. }
  37. }
  38.  
  39. }
  40.  
  41. -------------------------------------------------
  42.  
  43. class Ksiazki extends Polaczenie
  44. {
  45.  
  46.  
  47. public function __construct()
  48. {
  49. parent::__construct();
  50. }
  51.  
  52. public function __destruct()
  53. {
  54. }
  55.  
  56. public function pobierzKsiazki()
  57. {
  58. try
  59. {
  60. $wynik = parent::$pdo->query("SELECT * FROM ksiazki");
  61. $wynik_tablica = $wynik->fetchAll();
  62. $wynik -> closeCursor();
  63. return $wynik_tablica;
  64. }
  65. catch(PDOException $e)
  66. {
  67. echo '<p>blad w zapytaniu: '.$e->getMessage().'</p>';
  68. }
  69. }
  70. }
  71. --------------------------------------------------------
  72. $ksiazki = new Ksiazki();
  73. $rezultat = $ksiazki->pobierzKsiazki();



Moze wiec wrocic do tego sposobu z przekazywaniem do konstruktora klasy Ksiazki obiektu klasy Polaczenie - jak nizej ?

  1. class Polaczenie extends PDO{
  2.  
  3. private $pdo;
  4.  
  5. private $dsn = 'mysql:host=localhost;dbname=koszyk';
  6. private $uzytkownik = 'root';
  7. private $haslo = '';
  8.  
  9.  
  10. public function __construct() {
  11. try {
  12. $this->pdo = new PDO($this->dsn, $this->uzytkownik, $this->haslo, array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8"));
  13. $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  14. }
  15. catch(PDOException $e) {
  16. echo '<p>Polaczenie nie moglo zostac utworzone: '.$e->getMessage().'</p>';
  17. }
  18. }
  19.  
  20. public function __destruct() {
  21. }
  22.  
  23.  
  24. public function pobieranieDanych($z) {
  25. try{
  26. $wynik = $this->pdo->query($z);
  27. $wynik_tablica = $wynik->fetchAll();
  28. $wynik -> closeCursor();
  29. return $wynik_tablica;
  30. }
  31. catch(PDOException $e) {
  32. echo '<p>blad w metodzie pobieranieDanych(): '.$e->getMessage().'</p>';
  33. }
  34. }
  35.  
  36. }
  37.  
  38. ----------------------------------------------
  39.  
  40. class Ksiazki
  41. {
  42. private $polaczenie;
  43.  
  44. public function __construct($db)
  45. {
  46. $this->polaczenie = $db;
  47. }
  48.  
  49. public function __destruct()
  50. {
  51. }
  52.  
  53. public function pobierzKsiazki()
  54. {
  55. return $this->polaczenie->pobieranieDanych("SELECT * FROM ksiazki");
  56. }
  57. }
  58.  
  59. -----------------------------------------------------------------
  60.  
  61. $polaczenie = new klasaPolaczenie();
  62.  
  63. $ksiazki = new klasaKsiazki($polaczenie);
  64. $rezultat = $ksiazki->pobierzKsiazki();

Void
Masz złe podejście do programowanie obiektowego. Poczytaj trochę o ogólnych zasadach OOP, dziedziczeniu itp. W typowo modelowych klasach jakimi są Książki i Autorzy dziedziczysz po abstrakcyjnej klasie Polaczenie. Jaki to ma sens? Dziedziczenie wykorzystuje się do poszerzania możliwości klas w jakiś logiczny sposób ze sobą powiązanych - piszesz klasę ogólną, posiadającą jakieś podstawowe właściwości i metody charakterystyczne dla pewnego zbioru obiektów i dzięki klasom potomnym specjalizujesz ją (rozszerzasz jej działanie). Np. masz klasę Pojazd, która jest klasą abstrakcyjną - nie istnieje coś takiego jak "pojazd", nazywamy tak różne środki transportu służące do przemieszczania się, ale sam "pojazd" nie istnieje. Dopiero konkretne typy pojazdów (a raczej wypadałoby powiedzieć że to "Pojazd" jest typem dla konkretnych obiektów) można stworzyć i wykorzystywać. Klasy Samochód, Motocykl, Rower dziedziczą więc po klasie Pojazd - są pewną wyspecjalizowaną formą tej klasy (a przy tym są z nią logicznie powiązane).

Wracając do twojego przykładu - Połączenie nie jest w żaden sposób typem dla obiektu Książka, ani Autor. Klasy te mogą posiadać informację o połączeniu z bazą, ale na pewno nie w wyniku dziedziczenia. Możesz więc przekazywać uchwyt połączenia z bazą w argumencie konstruktora klasy Książka i przechowywać go w prywatnym polu tej klasy. Możesz też wykorzystać tzw. rejestr (wzorzec registry), czyli taki globalny pojemnik na różne obiekty (oparty na singletonie), do którego wrzucasz sobie obiekt połączenia i uzyskujesz do niego dostęp w dowolnym momencie programu. Niektórzy nie lubią takiego rozwiązania, ze względu właśnie na tą "globalność", ale czasami może być wygodne. Poza tym możesz spróbować dowiedzieć się trochę i zaimplementować wzorzec context
wiewiorek
"Możesz więc przekazywać uchwyt połączenia z bazą w argumencie konstruktora klasy Książka i przechowywać go w prywatnym polu tej klasy." - no wlasnie tak sadzilem, ze to byloby najwygodniejsze i najlepsze rozwiazanie jak w moim poprzednim poscie (jego druga czesc) smile.gif Dziekuje

Dzieki za uwage odnosnie dziedziczenia w tym wypadku, aczkolwiek w zyciu to chyba roznie z tym bywa - np. wczoraj natknalem sie na cos takiego:
http://webinside.pl/artykul-369-zachowanie...ebie-klasy.html tongue.gif
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.