Wprowadzenie do Doctrine ORM Przygotowanie środowiska Do wykonania ćwiczenia konieczne będzie zainstalowanie narzędzia Composer i odpowiednie skonfigurowanie Netbeans (Tools->Options->Framework & Tools->Composer, wskazujemy ścieżkę do pliku composher.phar). Tworzenie projektu Podczas tworzenia nowego projektu wykorzystamy Composer do pobrania biblioteki Doctrine i dodania jej do projektu: Uruchamiamy kreator New Project; w 4 kroku NIE zaznaczamy Doctrine na liście frameworków; w ostatnim kroku kreatora: o wpisujemy doctrine w polu Token i klikamy Search; o z listy w polu Packages wybieramy doctrine/orm, z listy rozwijanej Version wybieramy 2.4.7 i klikamy przycisk >. Konfiguracja Doctrine Kod z konfiguracją i inicjalizacją Doctrine umieścimy w pliku bootstrap.php: <?php use Doctrine\ORM\Tools\Setup; use Doctrine\ORM\EntityManager; require_once "vendor/autoload.php"; // użyjemy konfiguracji XML // pliki konfiguracyjne będą umieszczone w katalogu config $isdevmode = true; $config = Setup::createXMLMetadataConfiguration(array( DIR."/config"), $isdevmode); // definicja połączenia z bazą danych $conn = array( 'driver' => 'pdo_mysql', 'dbname' => 'biblioteka', 'user' => 'biblioteka', 'password' => 'biblioteka', 'host' => 'localhost', ); // stworzenie obiektu Entity Managera $entitymanager = EntityManager::create($conn, $config); Strona 1 z 5
Podczas pracy z Doctrine będziemy korzystali z pewnego narzędzia wywoływanego z linii poleceń. Aby działało ono poprawnie, w katalogu projektu musimy umieścić plik o nazwie cli-config.php z następującą zawartością: <?php use Doctrine\ORM\Tools\Console\ConsoleRunner; require_once 'bootstrap.php'; return ConsoleRunner::createHelperSet($entityManager); Przygotowanie definicji mapowania Do mapowania struktur bazy danych na klasy można wykorzystać adnotacje w kodzie, pliki YAML lub pliki XML. Skorzystamy z plików XML wygenerujemy je automatycznie na podstawie bazy danych. Aby wygenerować pliki mapowania, otwórz linię poleceń, przejdź do katalogu projektu i wykonaj polecenie: vendor\bin\doctrine orm:convert:mapping --from-database xml config Zajrzyj do wygenerowanych plików mapowania znajdują się w katalogu config. Wygenerowane encje nazywają się tak, jak tabele w bazie danych czyli odpowiednio Ksiazki i Wydawnictwa. Jest to mylące, gdyż pojedynczy obiekt encji ma reprezentować pojedynczą książkę. Zmieńmy zatem nazwy na liczbę pojedynczą, odpowiednio na Ksiazka i Wydawnictwo: o zmień odpowiednio nazwy plików z mapowaniem (Ksiazka.dcm.xml, Wydawnictwo.dcm.xml); o w plikach mapowania XML zmień nazwy encji: w encji Wydawnictwa: <entity name="wydawnictwo" table="wydawnictwa"> w encji Ksiazki: <entity name="ksiazka" table="ksiazki"> oraz w relacji do wydawnictw: <many-to-one field="wydawnictwo" target-entity="wydawnictwo"> Strona 2 z 5
Na podstawie plików XML wygenerujemy klasy encji (klasy Ksiazka oraz Wydawnictwo). Stwórz w projekcie folder o nazwie entities. Z linii poleceń wydaj komendę: vendor\bin\doctrine orm:generate:entities entities Przyjrzyj się klasom wygenerowanym w katalogu entities. Pobranie encji z bazy danych Na stronie głównej, w index.php, wyświetlimy tabelkę z książkami. Musimy załączyć pliki z inicjalizacją Doctrine oraz z definicją encji. W pliku index.php umieść następujący kod: require 'bootstrap.php'; require 'entities/ksiazka.php'; require 'entities/wydawnictwo.php'; Następnie możemy zadać zapytanie w języku DQL pobierzmy z bazy wszystkie encje typu Ksiazka i wyświetlmy w tabelce: $dql = "SELECT b FROM Ksiazka b"; $query = $entitymanager->createquery($dql); $ksiazki = $query->getresult(); echo '<table>'; echo '<tr><th>id</th><th>tytuł</th><th>wydawnictwo</th></tr>'; foreach ($ksiazki as $k) { $id = $k->getid(); $tytul = $k->gettytul(); $nazwawydawcy = $k->getwydawnictwo()->getnazwa(); $idwydawcy = $k->getwydawnictwo()->getid(); echo "<tr><td>{$id}</td><td>{$tytul}</td> <td>[{$idwydawcy}] {$nazwawydawcy}</td></tr>"; } echo '</table>'; Zwróć uwagę na dostęp do pól pochodzących z encji Wydawnictwo Doctrine automatycznie dba o zapewnienie dostępu do powiązanych encji. W przypadku prostych zapytań, zamiast pisać zapytanie DQL możemy skorzystać z repozytorium skojarzonego z danym typem encji zamiast trzech linijek z zapytaniem DQL i wywołaniem metod createquery() i getresult(), umieść następujący kod: $repo = $entitymanager->getrepository('ksiazka'); $ksiazki = $repo->findall(); Strona 3 z 5
Repozytoria mają też inne metody do pobierania danych, np. według klucza głównego albo dowolnego innego pola, na przykład: $k = $repo->find(3); $k = $repo->findoneby(['tytul' => 'Biblia MySQL']); $k = $repo->findby(['wydawnictwo' => 3]); Wypróbuj powyższe metody pobierając i wyświetlając pod tabelką tytuły wybranych, pojedynczych książek. Zapisywanie danych do bazy modyfikacja i tworzenie rekordów Modyfikację istniejącej książki przeprowadzimy następująco: pobierzemy encję (na przykład zapytaniem DQL, metodą find() z repozytorium, lub też metodą find() z klasy EntityManager), zmienimy wybrane pola obiektu, zapiszemy obiekt metodą persist() menedżera encji, zatwierdzimy zapis wszystkich danych do bazy metodą flush() menedżera encji. // Modyfikacja obiektu - znajdź, zmień, zapisz $ksiazkatelefoniczna = $entitymanager->find('ksiazka', 1); $ksiazkatelefoniczna->settytul('zmieniony tytul'); $entitymanager->persist($ksiazkatelefoniczna); $entitymanager->flush(); Tworzenie nowej książki przeprowadzimy następująco: pobierzemy z bazy wybrane wydawnictwo, aby je następnie skojarzyć z tworzoną książką; stworzymy obiekt klasy Ksiazka i wypełnimy danymi; podobnie jak przy modyfikacji wywołamy persist() oraz flush(). $helion = $entitymanager->find('wydawnictwo', 2); $nowaksiazka = new Ksiazka(); $nowaksiazka->settytul('testowy tytul 2'); $nowaksiazka->setautor('jan Kowalski'); $nowaksiazka->setwydawnictwo($helion); $entitymanager->persist($nowaksiazka); $entitymanager->flush(); Strona 4 z 5
Zadanie 1 Zaimplementuj funkcjonalność dodawania książek. Zadanie 2 Włącz logowanie zapytań SQL. Sprawdź, jakie zapytania są wysyłane do bazy przy wyświetlaniu listy książek. Sprawdź, co się zmieni, jeśli zamiast zapytania: SELECT b FROM Ksiazka b użyjemy zapytania: SELECT b, w FROM Ksiazka b JOIN b.wydawnictwo w Sprawdź, co się zmieni, jeśli zmienimy mapowanie w relacji pomiędzy encjami Ksiazka i Wydawnictwo włączymy parametr fetch=eager. Strona 5 z 5