Wyzwól potęgę ORM
PHP Kohana
Twórcy Kohany dali nam do ręki potężne narzędzie - bibliotekę Object Relational Mapping (ORM). Programowanie z jej użyciem jest dziecinnie proste, wygodne i bardzo intuicyjne. ORM to tak naprawdę zestaw kilku rozszerzalnych klas, które pozwalają na traktowanie rekordów bazy danych jako obiektów powiązanych ze sobą relacjami. Co ważne, z ORM można pracować zupełnie nie wykorzystując zapytań SQL. Zapraszam do przeczytania artykułu.
W czym tkwi tak naprawdę moc ORM?
W prostocie udostępnionego API. Przykładowo, jeśli mamy w bazie danych tabele newsów i komentarzy, a między nimi zachodzi relacja wiele do jednego (jeden news może mieć nieskończenie wiele komentarzy), to po utworzeniu obiektu reprezentującego news'a
$news = ORM::factory('news', $news_id);możemy się odwołać do jego komentarzy w ten sposób:
foreach($news -> comments as $comment) {
echo $comment -> content;
};Prawda, że proste? Należy pamiętać, że właściwość comments w obiekcie news'a to tak naprawdę obiekt typu kolekcja, (o kolekcji też będzie artykuł
) ORM_Iterator, który zwraca przy każdej iteracji kolejny obiekt komentarza.
Nie zawsze można używać tego bajeru
Jak wszystko na tym świecie, również ORM ma pewne wymagania, które należy spełnić, aby można było z niego skorzystać. Dotyczą one struktury tabeli w bazie danych.
- Należy stosować anglojęzyczne nazewnictwo w nazwach tabeli i klas
- Nazwy tabeli muszą być rzeczownikami liczby mnogiej, na przykład comments, songs itd.
- Nazwy klas modeli rozszerzających ORM muszą być rzeczownikami liczby pojedynczej, na przykład comment, song
- Każda tablica powinna mieć kolumnę o nazwie id ustawioną jako klucz główny z właściwością auto_increment
- Kolumnom zawierającym klucze obce należy nadawać nazwy według schematu:nazwaklasymodelu_id, na przykład song_id
- Tabele zawierające relacje wiele do wielu powinny być nazywane według schematu: nazwatabeli1_nazwatabeli2, gdzie nazwatabeli1 i nazwatabeli2 ułożone są alfabetycznie, na przykład comments_news, czy authors_songs
Definiowanie klasy korzystającej z ORM
Na początek zdefiniujmy klasy song i artist. Klasa song:
class Song_Model extends ORM {}Klasa artist:
class Artist_Model extends ORM {}Zapisujemy oba pliki w folderze application/models (song.php i artist.php).
Określanie wzajemnych relacji
Aby zdefiniować zależności w ORM, należy posłużyć się jedną lub więcej z 4 specjalnych właściwości obiektu. Są to:
- has_one - relacja jeden do jednego
- has_many - relacja jeden do wielu (dla korzenia w drzewie zależności)
- belongs_to - relacja jeden do wielu (dla liścia w drzewie zależności)
- has_and_belongs_to_many - relacja wiele do wielu
class Song_Model extends ORM {
protected $belongs_to = array('artist');
}class Artist_Model extends ORM {
protected $has_many = array('songs');
}Należy zwrócić uwagę, że belongs_to zawiera nazwy modeli (liczba pojedyncza), zaś pozostałe trzy właściwości określające relacje zawierają nazwy tabel (liczba mnoga).
Tutaj znajduje się zrzut bazy danych MySQL z przykładowymi danymi.Tworzenie instancji klasy rozszerzającej ORM
Można tego dokonać dwojako - albo korzystając ze statycznej metody factory klasy ORM
$artist = ORM::factory('artist', 1);Metoda ta przyjmuje dwa argumenty. Pierwszym jest nazwa modelu, a drugim (opcjonalnym) ID rekordu w bazie danych. Jeśli go pominiemy, wtedy zostanie utworzony nowy, pusty obiekt. Drugim sposobem jest skorzystanie z tradycyjnej składni instancjonowania
$artist = new Artist_Model(1);
W tym przypadku konstruktor przyjmuje jeden opcjonalny argument i ponownie jest to ID rekordu.
Dodawanie nowego wykonawcy do bazy danych
Algorytm postępowania wygląda następująco:
- 1. Tworzymy nowy obiekt Artist_Model bez podawania ID
- 2. Przypisujemy mu wartości
- 3. Wywołujemy metodę save i cieszymy się nowo zapisanym rekordem.

`id` MEDIUMINT NOT NULL PRIMARY KEY AUTO_INCREMENT, `name_and_surname` VARCHAR(255) NOT NULLZatem interesuje nas ustawienie tylko imienia i nazwiska
$new_artist = ORM::factory('artist');
$new_artist -> name_and_surname = 'Jay-Z';
$new_artist -> save();I to wszystko.
Przypisywanie piosenek do wykonawcy
...to też kaszka z mleczkiem.
- 1. Tworzymy nowy obiekt(y) Song_Model bez podawania ID
- 2. Przypisujemy mu(im) wartości
- 3. Przypisujemy obiektom Song_Model wartość id obiektu Artist_Model dla własności artist_id
- 4. Wywołujemy metodę save w obiektach modeli.
$song1 = ORM::factory('song');
$song1 -> name = 'Dirt off your shoulder';
$song1 -> artist_id = $new_artist -> id;
$song1 -> save();Bardziej interesujący przykład
Znacznie ciekawsze jest budowanie relacji typu wiele do wielu. Zmodyfikujmy klasę Artist_Model i dodajmy jeszcze jedną, Album_Model. Przyjmijmy, że jeden wykonawca może występować na nieskończenie wielkiej liczbie albumów i zarazem na albumie może gościć nieskończenie wielu artystów, zatem jest to relacja wiele do wielu. Najpierw modyfikacje w kodzie:
class Artist_Model extends ORM {
protected $has_many = array('songs');
protected $has_and_belongs_to_many('albums');
}class Album_Model extends ORM {
protected $has_and_belongs_to_many('artists');
}W bazie danych będziemy potrzebowali dwóch nowych tabel:
CREATE TABLE `albums` ( `id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, `title` VARCHAR(255) NOT NULL ) ENGINE = InnoDB DEFAULT CHARSET = utf8; CREATE TABLE `albums_artists` ( `album_id` MEDIUMINT UNSIGNED NOT NULL, `artist_id` MEDIUMINT UNSIGNED NOT NULL, PRIMARY KEY (album_id, artist_id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; ALTER TABLE `albums_artists` ADD CONSTRAINT albums_artists_idfk_1 FOREIGN KEY (album_id) REFERENCES albums (id) ON DELETE CASCADE, ADD CONSTRAINT albums_artists_idfk_2 FOREIGN KEY (artist_id) REFERENCES artists (id) ON DELETE CASCADE;
Tym razem nie będziemy przez ORM przypisywać id albumów i wykonawców do tabeli albums_artists łącząc je w pary, gdyż nie o to chodzi. Natomiast dysponując zapisanymi w bazie obiektami Artist_Model i Album_Model...
$artist = ORM::factory('artist');
$artist -> name_and_surname = 'Szymon Wydra';
$artist -> save();
$album = ORM::factory('album');
$album -> title = 'Teraz wiem';
$album -> save();Wywołujemy w dowolnym z nich metodę add i jako argument przekazujemy drugi obiekt. Następnie wywołujemy metodę save.
$album -> add($artist); $album -> save();
Nic trudnego. 
Na koniec
...pozostała jeszcze jedna ważna kwestia - jak pobierać z bazy rekordy spełniające określone warunki? W tym celu wystarczy się posłużyć query_builder'em z biblioteki Database. Brzmi groźnie? Spokojnie, to nic strasznego. Prześledźmy kilka przykładów:
$song = ORM::factory('song') -> where('name', 'Feel') -> find();
$artist = ORM::factory('artist') -> where('name_and_surname', 'Serj Tankian') -> find();
$songs = ORM::factory('song') -> where('artist_id', $artist -> id) -> find_all();W pierwszym przykładzie wybieramy z bazy rekord z tabeli, którą reprezentuje obiekt Song_Model (notabene chodzi o songs) i którego pole 'name' ma wartość 'Feel'. Innymi słowy, pobieramy piosenkę o tytule Feel.
Jak widać, pierwsza linijka kodu kończy się na metodzie find(). To istotne, bez wywoływania jej lub find_all (o której za chwilę) baza nie zwróci nam żadnych wyników i pozostaniemy z niczym.
Różnica między find, a find_all polega na tym, że find zwraca jeden rekord (jeden obiekt ORM), a find_all wszystkie, które spełniają warunki (obiekt ORM Iterator, o którym była już mowa wcześniej). Jak widać w drugim przykładzie, próbuję wybrać z bazy danych informacje o piosenkach Serj'ego Tankiana. Nieważne, czy jest tam jedna czy 10000, baza zwróci wszystkie.
Odnośnie query_builder'a powstanie również artykuł(y?). Wspomnę tylko, że można go również wykorzystywać w ten sposób:
foreach($artist -> where('rank', 1) -> songs as $ranked_songs) {
echo $song -> name;
}To przykład trochę 'na sucho', bowiem w przykładowej tabeli nie ma kolumny rank. Wystarczy, że demonstruje jak wybierać spośród powiązanych z obiektem rekordów te, które nas interesują.
Na dzisiaj to już wszystko, dziękuję wszystkim, którzy dotrwali jakoś do końca.
Dokumentacja ORM (w języku Szekspira naturalnie)