Artykuł

flickr.com/photos/polarity/3138680190 flickr.com/photos/polarity/3138680190
cze 13 2013
0

Doctrine DBAL - przyjemna biblioteka do obsługi bazy danych w PHP

Baza danych to w dzisiejszych czasach kluczowy element wielu usług i aplikacji. Można powiedzieć, że na przestrzeni lat zmieniają się języki i podejścia do programowania, ale zawsze gdzieś tam w tle pojawia się magazyn, do którego zapisywane są informacje. Silniki baz danych oczywiście również ewoluują, ale sama mechanika działania pozostaje raczej niezmienna.

Bazy danych dobrze współgrają z językami programowania i każdy szanujący się język oferuje wsparcie dla tego mechanizmu. Nie mogło go oczywiście zabraknąć w PHP, którym sieć stoi - samych instalacji Wordpressa i Joomli mamy przecież dziesiątki milionów.

Obsługa bazy danych w PHP zrealizowana jest dość nisko poziomo i w praktyce, większość programistów albo obudowuje standardowy kod według własnego uznania, albo też korzysta z gotowych bibliotek. Pamiętajcie, że programując nie warto wymyślać koła od nowa, dlatego też jeśli tylko możecie, sięgajcie po gotowe rozwiązania.

W przypadku bazy danych i PHP warto zapoznać się z tytułową biblioteką Doctrine, która dostarcza nam pewną abstrakcyjną warstwę do obsługi bazy danych.

Doctrine project

Doctrine to generalnie większy projekt, na który składa się kilka mniejszych, tematycznych bibliotek przy czym najbardziej znany jest DBAL (skrót od Database Abstraction Layer) oraz ORM (skrót od Object Relational Mapper). W tym tekście zajmę się tą pierwszą z nich.

DBAL to tak jak wspominałem wcześniej (i jak zresztą wynika z angielskiego tłumaczenia) specjalna abstrakcyjna warstwa do obsługi bazy danych. Korzystając z Doctrine, piszemy tak na dobrą sprawę jeden kod, który bez większych zmian zadziała pod różnymi bazami danych - wystarczy zmienić tylko użyty przez bibliotekę sterownik.

Skoro już mowa o sterownikach, to tych w bibliotece mamy kilka:

  • pdo_mysql - sterownik MySQL wykorzystujący rozszerzenie pdo_mysql
  • pdo_sqlite - sterownik SQLite wykorzystujący rozszerzenie pdo_sqlite
  • pdo_pgsql - sterownik PostgreSQL wykorzystujący rozszerzenie pdo_sqlite
  • pdo_oci - sterownik Oracle wykorzystujący rozszerzenie pdo_oci
  • pdo_sqlsrv - sterownik Microsoft SQL Server wykorzystujący rozszerzenie pdo_sqlsrv
  • oci8 - sterownik Oracle wykorzystujący rozszerzenie oci8 z PHP

Jak widać, biblioteka obsługuje wszystkie popularne silniki i powinna zadowolić największych malkontentów;)

Siłą tego rozwiązania jest również spora liczba gotowych funkcji. Korzystając z Doctrine można w praktyce zapomnieć o standardowych zapytaniach SQL typu INSERT, UPDATE oraz DELETE. Biblioteka oferuje gotowe metody - odpowiednio insert, update oraz delete, do których wystarczy przekazać odpowiednie tablice z danymi.

Postaram się zaprezentować poszczególne funkcje w dalszej części wpisu.

Testowa baza MySQL

Dla przykładu postanowiłem zastosować bazę MySQL. PHP i MySQL to wciąż dobrze zgrany tandem. Łatwo można sobie również takie testowe środowisko postawić w domu - instalując choćby popularny WampServer.

Na wstępie utwórzcie sobie bazę danych o dowolnej nazwie, a następnie za pomocą poniższego zapytania, dodajcie do niej tabelę Article:

CREATE TABLE Article (
	ArticleId INTEGER NOT NULL AUTO_INCREMENT,
	Title VARCHAR(150) NOT NULL,
	Content TEXT NOT NULL,
	Published BOOL NOT NULL,
	PRIMARY KEY(ArticleId)
)
DEFAULT CHARSET="utf8" COLLATE utf8_polish_ci;

Nasza tabela jest bardzo prosta i pozwoli ona nam na zapisywanie danych naszych wyimaginowanych artykułów. W kolejnych jej polach przechowujemy:

  • Id artykułu
  • Tytuł
  • Zawartość
  • Flagę publikacji

Kluczem głównym jest oczywiście ArticleId, który dodatkowo korzysta z właściwości AUTO_INCREMENT.

Pobieranie Doctrine

Zanim przystąpimy do pracy z kodem, musimy pobrać bibliotekę Doctrine. W momencie gdy piszę ten wpis, z niewiadomych dla mnie przyczyn na stronie projektu wycięto linki do pobrania. Na szczęście można pobrać bibliotekę skąd inąd. Ja znalazłem ją (w odrobinę starszej wersji) na stronie Softpedii.

Po pobraniu biblioteki, należy ją umieścić w odpowiednim folderze naszej aplikacji. Struktura mojej strony wygląda tak jak na screenie 1. W katalogu głównym znajduje się oczywiście plik index.php, w którym dla uproszczenia umieszczę cały kod. Nie bierzcie oczywiście ze mnie przykładu w rozwiązaniach produkcyjnych;-)

Inicjalizacja Doctrine

Inicjalizacja Doctrine to chyba najbardziej zagmatwany moment w wykorzystaniu tej biblioteki. Musimy tutaj zadbać o odpowiednie ustawienia ścieżek oraz należycie zainicjalizować klasę, która wczytuje inne klasy biblioteki (ClassLoader).

Na poniższym listingu znajduje się kod, który:

  • inicjalizuje wszystkie niezbędne klasy potrzebne do pracy z naszym przykładem
  • ustawia tablicę parametrów połączenia
  • pobiera obiekt połączenia
<?php
require_once 'lib/php/Doctrine/Common/ClassLoader.php';

$classLoader = new \Doctrine\Common\ClassLoader('Doctrine', 'lib/php');
$classLoader->register();

$config = new \Doctrine\DBAL\Configuration();
$connectionParams = array(
    'dbname' => 'test',
    'user' => 'uzytkownik',
    'password' => 'haslo',
    'host' => 'localhost',
    'driver' => 'pdo_mysql',
	'charset' => 'utf8'
);
$conn = \Doctrine\DBAL\DriverManager::getConnection($connectionParams, $config);
//
?>

Na powyższym listingu warto zwrócić uwagę na dwie rzeczy:

  • W linii 4 tworzymy obiekt klasy ClassLoader. Przyjmuje on dwa argumenty, z których pierwszy odpowiada za nazwę przestrzeni nazw w wybranej bibliotece, natomiast drugi pozwala na opcjonalne podanie ścieżki do podkatalogu w którym znajduje się Doctrine (patrz screen 1)
  • W liniach 8-15 tworzymy tablicę parametrów połączenia. Wydaje mi się że nazwy kluczy są dość oczywiste. Warto zwrócić jednak uwagę na nazwę sterownika (możliwe opcje wymieniłem w akapicie Doctrine Project) oraz na kodowanie - należy je ustawić aby uniknąć krzaków. Reszta ustawień zależna jest już stricte od Was

Powyższy kod dostarcza nam obiekt, za pomocą którego będziemy mogli wykonywać konkretne zapytania. Pamiętajcie również by na końcu zamknąć połączenie:

$conn->close();

Oczywiście nie muszę nikomu mówić, że cały kod aż prosi się o upakowanie w odpowiedniej klasie;-) Mój luźny kod to tak jak wspominałem tylko przykład.

Standardowe operacje na bazie danych

Przyjrzyjmy się teraz kluczowym funkcjom, które pozwolą na wykonanie standardowych zapytań do bazy danych.

Insert

Tak jak wpisałem wcześniej, Doctrine posiada wiele gotowych funkcji, które z pewnością wykorzystamy na co dzień. jedną z nich jest funkcja pozwalająca na dodawanie danych do tabeli, czy najzwyczajniejszy w świecie insert. Spójrzcie tylko na przykładowo kod, by zobaczyć jak dodatkowa warstwa abstrakcji ułatwia w tym przypadku życie:

$conn->insert('Article',
	array(
		'Title' => 'Wpis testowy',
		'Content' => 'Testowa zawartość',
		'Published' => 1
	),
	array(
		PDO::PARAM_STR, PDO::PARAM_STR, PDO::PARAM_INT
	)
);

Konstrukcja tej funkcji jest prosta i zasadniczo przyjmuje ona trzy argumenty, z czego ostatni jest opcjonalny:

  • Nazwa tabeli
  • Tablica asocjacyjna wartości, gdzie kluczami są nazwy kolumn z bazy danych, a wartościami nasze własne dane. Zwróćcie uwagę, że pominąłem ArticleId ze względu na AUTO_INCREMENT
  • Opcjonalna tablica asocjacyjna typów danych. W tym przypadku jest ona opcjonalna, ale bywa bardzo przydatna gdy operujemy np. na datach. Poszczególne typy danych znajdziemy w kodzie biblioteki oraz dokumentacji

Podobną strukturę mają również funkcje update oraz delete.

Update

Funkcja update jest bardzo zbliżona do wcześniej poznanej funkcji insert, z tym że pojawia się tutaj dodatkowa tablica, która pozwala określić, które rekordy mają zostać zaktualizowane. Aby zatem zmienić artykuł o id 1, musimy wykonać poniższy kod:

$conn->update('Article',
	array(
		'Title' => 'Wpis testowy [Aktualizacja]',
		'Content' => 'Testowa zawartość. Testowa zawartość.'
	),
	array('ArticleId' => 1),
	array(
		PDO::PARAM_STR, PDO::PARAM_STR, PDO::PARAM_INT
	)
);

Zwróćcie uwagę, że nie musimy oczywiście aktualizować wszystkich kolumn, tylko te które nam się żywnie podoba. Również w tym przypadku można zastosować opcjonalną tablicę typów. Tu zachodzi pewien trick, ponieważ określona ona typy zarówno wartości aktualizowanych jak i kluczy wyszukiwania. Poszczególne typy są po prostu wymienione w kolejności wystąpienia:

  • PDO::PARAM_STR - Title
  • PDO::PARAM_STR - Content
  • PDO::PARAM_INT - ArticleId

Delete

Usuwanie jest równie proste. W tym przypadku należy podać nazwę tabeli oraz tablicę asocjacyjną, w której wskażemy dane do usunięcia. Żeby usunąć artykuł o identyfikatorze 1, należy zastosować następujący kod:

$conn->delete('Article', array('ArticleId' => 1));

Select

Wykonaliśmy szereg operacji na naszej tabeli, ale wciąż nie zrobiliśmy jednej dość istotnej rzeczy - nie wyświetliliśmy żadnych danych! Za tą operację odpowiada kilka różnych funkcji. Najprostszą z nich jest query i używamy jej zazwyczaj w sytuacji gdy wyświetlamy zawartość wybranej tabeli:

$statement = $conn->query('SELECT * FROM Article');
while($row = $statement->fetch())
{
	echo '<br />Dane dla artykułu o id: '.$row['ArticleId'];
	print_r($row);
}

Funkcja query zwraca obiekt statement, z którego za pomocą funkcji fetch pobieramy kolejne wiersze. W każdym wierszu znajdziemy tablicę asocjacyjną, której kluczami będą pobrane nazwy kolumn.

Sprawy komplikują się trochę bardziej, w sytuacji gdy chcemy np. wyświetlić konkretny artykuł, czyli musimy w zapytaniu umieścić nasze własne parametry. W takim przypadku powinniśmy korzystać z zapytań sparametryzowanych, które z Doctrine są jednak dużo prostsze niż ma to miejsce w przypadku czystego PHP. Spójrzcie na poniższy przykład:

$statement = $conn->prepare('
	SELECT * FROM Article
	WHERE ArticleId = :articleid
');
$statement->bindValue('articleid', 1);
$statement->execute();
$row = $statement->fetch()
echo '<br />Dane dla artykułu o id: '.$row['ArticleId'];
print_r($row);

W tym przypadku zamiast funkcji query, stosujemy nową funkcję prepare. Następnie za pomocą bindValue przekazujemy wartość parametru articleid. Parametry oznaczamy albo za pomocą dwukropka i unikalnej nazwy, albo przy pomocy znaku zapytania. Korzystając z tej drugiej konstrukcji, w funkcji bindValue podajemy porządkowy numer parametru, licząc od 1.

W przypadku zapytań sparametryzowanych, musimy również powiedzieć, kiedy zapytanie ma się wykonać. Czynimy to za pomocą funkcji execute. Dalej postępujemy już analogicznie jak w przypadku funkcji query.

Na koniec prosty przykład, w którym pobierzemy liczbę artykułów:

$statement = $conn->query('SELECT COUNT(ArticleId) FROM Article');
echo 'Liczba artykułów w bazie: '.$statement->fetchColumn();

W tym przypadku całość została bardzo uproszczona. Zapytanie zwraca jedną kolumnę, dlatego też nie ma w takim przypadku konieczności parsowania całego wiersza, więc wydobywamy informacje z pierwszej kolumny korzystając z funkcji fetchColumn.

Doctrine ma znacznie większe możliwości, a powyższe uznajcie tylko za próbkę tego, co tak naprawdę oferuje biblioteka. Nie będę powielał czegoś co już napisano, dlatego też po więcej odsyłam do oryginalnej dokumentacji projektu.

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus