Prosta klasa php do zarządzania bazą danych MySQL cz. 2

Słowem wstępu
Ze spooorym opóźnieniem, ale w końcu zabieramy się za drugą część naszej klasy do obsługi bazy danych, a mianowicie postaram się opisać sposób jej wykorzystania.
Skoro nasza klasa, jak mówi tytuł, jest prosta to i sposób wykorzystania ma na celu przede wszystkim pokazać jedną z możliwości.
Jak możemy wykorzystać naszą klasę?
Powiedzmy, że budujemy mini-aplikację php. Od czego zaczniemy ? oczywiście od określenia odpowiedniej struktury folderów i plików. Struktura naszej aplikacji może wyglądać następująco:
— classes
— css
— js
— inc
— index.php
Podstawowa struktura folderów i plików. Gdzie folder css będzie zawierać style css, folder js pliki JavaScript, inc pliki ładowane ? funkcja include() lub require() – i plik index.php.
W poniższym przykładzie interesują nas tylko dwa elementy: folder classes oraz plik index.php, resztę pominiemy.
W folderze classes umieszczamy plik z nasza “prostą klasą php do zarządzania bazą danych mysql”
Struktura naszej aplikacji wygląda następująco:
— classes
—- class_db.php
— index.php
Musimy teraz załadować naszą klasę w głównym pliku aplikacji ? index.php
Będzie to wyglądało mniej więcej tak:
<?php
require_once('classes/class_db.php');
?>
Dlaczego require() a nie include()?? Dlaczego require_once(), a nie require()?? W tym temacie odsyłam już do manuala php:
http://pl.php.net/manual/en/function.require.php
http://pl.php.net/manual/en/function.require-once.php
Dobrym pomysłem jest dodać warunek przed wywołaniem funkcji require_once(). Sprawdzimy czy nasz plik istnieje, jeżeli tak ładujemy naszą klasę, jeżeli nie ? pokazujemy komunikat o nieodnalezieniu pliku, który możemy sformatować dużo lepiej niż standardowy błąd php.
Tak więc po zmianach nasz kod wyglądać będzie następująco:
<?php ... $class_db_file = 'classes/class_db.php'; if (file_exists($class_db_file)): require_once($class_db_file); else: echo 'Klasa db() nie została odnaleziona'; exit; endif; ... ?>
Instrukcja exit; powoduje zakończenie działania naszej aplikacji. Jeżeli bazy danych są istotnym elementem naszej aplikacji (a pewnie tak właśnie jest), nie ma sensu dalsze przetwarzanie kodu. Na 90% pojawi się sporo błędów w miejscach gdzie wykonane były jakiekolwiek operacje na bazie danych.
Ale w naszym przypadku wszystko działa poprawnie, klasa plik z klasą został odnaleziony, klasa dołączona do aplikacji, tak więc teraz pora na kolejny krok…
Implementacja klasy
Zacznijmy od zdefiniowania zmiennych naszej klasy. Na początku klasy db() mamy linijki odpowiedzialne za podanie danych dostępu do bazy danych (host, nazwa, login i hasło). Początek naszej klasy wyglądał następująco:
<?php
class db {
private $db_host = 'localhost';
private $db_name = 'nazwa_bazy';
private $db_user = 'admin';
private $db_passw = 'passw';
...
?>
W tym miejscu powinniśmy zmodyfikować tę część naszej klasy, aby podać dane odpowiednie dla naszej bazy danych. Co gorsza powinniśmy to robić za każdym razem kiedy chcemy wykorzystać klasę w kolejnych projektach, ale że czas to pieniądz z pomocą przychodzą nam stałe php.
Przerobimy naszą klasę tylko raz. Powyższy kod modyfikujemy w następujący sposób:
<?php
class db {
private $db_host = DB_HOST;
private $db_name = DB_NAME;
private $db_user = DB_USER;
private $db_passw = DB_PASSW;
...
?>
Tak zmodyfikowaną klasę możemy wykorzystywać do obsługi dowolnej bazy danych, bez każdorazowej ingerencji w plik class_db.php. Jedyną operację jaką musimy wykonać to zadeklarowanie stałych w pliku index.php tuż przed operacją require_once() – załadowaniem naszej klasy.
Tak więc nasz plik index.php będzie mieć postać:
<?php define(DB_HOST, 'db_host'); // host naszej bazy danych najczęściej localhost lub IP bazy define(DB_NAME, 'db_name'); // nazwa naszej bazy danych define(DB_USER, 'user_name'); // nazwa użytkownika define(DB_PASSW, 'user_passw'); // hasło dostępu dla użytkownika $class_db_file = 'classes/class_db.php'; if (file_exists($class_db_file): require_once($class_db_file); else: echo 'Klasa db() nie została odnaleziona'; exit; endif; ... ?>
Przy budowie większych aplikacji prawdopodobnie będziemy wykorzystywać plik z ustawieniami i właśnie w nim możemy dodać zdefiniowane elementy dla naszej bazy danych. Jeżeli kiedykolwiek “bawiliście” się CMS’ami
( Word Press , Joomla , Drupal itp. ) na pewno napotkaliście na pliki typu settings.php, config.php, configuration.php itp., które to składają się przeważnie z ciągu definicji ? define(ELEMENT, ‘value’);
Jeżeli mamy już zdefiniowane dane dostępu do bazy, zobaczy jak wyglądać będzie…
Wykorzytsanie klasy db()
Na warsztat bierzemy jeden z najbardziej oklepanych przykładów ? tabela z produktami, zawierająca podstawowe informacje: id, producent, produkt, cena.
| id | brand | product | price |
|---|---|---|---|
| 1 | 7th Sunrise | website | 1000 000 $ |
| 2 | 7th Sunrise | catalog | 600 000 $ |
| 3 | 7th Sunrise | layout | 100 000 $ |
Powiedzmy, że mamy sklep internetowy z produktami i chemy wyświetlić dostępne produkty ? musimy więc pobrać z bazy listę produktów wraz z ich właściwościami. Tak więc tworzymy funkcję odpowiedzialną za pobranie informacji z bazy danych, która jako wynik zwróci nam listę produktów w postaci tablicy, ale…pierwszym krokiem będzie utworzenie obiektu naszej klasy db:
<?php ... $db = new db(); // tworzymy objekt $db klasy db, new oznacza nowy obiekt ... ?>
Następnie tworzymy funkcję odpowiedzialną za pobranie produktów:
...
$db = new db();
function select_all_products($db){
if ($db->connect()):
$sql = "SELECT * FROM `products`;";
if ($results = $db->select($sql):
$returned = array();
while ($row = mysql_fetch_array($results):
$returned[] = array(
'id' => $row['id'],
'brand' => $row['brand'],
'product' => $row['product'],
'price' => $row['price']
);
endwhile;
return $returned;
else:
echo 'Błąd pobrania danych mysql: ' . $db->error;
return false;
endif;
$db->close();
else:
echo 'Błąd połączenia mysql: ' . $db->error;
return false;
endif;
}
...
I po kolei:
if ($db->connect()):
sprawdzamy czy połączenie z bazą danych zostanie utworzone, jeżeli nie zwracamy komunikat z błędem mysql, który zawarty jest w zmiennej error naszej klasy oraz false jako wynik funkcji select_all_products()
else:
echo 'Błąd połączenia mysql: ' . $db->error;
return false;
endif;
Jeżeli połączenie zostanie nawiązane:
$sql = "SELECT * FROM `products`;";
if ($results = $db->select($sql):
Następnie pod zmienną $results podstawiamy wynik zapytania, jeżeli zwróci false (błąd przy operacji na bazie):
else:
echo 'Błąd pobrania danych mysql: ' . $db->error;
return false;
endif;
wyświetlamy błąd operacji na bazie ($db->error) oraz zwracamy false jako wynik funkcji.
Jednakże jesteśmy dobrej myśli i przypuszczamy, że wszystko poszło ok i mamy zwróconą wartość z bazy danych, a dokładnie tablicę z wierszami i kolumnami.
Aby operować na wynikach z bazy danych musimy użyć funkcji mysql_fetch_array() . Jako, że pobieramy cała tablicę, a nie jeden wiersz ? musimy zrobić to w pętli:
while ($row = mysql_fetch_array($results)):
co znaczy mniej więcej ‘dla każdego wiersza zwróconego w zapytaniu ($results) pod zmienną $row podstaw wynik. Jako wynik funkcja select_all_product() również zwróci tablicę z tablicami
, czyli tablicę, której elementami są również tablice reprezentujące kolejne wiersze tabeli products
returned[0] = array( id => wiersz_1[kolumna_id], barnd => wiersz_1[kolumna_brand], itd... );
jako wartości dla $row[] podajemy w cudzysłowach nazwy kolumn tabeli products. Niestety dla każdej kolumny musimy osobno podawać $row['nazwa_kolumny']. Jeżeli mamy 3, 4 kolumny nie jest to duży problem, ale co jeżeli mamy pokaźną tablicę produktów gdzie oprócz id, producenta, nazwy, ceny mamy jeszcze np. rozmiary, kolory, ilość na magazynie itd. Itd. aż do kilkunastu kolumn?? Od czego mamy pętlę typu foreach ? Funkcję select_all_products() możemy zmodyfikować następująco:
...
$sql = "SELECT * FROM `products`;";
if ($results = $db->select($sql):
$returned = array();
while ($row = mysql_fetch_array($results):
$mx = array();
foreach ($row as $key => $val):
$mx[$key] = $val;
endforeach;
$returned[] = $mx;
);
endwhile;
return $returned;
...
Na koniec zamykamy połączenie z bazą:
... $db->close(); ...
Dzięki temu możemy utworzyć tablicę oszczędzając kilkanaście linijek kodu.
Aby wyświetlić wynik naszej funkcji:
... $selected_products = select_all_products($db); var_dump($selected_products); ...
Obsługa błędów
Do obsługi błędów spowodowanych operacjami na bazie danych, wykorzystujemy zmienną $error, która została utworzona na początku klasy db(). Wewnątrz funkcji klasy db() w przypadku wystąpienia błędu MySQL, jego treść zostanie podstawiona pod zmienną $db->error. Dzięki temu możemy dowolnie sformatować ew. Błąd. Możemy też wyświetlać go tylko podczas testów, następnie użytkownikowi wyświetlić tylko niezbędną informację np. “wystąpił błąd połączenia z bazą danych“. Nie ma potrzeby pokazywać użytkownikom całej treści zwróconego przez bazę błędu, tym bardziej, że może on często zawierać informacje, których nie chcemy ujawniać, takie jak nazwa użytkownika, nazwa bazy, host itp.
Inne możliwości wykorzystania
Naszą klasę db() możemy również wykorzystać wewnątrz innej klasy, wystarczy tylko w definicji zmiennych utworzyć:
class inna_klasa {
...
public $db;
...
Następnie wewnątrz funkcji _construct() tworzymy objekt klasy db() i podstawiamy pod zmienną $db klasy:
...
function _construct(){
$class_db_file = 'classes/class_db.php';
if (file_exists($class_db_file):
require_once($class_db_file);
$this->db = new db();
else:
echo 'Brak pliku z klasą db';
endif;
}
...
Funkcja operująca na bazie danych wewnątrz klasy inna_klasa() będzie wyglądać trochę inaczej. Jako przykład posłuzy nam funkcja dopisująca wiersz do tabeli products:
...
function insert_product(){
if ($this->db->connect()):
$brand = 'producent';
$product = 'nazwa produktu';
$price = 'cena produktu';
$sql = "INSERT INTO `products` VALUES (null, '$brand', '$product', '$price');";
if ($this->db->query($sql)):
return true;
else:
echo 'Błąd zapisu bazy danych: ' . $this->db->error;
return false;
endif;
$this->db->close();
else:
echo 'Błąd połączenia z bazą danych: ' . $this->db->error;
return false;
endif;
}
...
Podstawową różnicą jest sposob dostępu do funkcji $this->db->nazwa_funkcji() zamiast $db->nazwa_funkcji().
I to już chyba wszystko. Wszelkie uwagi, pomysły rozwinięcia mile widziane…


Witam dziękuje za bardzo przystępne opisanie klasy do obsługi bazy danych, ale czy w tym fragmencie: if (file_exists($class_db_file): nie brakuje drugiego nawiasu zamykającego? Ten fragment nie powinien wyglądać tak: if (file_exists($class_db_file)):
Oczywiście, że powinien. Dziękuję za zwrócenie uwagi na ten dosyć istotny błąd, który już został poprawiony.
załóżmy że do tej klasy chce dopisać funkcje realizującą mysql_fetch_array(), i mysql_num_rows(), to powinienem dać if($result = mysql_fetch_array($this->query($sql))) { return $result; czy if($result = mysql_fetch_array($this->select($sql))) { return $result; if($result = mysql_num_rows($this->query($sql))) { return $result; czy if($result = mysql_num_rows($this->select($sql))) { return $result;
Zdecydowanie $this->select(), funkcja query() służy do pozostałych operacji, takich jak UPDATE, INSERT, DELETE i zwraca true przy sukcesie lub false jeżeli operacja na bazie się nie powiedzie. Tylko funkcja select() zwraca dane z bazy, na których możemy wykonywać dalsze operacje.
Czy funkcja select_all_products() jest umieszczona w klasie db(), czy całkowicie poza nią i nie jest umieszczona w żadnej klasie? Po wprowadzeniu prezentowanych skryptów, małej przeróbce – dostosowaniu do własnych potrzeb – skrypt wyświetla błąd: “Fatal error: Call to a member function connect() on a non-object in /home itd.” Linijka, w której jest ten błąd: “if ($db->connect())” O co w tym chodzi? Połączenie jest wykonywane obiektowo metodą connect klasy db(), więc co nieobiektowo jest zwracane – wartość true (boolean). Gdzie jest błąd w prezentowanym na tej stronie skrypcie?
W tej części artykułu ukazane są sposoby wykorzystania klasy. Funkcja select_all_products() jest tylko przykładem, jedną z możliwości użycia klasy. Nie jest częścią klasy. Natomiast co do zwracanego błędu, to wygląda na to że $db nie jest obiektem. Przed wywołaniem jakiejkolwiek funkcji musi nastąpić utworzenie obiektu klasy, czyli np. $db = new db(); Dopiero wtedy można operować na funkcjach klasy i samym obiekcie, np. $db->connect()
Też miałem ten błąd co bassyl Fatal error: Call to a member function connect() on a non-object in Po analizie kodu udało mi się wywnioskować, że w funkcji function select_all_products() powinien zostać przekazany jako argument obiekt klasy db. Funkcja powinna mieć postać: function select_all_products($db) . Pojawiła się też literówka w : var_dump($selecEted_products); Po wprowadzeniu poprawek wszystko działa.
Bardzo słuszna uwaga. Dziękuję za zwrócenie uwagi. Faktycznie brak parametru przy deklarowaniu funkcji. Ewentualnie “global $db;” na początku funkcji też powinno załatwić sprawę.
Hej a czy my przypadkiem nie mamy roku 2011?
No mamy mamy… czyżbym w powyższym poście twierdził inaczej?
Opis klasy jest SUPER . . . Poza mała wątpliwościa: Jesteś pewien, że query nie zwraca wyniku zapytania “SELECT . . .”
Sorry moje wątpliwości są bez sensu. Funkcję se lect() i query() są metodami klasy a nie funkcjami php. Mylnęło mi się