Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Obsługa baz danych z PDO, Szybki kurs operowania bazą danych przy użyciu rozszerzenia PDO
SlimShady
post 18.08.2013, 00:37:34
Post #1





Grupa: Zarejestrowani
Postów: 29
Pomógł: 0
Dołączył: 11.05.2013

Ostrzeżenie: (0%)
-----


Tradycyjny i popularny sposób łączenia z bazą danych przy pomocy mysql_* dawno stał się przestarzały. Teraźniejszość z PHP daje nam do wyboru 2 rozszerzenia, które są dużo lepszą alternatywą niż dotychczasowa metoda. Mowa o MySQLi oraz PDO.

Przedstawmy zalety i wady tego rozszerzenia w porównaniu z MySQLi:
  • PDO obsługuje wiele interfejsów baz danych. MySQLi (jak sama nazwa wskazuje) skupia się jedynie na systemie MySQL, natomiast w PDO wystarczą niewielkie zmiany, aby pasował on pod takie systemy baz danych jak np. PostgreSQL, SQLite oraz oczywiście MySQL. Oznacza to, że kod w PDO jest wysoce przenośny i doskonale nadaje się do projektów na sprzedaż (np. silniki gier).
  • Mamy do dyspozycji podpinanie danych, dzięki któremu programista zaoszczędzi sobie pracy z pisaniem dodatkowego kodu. PDO pomoże nam zabezpieczyć zapytania w zaledwie 2 linijkach.
  • Łatwy i przejrzysty zapis instrukcji.
  • Duża wygoda korzystania.
  • Multum poradników.
  • W niektórych przypadkach MySQLi okazuję się być szybsze, jednak to nie przysłania wszystkich zalet PDO.

Bez względu na to co wybierzesz i tak będzie to lepszy sposób niż stare mysql_* wink.gif

1. Pierwszą czynnością będzie ustanowienie nowego połączenia. Najprościej zrobimy to w ten sposób:
  1. try {
  2. $baza = new PDO('mysql:host=localhost;dbname=testowa_baza;charset=utf8', 'użytkownik', 'hasło');
  3.  
  4. // kod php
  5.  
  6. } catch(PDOException $err) {
  7. exit('Blad polaczenia z baza danych: '.$err->getMessage();
  8. }

mysql: definiuję system bazy danych, na którym będziemy opierać swoje działania. Dalej podajemy host, nazwę bazy, ustalamy na szybko kodowanie (utf8) oraz wpisujemy login użytkownika + jego hasło.
Blok try { ... } catch { ... } po prostu obsługuję zaistniałe błędy. Pomiędzy klamrami od try zamieszczamy cały skrypt naszej strony, a w catch ustalamy sposób raportowania błędów. Robimy to poprzez wyjątek PDOException, który zwraca nam kilka możliwych metod. Skorzystaliśmy z getMessage() przechowującą pełną informację o błędzie. Co należy wiedzieć o PDOException? Jest na tyle fajne, że nie musimy umieszczać dotychczasowego or die mysql_error.. a jedną linijką dostaniemy wszystkie informację.

Choć kod działa i dodatkowo prosto, to nie oznacza, że w pełni swoich możliwości. Wprowadźmy parę zmian:
  1. // pobraliśmy skądś dane do połączenia (np. pliku)
  2. $host = 'localhost';
  3. $user = 'root';
  4. $pass = '';
  5. $base = 'testowa';
  6.  
  7. try {
  8.  
  9. $baza = new PDO('mysql:host='.$host.';dbname='.$base.';charset=utf8', $user, $pass,
  10. // wyłączenie zbędnego emulate prepares
  11. PDO::ATTR_EMULATE_PREPARES => false,
  12. // ustalenie sposobu raportowania błędów
  13. PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
  14. ));
  15.  
  16. // kod php
  17.  
  18. } catch(PDOException $err) {
  19. exit('Blad polaczenia z baza danych: '.$err->getMessage();
  20. }


Dodaliśmy kilka parametrów i przy okazji przedstawiłem sposób zamieszczania zmiennych w połączeniu.

2. Będąc już połączonym z bazą danych, przyszedł czas na parę zapytań. Odpowiednikiem mysql_query() jest query():
  1. // pierwszy przykład
  2. $zapytanie = $baza->query("SELECT login FROM gracze WHERE id = 2");
  3.  
  4. $zapytanie->closeCursos(); // zamknięcie pierwszego zapytania
  5.  
  6. // drugi przykład
  7. $nazwa_nadawcy = 'shady';
  8. $minimum_id = 5;
  9.  
  10. $zapytanie = $baza->query("SELECT * FROM poczta WHERE nadawca = '".$nazwa_nadawcy."' AND id >= ".$minimum_id);

Jak widać nie ma tu praktycznie nic nowego.
Omówić należy closeCursor(). Używamy go wtedy, gdy wykonamy zapytanie typu SELECT, a zaraz po nim chcemy wykonać inne zapytanie POBIERAJĄCE dane. Wtedy zamykamy tzw. kursor zapytania. Jest wiele teorii, dla których ten zapis powinien być stosowany. W sieci spotkamy się z tym, że pozwala to nadać większej kompatybilności między różnymi interfejsami, a nawet, iż przyśpiesza to działanie, choć osobiście nie sprawdzałem różnic..

3. Należy zwrócić uwagę, że polecenia query() użyjemy tylko do zapytań SELECT, a wszelkie inne jak UPDATE, DELETE, INSERT itp. zamieszczamy w exec():
  1. // przykład pierwszy
  2. $baza->exec("UPDATE gracze SET zloto = zloto + 5 WHERE id = ".$gracz['id']);
  3.  
  4. // przykład drugi
  5. $baza->exec("INSERT INTO przedmioty(nazwa, gracz_id, sila, obrona) VALUES('".$item['nazwa']."', ".$gracz['id'].", ".$item['sila'].", ".$item['obrona'].")");

W przeciwieństwie do query() metoda exec() zwraca liczbę, a dokładniej liczbę zoperowanych rekordów. Przykład:
  1. $ile = $baza->exec("UPDATE gracze SET energia = 100 WHERE energia <= 20");
  2.  
  3. echo 'Zmienionych rekordów: '.$ile;


4. Nie pokazaliśmy jeszcze jak pobrane dane przy użyciu query() przypisać do elementu dokumentu:
  1. $zapytanie = $baza->query("SELECT login, level FROM gracze WHERE id = ".$_SESSION['id']);
  2. $gracz = $zapytanie->fetch();
  3.  
  4. echo 'Witaj '.$gracz['login'].'! Masz obecnie '.$gracz['leve'].' level.';


Proste? Proste wink.gif fetch() może przyjmować jeszcze parametr ustalający sposób zwracanych danych. Dostępne są m.in:
  • $dane = $zap->fetch(PDO::FETCH_BOTH) - ustawiane domyślnie, możemy stosować zarówno $dane['kolumna'] jak i $dane[1]
  • $dane = $zap->fetch(PDO::FETCH_ASSOC) - pozwala na stosowanie wyłącznie indeksów asocjacyjnych ($dane['kolumna'])
  • $dane = $zap->fetch(PDO::FETCH_OBJ) - zwraca tablice z indeksami wyłącznie numerycznymi ($dane[1])
  • $dane = $zap->fetch(PDO::FETCH_OBJ) - zwraca dane jako anonimowy obiekt ($dane->kolumna)
    oraz 4 inne (patrz manual)


A tak wygląda fetchowanie z wykorzystaniem pętli (przykład na liście graczy z levelem większym lub równym 50):
  1. $gracze = $baza->query("SELECT login, level FROM gracze WHERE level >= 50");
  2.  
  3. while ($rekord = $gracze->fetch()) {
  4. echo $rekord['login'].' ma lvl: '.$rekord['level'].'<br/>';
  5. }


Warto napisać o sposobie wyświetlania liczby pobranych rekordów:
  1. $gracze = $baza->query("SELECT login, level FROM gracze WHERE level >= 50");
  2.  
  3. echo 'Graczy na levelu 50+: '.$gracze->rowCount();


5. Bindowanie danych, a mówiąc polskim odpowiednikiem: podpinanie danych. To jedna z największych zalet PDO, a zarazem wnosząca największą ze zmian w zapisie kodu. Na czym ono polega? Nie wykonujemy od razu zapytania, lecz najpierw je pod to przygotowujemy. Ustalamy dane, które mają ulec filtracji i dopiero teraz możemy je wykonać.

Przyjmijmy, że mamy formularz logowania i chcemy sprawdzić istnienie danego konta. W tym celu wykonujemy do bazy danych zapytanie, mające na celu odnalezienie konta o wprowadzonym nicku.
  1. $zapytanie = $baza->query("SELECT id, haslo FROM gracze WHERE login = '".$_POST['login']."'");
  2.  
  3. $konto = $zapytanie->fetch();

Powyższy zapis naraża bezpieczeństwo naszej aplikacji na katastrofalne straty. Potencjalny hacker wykorzystujący takie nie zabezpieczone dane może wykonać dosłownie KAŻDE zapytanie! W tym usunąć użytkowników, a także całą bazę..

Jak sobie z tym poradzić? Nie jest to trudne, a dzięki PDO wręcz banalne. Każde dane otrzymywane od użytkowników (zmienne super globalne GET, POST, ale także ciastka i sesje) odkazić z potencjalnego niebezpieczeństwa. Po prostu sprawdzić, czy nie zawierają one cudzysłowów, kodu html/js i innego świństwa, które należałoby zamienić.

Nowy zapis ostatniego kodu powinien mieć taką formę:
  1. // przygotowanie zapytania
  2. $zapytanie = $baza->prepare("SELECT id, haslo FROM gracze WHERE login = :login");
  3. // filtracja wskazanych danych
  4. $zapytanie->bindValue(':login', $_POST['login'], PDO::PARAM_STR);
  5. // właściwe wykonanie
  6. $zapytanie->execute();
  7.  
  8. $konto = $zapytanie->fetch();


bindValue przyjmuję 3 parametry:
  1. nazwa używana w zapytaniu, poprzedzona dwukropkiem (:)
  2. dane, które mają ulec filtracji
  3. pod jakim kątem przefiltrować te dane

Jak widać w 3 parametr (kąt filtracji) podaliśmy akurat PDO::PARAM_STR, co oznacza, że PDO ma filtrować odebrane dane jako ciąg znaków. Drugą wartością dla tego parametru może być PDO::PARAM_INT, czyli filtracja dla liczb. Inne znajdziemy >tutaj<. Bindować możemy nie tylko SELECT, ale i całą resztę

W celach lepszego zrozumienia dam kolejny przykład. Panel administratora wyświetlający adresy email graczy o podanym levelu i klasie postaci.
  1. // przygotowanie zapytania
  2. $zapytanie = $baza->prepare("SELECT login, email FROM gracze WHERE level = :poziom AND klasa= :klasa");
  3. // filtracja wskazanych danych
  4. $zapytanie->bindValue(':poziom', $_POST['poziom'], PDO::PARAM_INT);
  5. $zapytanie->bindValue(':klasa', trim($_GET['klasa']), PDO::PARAM_STR);
  6. // właściwe wykonanie
  7. $zapytanie->execute();
  8.  
  9. if ($gracze->rowCount() > 0) {
  10. while ($gracz = $zapytanie->fetch()) {
  11. echo 'Gracz: '.$gracz['login'].' ma adres emaill: '.$gracz['email'].'<br/>';
  12. }
  13. } else echo 'Brak graczy o podanych parametrach..';

Bindowanie nie ma w sobie trimowania (usuwania z początku i końca linii białych znaków) dlatego przy klasie postaci (ogólna tendencja głównie do stringów) użyliśmy jeszcze funkcji trim().

Nie wspomniałem o przydatnej funkcji, która zwróci numer id ostatnio dodanego wiersza przez polecenie INSERT.
  1. $baza->exec("INSERT INTO kurs_pdo(uzytkownik, stan) VALUES('Ty', 'zaliczony')");
  2.  
  3. echo 'Ten rekord otrzymał ID: '.$baza->lastInsertId();


Po więcej wiedzy warto zajrzeć oczywiście do manuala.
Przepraszam, jeśli temat wykracza poza tematykę działu, ale lubię się czymś dzielić, a poza tym nie znalazłem tu innego działu do zamieszczenia tego sad.gif Zapraszam do dyskusji i krytyki tego co wybazgrałem, sam chętnie dowiem się nowych rzeczy.

Ten post edytował SlimShady 18.08.2013, 00:39:10


--------------------
wszystkie drogi prowadzą do manuala :3
Go to the top of the page
+Quote Post
!*!
post 18.08.2013, 10:59:50
Post #2





Grupa: Zarejestrowani
Postów: 4 298
Pomógł: 447
Dołączył: 16.11.2006

Ostrzeżenie: (0%)
-----


A czym to się różni od http://pl.wikibooks.org/wiki/PHP/Biblioteka_PDO ?


--------------------
Nie udzielam pomocy poprzez PW i nie mam GG.
Niektóre języki programowania, na przykład C# są znane z niezwykłej przenośności (kompatybilność ze wszystkimi wersjami Visty jest wiele warta).
Go to the top of the page
+Quote Post
Turson
post 19.08.2013, 19:29:41
Post #3





Grupa: Zarejestrowani
Postów: 4 291
Pomógł: 829
Dołączył: 14.02.2009
Skąd: łódź

Ostrzeżenie: (0%)
-----


Dobrze opisane biggrin.gif

Literówka w 3. kodzie CloseCursos
Go to the top of the page
+Quote Post
gitbejbe
post 26.08.2013, 13:19:08
Post #4





Grupa: Zarejestrowani
Postów: 405
Pomógł: 41
Dołączył: 27.08.2012

Ostrzeżenie: (0%)
-----


ja, który zawsze znajdzie wymówkę przed pisaniem w oop, zostałem w końcu łopatologicznie uświadomiony jak korzystać z tego całego PDO ! wielki + ! Robiłem juz pare podeść ale bez skutku. Po obiedzie lece orać kod specool.gif

edit:
Z mojej strony prośba do administracji, żeby wyróżnić ten poradnik, na bank nie raz zajrze

Ten post edytował gitbejbe 26.08.2013, 13:21:57
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 21.09.2018 - 07:44