Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP] PDOStatement pobieranie danych
Forum PHP.pl > Forum > Przedszkole
by_ikar
Witam. Z racji że ostatnio zrobiłem kilka aplikacji opartych o sqlite (dość małe aplikacje) już na samym początku przygody z sqlite, dowiedziałem się że api sqlite w wersji 3 nie posiada czegoś takiego jak "*_num_rows" co za tym idzie, obiekt PDOStatement w przypadku użycia metody rowCount zawsze zwraca 0. Problem można w pewien sposób ominąć, poprzez modyfikacje PDOStatement, a dokładnie to napisanie swojej klasy która będzie dziedziczyć PDOStatement.

I taki przykładowy kod "naprawiający" to nasze rowCount, mógłby wyglądać tak:

  1. <?php
  2.  
  3. class Statement extends PDOStatement
  4. {
  5. protected $rowCount = false;
  6.  
  7. public function rowCount()
  8. {
  9. if(false !== $this->rowCount)
  10. {
  11. return $this->rowCount;
  12. }
  13.  
  14. $this->rowCount = count($this->fetchAll(PDO::FETCH_ASSOC));
  15.  
  16. return $this->rowCount;
  17. }
  18. }


Oczywiście jest to tylko przykładowy kod.

I mam teraz pytanie. Nie znalazłem nigdzie informacji na ten temat, możliwe że źle szukałem (pod złymi frazami), ale jeżeli bym chciał użyć powyższe rozwiązanie problemu, to od razu nasuwa mi się pytanie, czy do PDOStatement już są przekazywane wyniki, np w postaci tablicy, czy dopiero metoda fetch/fetchAll wykonują takie zapytanie i zwracają wyniki?
Sephirus
Cytat
czy do PDOStatement już są przekazywane wyniki, np w postaci tablicy, czy dopiero metoda fetch/fetchAll wykonują takie zapytanie i zwracają wyniki?


Odpowiedź jest prosta "do PDOStatement już są przekazywane wyniki" - dlaczego? Napisz błędnę zapytanie i zobacz czy zwróci Ci błąd po samym utworzeniu PDOstatement czy po fetchAll wink.gif
Crozin
To nienajlepsze rozwiązanie. PDOStatement::fetchAll(), zwróci Ci tablicę wyników oraz ustawi swój wewnętrzny kursor (którego pozycji w PDO zmienić się nie da) na końcu strumienia danych uniemożliwiając ponowne pobranie tych samych danych. Innymi słowy po wykonaniu Statement::rowCount() nie będziesz mógł już odczytać wyników (ponieważ zostały już raz odczytane).

Na dobrą sprawę masz możesz nie rozszerzać w ogóle PDO i przywyknąć do:
  1. $rs = $stmt->fetchAll();
  2.  
  3. if (count($rs) > 0) {
  4. // do sth
  5. }
Innym rozwiązaniem byłoby niby nadpisanie wszystkich metod zwracających dane tak by korzystały z jakiejś wewnętrznej tablicy wypełnionej już od początku danymi z ::fetchAll(), ale to będzie miało poważną wadę - nie będziesz w stanie pobierać po jednym rekordzie co może w pewnych przypadkach być bardzo nieefektywne.
by_ikar
@Crozin no właśnie na chwilę obecną tak się bawię, tylko właśnie próbuje tego uniknąć i zrobić to jakoś bardziej elegancko. Jest jeszcze inna opcja, z racji że korzystam z nakładki na PDO w stylu Zend_Db_Select, mogę przekazać do takiej nakładki na PDOStatement również ostatnie zapytanie i jakoś zgrabnie nim operować. I mógłbym zrobić coś takiego przykładowo:

  1. <?php
  2.  
  3. class Statement extends PDOStatement
  4. {
  5. protected $rowCount = false;
  6. protected $database;
  7.  
  8. public function __construct(Database $database)
  9. {
  10. $this->database = $database;
  11. }
  12.  
  13. public function rowCount()
  14. {
  15. if(false !== $this->rowCount)
  16. {
  17. return $this->rowCount;
  18. }
  19.  
  20. $res = $this->database->reset(Database::SELECT)->select('COUNT(*) as total')->get()->fetch(PDO::FETCH_ASSOC);
  21.  
  22. $this->rowCount = $res['total'];
  23.  
  24. return $this->rowCount;
  25. }
  26. }


Tyle że to niesie ze sobą kilka problemów. Pierwszym jest sytuacja kiedy ktoś zamiast "buildera" użyje zwykłego query/prepare, wówczas powyższy przykład bankowo zwróci błąd. Drugi problem, to jest kolejne zapytanie.

O ile count jest ok, dla sprawdzenia czy wynik zawiera jakieś dane, o tyle w przypadku stronicowania, gdzie potrzebuje wiedzieć ile tych rekordów jest ogółem, tak count się nie sprawdzi.. O ile w niektórych miejscach tego stronicowania nie muszę używać, a byłoby jedynie dodatkową funkcjonalnością, tak w galerii którą ostatnio robiłem, z listą dodanych zdjęć wypadałoby już stronicować, co by nie wywalić kilkuset rekordów na monitor..
Crozin
Ale do stronicowania to już potrzebne są dwa, zupełnie różne zapytania:
  1. SELECT COUNT(*) FROM tbl_name WHERE ...;
  2. SELECT ........ FROM tbl_name WHERE ... LIMIT ? OFFSET ?;
Także tutaj PDO nie ma nic do rzeczy i metoda rowCount() nie powinna być wykorzystywana do wygenerowania pierwszego zapytania.

Pamiętaj, że PDO to warstwa abstrakcji nad połączeniem z bazą danych, nie abstrakcja samej bazy danych (Zend_Db/Doctrine DBAL).
by_ikar
W sumie w pewnym sensie racja - trochę z tym stronicowaniem się zagalopowałem i co prawda nie używałem do tego count (array), czy rowCount, tylko właściwie osobne zapytanie.

Cały problem by nie istniał, gdybym zamiast leniwie skopiować, poprostu przerobił templatki/modele których używałem pod mysql, a użyłem ich pod PDO zmieniając w sumie tylko w sumie niektóre zapytania, oraz ustawienia połączenia z bazą. Wówczas wystarczyłoby żebym nadpisał to rowCount, a tak będzie trzeba najpewniej zmodyfikować kilka widoków i modeli. No nic, dzięki za pomoc! wink.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.