3. Tworzenie schematu bazy danych
Doctrine wykorzystuje pliki YAML do tworzenia i zarządzania schematem bazy danych. Składają się na niego dwa elementy:
- Struktura
- Początkowe dane (tzw. fixtures), które są wgrywane wraz ze strukturą.
My zajmiemy się jedynie pierwszym elementem, jako że do drugiego nie bardzo mamy co wgrywać. Nasza baza będzie bardzo prosta, a w jej skład wejdą tylko dwie tabele: na zdjęcia oraz na komentarze. Utwórzmy plik /doctrine/schema/schema.yml i na jego początku umieśćmy następujący tekst:
---
options:
type: InnoDB
collate: utf8_polish_ci
charset: utf8
Są to opcje dotyczące tabel i zależą od wyboru konkretnego silnika bazodanowego. Artykuł ten pisany jest z myślą o bazie MySQL, stąd ustaliliśmy, iż żądamy tabel w formacie InnoDB oraz obsługi unikodu.
Jak widać, składnia YAML jest bardzo czytelna dla człowieka, a co ważniejsze, jest konwertowana bezpośrednio do tablic PHP, w przeciwieństwie np. do XML. Wadą są niestety dość powolne parsery, ale Doctrine korzysta z nich jedynie od czasu do czasu, na nasze żądanie. Niezmiernie istotne jest, aby nie używać tabulacji, lecz wcięcia robić stałą, określoną ilością spacji – ja przyjąłem cztery. Dodajmy teraz definicję pierwszej tabeli, przechowującej informacje o zdjęciach:
Photo:
tableName: photos
columns:
id:
type: integer(4)
primary: true
notnull: true
autoincrement: true
title:
type: string(50)
notnull: true
filename:
type: string(50)
notnull: true
date:
type: integer(4)
notnull: true
listeners: [PhotoListener]
relations:
Comments:
class: Comment
local: id
foreign: photo_id
onUpdate: CASCADE
onDelete: CASCADE
type: many
foreignType: one
foreignAlias: Photo
Pierwszy identyfikator jest nazwą, jaką będziemy używać do odnoszenia się do danej tabeli w Doctrine. U nas będzie to Photo. Możemy również określić, jaka nazwa ma być faktycznie używana po stronie bazy danych. W sekcji columns umieszczamy informacje o wszystkich kolumnach. Jeśli chcemy jedynie zdefiniować typ kolumny, możemy użyć skróconej formy zapisu:
pole1: typ
pole2: typ
pole3: typ(długość)
Doctrine posiada własny system typów, które są automatycznie konwertowane na typy charakterystyczne dla wybranego silnika bazodanowego. My korzystamy z dwóch standardowych: integer oraz string, w obydwu podajemy ich długość w bajtach. Doctrine musi również wiedzieć, jakich relacji między tabelami będziemy używać. Służy do tego sekcja relations. Każdej relacji w obrębie tabeli musimy nadać unikalną nazwę, poprzez którą będziemy mogli się do niej odnosić w DQL-u. Następnie określamy jej atrybuty:
class– nazwa tabeli (klasy) PHP, do której tworzymy relację.local– nazwa pola w aktualnej tabeli, które ma być użyte.foregin– nazwa pola w drugiej z tabel, które ma być użyteonUpdate,onDelete– zdefiniowanie akcji do wykonania na powiązanych relacją wierszach obcej tabeli w przypadku modyfikacji (usunięcia) wiersza określającego dane zdjęcie. OpcjaCASCADEjest jedną ze standardowych akcji silników bazodanowych, powoduje ona automatyczną modyfikację (usunięcie) wszystkich powiązanych wierszy.type– kierunek relacji Photo -> Comment (wiele komentarzy)foreignType– określamy kierunek relacji Comment -> Photo (jedno zdjęcie dla komentarza)foreignAlias– nazwa, pod jaką będzie widziany model Photo z punktu widzenia komentarzy.
Mamy tutaj jeszcze jedną sekcję, listeners, jednak zajmiemy się nią nieco później.
Tabela, w której będziemy przechowywać komentarze, zbudowana jest następująco:
Comment:
tableName: comments
columns:
id:
type: integer(4)
primary: true
notnull: true
autoincrement: true
author:
type: string(50)
notnull: true
date:
type: integer(4)
notnull: true
content:
type: string(4000)
notnull: true
photo_id:
type: integer(4)
notnull: true
listeners: [CommentListener]
indexes:
Photo_Index:
fields: [photo_id]
Struktura jest tu podobna do poprzedniej tabeli, lecz pojawiła się nam nowa sekcja: indexes. Służy ona do definiowania indeksów, jakie będą użyte w tabelach. Analogicznie, każdemu indeksowi musimy nadać unikalną nazwę oraz zdefiniować listę kolumn, na które zostanie on nałożony. Służy do tego nawias kwadratowy, a poszczególne nazwy wymieniamy po przecinku.