środa, 3 czerwca 2015

[PHP][Propel] Instalacja oraz wstęp do Propel ORM

TRUE
3045453951852043091
Propel do biblioteka służąca do mapowania obiektowo-relacyjnego, w skrócie ORM. Podobnie jak biblioteka Doctrine sprawia on, że praca na bazie danych odbywa się za pomocą zwykłych obiektów. Jest odrobinę łatwiejszy w użyciu, niż Doctrine, co jednak nie znaczy, że jest gorszy. Praca z obiema bibliotekami jest w zasadzie podobna, opiera się ona na przygotowaniu schematu bazy danych, wygenerowaniu odpowiednich metod dla modelu, a następnie na pracy na modelu jak na zwykłych obiektach. Propela, podobnie jak Doctrine możemy w prosty sposób połączyć z popularnymi frameworkami, jak Zend, czy Symfony - integracja taka zostanie tutaj opisana w którymś z artykułów.

W poniższym natomiast artykule pobierzemy i zainstalujemy sobie bibliotekę Propela, skonfigurujemy ją, uruchomimy, a następnie przygotujemy prosty, testowy model bazy i zobaczymy jak to mniej więcej działa. Artykuł przedstawia procedurę w systemach Windows, na Linuxach jednak wszystko (poza oczywiście innymi ścieżkami) wygląda podobnie.

Instalacja Propel ORM

Oficjalna strona Propela to: http://propelorm.org.
Samego Propela pobierzemy sobie za pomocą Composera, przygotowując plik composer.json w odpowiednim katalogu dla naszego projektu. Na potrzeby artykułu niech będzie to katalog:
[code]C:/www/propel[/code]
(na screenach będzie to u mnie c:/wamp/www/propel/)
W stworzonym katalogu utwórzmy plik composer.json o treści:
[code]
{
    "require": {
        "propel/propel": "~2.0@dev"
    }
}[/code]
zapiszmy, a następnie będąc w utworzonym katalogu zainstalujmy z poziomu terminala:
[code]
$ cd c:/www/propel
$ composer install[/code]
Pobierze to nam najnowszą wersję Propela z gałęzi 2.x.


Możemy też pobrać za pomocą Gita:
[code]
$ cd c:/www
$ git clone git://github.com/propelorm/Propel2.git propel[/code]

lub zwyczajnie pobrać plik zip lub tar.gz ze strony projektu i rozpakować do c:/www/propel. Ja polecam jednak metodę z Composerem.

Propel wraz z biblioteką zainstaluje nam małe narzędzie konsolowe w katalogu:
[code]/vendor/bin[/code]
które posłuży nam później do zautomatyzowania wielu operacji.
Jeśli chcemy później korzystać z polecenia propel bez podawania ścieżki, dodajmy ten folder do zmiennej PATH w Windowsie.

Pierwszy projekt

Możemu już zainicjować swój pierwszy projekt i możemy to zrobić na dwa sposoby: ręcznie, lub za pomocą narzędzia konsolowego, które lekko nam ten proces zautomatyzuje.

Pół automatycznie zrobimy to za pomocą:
[code]$ propel init[/code]
My jednak omówmy sobie ręczne przygotowanie Propela.

Testowa baza

Dobrze byłoby abyśmy na starcie mieli conajmniej dwie tabele, tak aby pokazać definicję kluczy obcych, zrobimy więc podobną strukturę jak w dokumentacji Propela, czyli sklep z książkami. Nasza testowa baza będzie mieć nazwę shop i składać się będzie z dwóch prostych tabel:

  • book
  • author


Tabela: book
Nazwa pola Typ Dodatkowe Klucz obcy Opis
id integer PRIMARY KEY, AUTO_INCREMENT, NOT NULL ID książki
title varchar(255) NOT NULL tytuł książki
author_id integer NOT NULL author.id ID autora
price integer NOT NULL cena książki



Tabela: author
Nazwa pola Typ Dodatkowe Klucz obcy Opis
id integer PRIMARY KEY, AUTO_INCREMENT, NOT NULL ID autora
first_name varchar(100) NOT NULL imię autora
last_name varchar(100) NOT NULL nazwisko autora

Uwaga: bazy na razie nie tworzymy, wyobraźmy sobie jedynie jej strukturę.

Schemat bazy - schema.xml

Na początek utworzyć musimy opis struktury naszej bazy w pliku:
[code]schema.xml[/code]
Plik ten zapisujemy w katalogu głównym naszego projektu, czyli w naszym przypadku w:
[code]c:/www/propel/[/code]
Jak już widać po rozszerzeniu, opis naszej bazy przygotowywać będziemy w języku XML.

1) <database>

Głównym tagiem w pliku XML musi być zawsze tag <database>, a w jego atrybutach powinniśmy podać nazwę naszej bazy oraz sposób w jaki będą inkrementowane wartości w polach typu auto increment. Domyślnie jest to native, co oznacza mniej więcej tyle, iż zajmie się tym automatycznie już sama baza.
[code]<?xml version="1.0" encoding="UTF-8"?>
<database name="shop" defaultIdMethod="native">
  <!-- tutaj następnie definicje tabel -->
</database>[/code]

2) <table>

Następnie zdefiniować musimy tabele w naszej bazie. Każda tabela musi zostać objęta tagiem <table> a w jej atrybutach musimy podać jej nazwę oraz opjonalnie nazwę klasy dla niej używaną przez Propela, o ile chcemy, aby była inna niż prawdziwa nazwa tabeli. Jeśli nie zdefiniujemy atrybutu phpName to Propel automatycznie wykorzysta nazwę tabeli, wykorzystując zapis camelCase, tj. klasa dla tabeli book otrzymałaby automatycznie nazwę Book, a np. klasa dla tabeli user_config otrzymała by nazwę UserConfig - zgodnie z zapisem formatu camelCase.
[code]<?xml version="1.0" encoding="UTF-8"?>
<database name="shop" defaultIdMethod="native">
  <table name="book" phpName="Book">
    <!-- tutaj następnie definicje pól/komun -->
  </table>
  <table name="author" phpName="Author">
    <!-- tutaj następnie definicje pól/komun -->
  </table>
</database>[/code]

3) <column>

Mamy już definicję bazy i tabel, pora zdefiniować kolumny w tabelach. Robimy to za pomocą znaczników <column>, podając następujące atrybuty:

  • name - nazwa kolumny, np. "title"
  • type - typ kolumny, np. "varchar"
  • size - długość pola, np "120" dla varchar
  • required - czy pole jest wymagane, np. true
  • autoIncrement - czy pole jest typu AUTO INCREMENT, np. true
  • primaryKey - czy pole posiada klucz podstawowy, np. true
  • phpName - analogicznie jak przy nazwie tabeli, możemy podać nazwę pod jaką bedzie dane pole deniniował Propel

Schemat naszej bazy będzie więc w tej chwili wyglądać tak:
[code]
<?xml version="1.0" encoding="UTF-8"?>
<database name="shop" defaultIdMethod="native">
  <table name="book" phpName="Book">
    <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
    <column name="title" type="varchar" size="255" required="true" />
    <column name="author_id" type="integer" required="true" />
    <column name="price" type="integer" required="true" />    
  </table>
  <table name="author" phpName="Author">
    <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
    <column name="first_name" type="varchar" size="100" required="true" />
    <column name="last_name" type="varchar" size="100" required="true" />  
  </table>
</database>[/code]
Ważna sprawa: zawsze pamiętajmy w XML-u o zamykaniu znaczników, które nie mają taga zamykającego za pomocą:
[code]/>[/code]

4) Klucze obce

Zdefiniować musimy jeszcze klucze obce dla poszczególnych tabel, dokonujemy tego za pomocą taga:
[code]<foreign-key>[/code]
któremu w atrybutach podajemy:

  • foreignTable - nazwę obcej tabeli
  • phpName - opcjonalnie własną nazwę dla relacji, jeśli nie podana, to Propel przypisze tu nazwę z obcej tabeli
  • refPhpName - opcjonalnie własną nazwę dla relacji, ale widzianą przez obcą tabelę
Przykladowo - nasza tabela book zawiera jeden klucz obcy, a jest nim pole author_id. Relacja prowadzi do tabeli author i jej pola id. Aby teraz zdefiniować relację z tabelą author, wpiszemy dla tabeli book taką definicję:
[code]
<foreign-key foreignTable="author" phpName="Author" refPhpName="Book">
     <!-- tutaj podajemy definicje kolumn -->
</foreign-key>[/code]
Powyższe określa nam relację z obcą tabelą, musimy teraz jeszcze podać jakich kolumn te relacje dotyczą, robimy to za pomocą taga:
[code]<reference>[/code]
jako atrybuty podając mu:

  • local - nazwę pola z obecnej tabeli
  • foreign - nazwę pola z tabeli obcej
Przykladowo - nasza tabela book zawiera jeden klucz obcy, a jest nim pole author_id.
Aby teraz zdefiniować relację z tabelą author i jej polem id, wpiszemy dla tabeli book taką definicję:
[code]
<foreign-key foreignTable="author" phpName="Author" refPhpName="Book">
    <reference local="author_id" foreign="id" />
</foreign-key>[/code]

Oczywiście dla każdej tabeli możemy podać wiele relacji, użyjemy wtedy kilku tagów <reference>
 
Finalnie schemat naszej bazy wygląda tak:
[code]
<?xml version="1.0" encoding="UTF-8"?>
<database name="shop" defaultIdMethod="native">
  <table name="book" phpName="Book">
    <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
    <column name="title" type="varchar" size="255" required="true" />
    <column name="author_id" type="integer" required="true" />
    <column name="price" type="integer" required="true" />
    <foreign-key foreignTable="author" phpName="Author" refPhpName="Book">
        <reference local="author_id" foreign="id" />
    </foreign-key>  
  </table>
  <table name="author" phpName="Author">
    <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
    <column name="first_name" type="varchar" size="100" required="true" />
    <column name="last_name" type="varchar" size="100" required="true" />  
  </table>
</database>[/code]
Zapiszmy więc taki powyższy schemat do pliku:
[code]schema.xml[/code]
Mamy w tym momencie gotowy schemat naszej bazy danych, pora teraz na utworzenie dla niej modelu.

Konfiguracja

Musimy teraz przygotować model dla naszej bazy, czyli wygenerować wszystkie niezbędne klasy, które będą ją opisywały w postaci obiektów, a także wszelkie niezbędne metody, jak gettery i settery, które pozwolą nam na pobieranie i ustawianie wartości pól. Propel zrobi to za nas automatycznie, nie będziemy więc musieli tworzyć klas ręcznie. Na podstawie definicji pobranej ze stworzonego schematu sam wygeneruje odpowiednie klasy i metody. Musimy jednak dokonać małej konfiguracji, podać parametry połączenia itp.

Plik z konfiguracją dla Propela powinien mieć nazwę propel oraz rozszerzenie odpowiednie dla języka, w którym został napisany, czyli:
[code]propel.php, propel.xml, propel.json lub propel.yaml[/code]
i powinien zostać umieszczony w tym samym katalogu co plik ze schematem, czyli schema.xml.

Może zostać również umieszczony w podfolderze /config lub /conf. My na razie umieśćmy go w naszym katalogu głównym, obok pliku schema.xml. Plik z konfiguracją, jak już wspomniałem wyżej przygotować możemy w jednym z 4 języków:

  • PHP
  • XML
  • JSON
  • YAML

Wybór jest dowolny, jednak najczęściej używanym formatem jest YAML, ze względu na swoją prościutką i miłą dla oka składnię. Konfiguracja jest dość rozbudowana i opisana w pełni w dokumentacji Propela, jednakże nie musimy na razie w nią wnikać, gdyż domyślnie Propel wymaga podania jedynie kluczowych informacji, dotyczących nazwy bazy i parametrów naszego połączenia z bazą. Jak pamiętamy, naszą bazę nazwaliśmy w schemacie:
[code]shop[/code]
Stwórzmy więc teraz plik z konfiguracją dla bazy shop.
W języku YAML będzie miał on taką postać:
[code]
# propel.yaml
propel:
  database:
      connections:
          shop:
              adapter: mysql
              classname: Propel\Runtime\Connection\ConnectionWrapper
              dsn: "mysql:host=localhost;dbname=nazwa_bazy"
              user: nazwa_użytkownika_bazy
              password: hasło_użytkownika_bazy
              attributes:
  runtime:
      defaultConnection: shop
      connections:
          - shop
  generator:
      defaultConnection: shop
      connections:
          - shop[/code]
gdzie oczywiście w pola:

  • host - podajemy adres serwera bazy
  • mysql - tutaj podajemy nazwę adaptera, w przykładzie podałem MySQL
  • nazwa_bazy - nazwa naszej bazy
  • nazwa_użytkownika_bazy - nazwa użytkownika
  • hasło_użytkownika_bazy - i jego hasło

Plik taki zapisujemy jako:
[code]propel.yaml[/code]
Więcej o konfiguracji przeczytacie tutaj: http://propelorm.org/documentation/10-configuration.html

Generowanie SQL

Mamy już dwa pliki:

  • schema.xml - czyli schemat bazy
  • propel.yaml - konfigurację bazy

Musimy więc teraz wygenerować zapytanie SQL, które zbuduje nam tabele w bazie na podstawie podanego przez nas schematu. Do wygenerowania takiego zapytania użyjemy oferowanego z Propelem narzędzia:
[code]/vendor/bin/propel[/code]
Wejdźmy teraz w terminalu do katalogu z naszym projektem:
[code]$ cd c:/www/propel[/code]
I wykonajmy następujące polecenie:
[code]$ propel sql:build[/code]
lub
[code]$ "/vendor/bin/propel" sql:build[/code]
jeśli nie dodaliśmy katalogu /vendor/bin do zmiennej PATH w Windowsie.


Narzędzie to utworzyło nam katalog:
[code]/generated_sql[/code]
A w nim 2 pliki:

  • shop.sql
  • sqldb.map

Spójrzmy teraz na zawartość pliku shop.sql:
[code]
# This is a fix for InnoDB in MySQL >= 4.1.x
# It "suspends judgement" for fkey relationships until are tables are set.
SET FOREIGN_KEY_CHECKS = 0;

-- ---------------------------------------------------------------------
-- book
-- ---------------------------------------------------------------------

DROP TABLE IF EXISTS `book`;

CREATE TABLE `book`
(
    `id` INTEGER NOT NULL AUTO_INCREMENT,
    `title` VARCHAR(255) NOT NULL,
    `author_id` INTEGER NOT NULL,
    `price` INTEGER NOT NULL,
    PRIMARY KEY (`id`),
    INDEX `book_fi_ea464c` (`author_id`),
    CONSTRAINT `book_fk_ea464c`
        FOREIGN KEY (`author_id`)
        REFERENCES `author` (`id`)
) ENGINE=InnoDB;

-- ---------------------------------------------------------------------
-- author
-- ---------------------------------------------------------------------

DROP TABLE IF EXISTS `author`;

CREATE TABLE `author`
(
    `id` INTEGER NOT NULL AUTO_INCREMENT,
    `first_name` VARCHAR(100) NOT NULL,
    `last_name` VARCHAR(100) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB;

# This restores the fkey checks, after having unset them earlier
SET FOREIGN_KEY_CHECKS = 1;[/code]
Jak widzimy wygenerowaliśmy pełne zapytanie SQL służace do utworzenia tabel w bazie.

Uwaga: każde ponowne użycie polecenia:
[code]$ propel sql:build[/code]
tworzy te zapytanie na nowo, więc musimy je wywołać przy każdej modyfikacji schematu bazy.
Pora teraz na wygenerowanie klas dla modelu.

Generowanie klas modelu

Do wygenerowania wszystkich niezbędnych klas dla naszego modelu ponownie użyjemy polecenia propel. Wpiszmy teraz w terminalu:
[code]$ propel model:build[/code]


Wygenerowało to nam katalog:
[code]/generated-classes[/code]
A w nim wszystkie wymagane klasy niezbędne do pracy z naszymi tabelami:



Propel utworzył nam 3 rodzaje klas:

  • klasę modelu, np. Book, która reprezentuje wiersz z tabeli jako obiekt
  • klasę zapytań, np. BookQuery, która zawiera definicje metod slużacych do operacji na wierszach/obiektach
  • klasę mapowania, np. Map/BookTableMap, która zawiera metody statyczne służace do operowania na wierszu/obiekcie
Uwaga: każde ponowne użycie polecenia:
[code]$ propel model:build[/code]
tworzy te klasy na nowo, więc musimy je wywołać przy każdej modyfikacji schematu bazy. Nie należy też w tych klasach umieszczać swojego kodu, gdyż ten przepadnie po kolejnym generowaniu modelu.

Mamy już gotowe klasy modelu, musimy je teraz dołączyć do autoloadera, tak aby były dostępne w naszym kodzie. Zmodyfikujmy więc plik composer.json znajdujący się w katalogu głównym, tak aby wyglądał następująco:
[code]
{
    "require": {
        "propel/propel": "~2.0@dev"
    },  
    "autoload": {
    "classmap": ["generated-classes/"]
    }
}[/code]
Następnie zaktualizujmy Composera:
[code]$ composer update[/code]


Sprawi to, że przebudujemy autoloader Composera, co za tym idzie, nasze klasy modelu zawarte w katalogu:
[code]/generated-classes[/code]
będą ładowane automatyczne jeśli skorzystamy z autoloadera wygenerowanego przez Composera.

Plik konfiguracyjny

Musimy teraz wygenerować plik z domyślną konfiguracją dla połączenia z bazą danych.
Poprzednio podaliśmy konfigurację w języku YAML, użyjemy teraz polecenia Propela, które na podstawie tego pliku przygotuje nam plik PHP z konfiguracją, który dołączymy do swojego projektu:

[code]$ propel config:convert[/code]


Polecenie utworzyło nam katalog:
[code]/generated-conf[/code]
a w nim plik:
[code]config.php[/code]
o treści:
[code]
<?php
$serviceContainer = \Propel\Runtime\Propel::getServiceContainer();
$serviceContainer->checkVersion('2.0.0-dev');
$serviceContainer->setAdapterClass('shop', 'mysql');
$manager = new \Propel\Runtime\Connection\ConnectionManagerSingle();
$manager->setConfiguration(array (
  'classname' => 'Propel\\Runtime\\Connection\\ConnectionWrapper',
  'dsn' => 'mysql:host=localhost;dbname=NAZWA_BAZY',
  'user' => 'UŻYTKOWNIK',
  'password' => 'HASŁO',
  'attributes' =>
  array (
    'ATTR_EMULATE_PREPARES' => false,
  ),
));
$manager->setName('shop');
$serviceContainer->setConnectionManager('shop', $manager);
$serviceContainer->setDefaultDatasource('shop');[/code]

Utworzenie tabeli w bazie

Przyszła pora na utworzenie naszych opisanych w schemacie tabel w bazie.
Jak pamiętamy, Propel utworzył nam do tego zapytanie SQL, aby teraz wykonać to zapytanie z poziomu Propela, użyjemy polecenia:

[code]$ propel sql:insert[/code]


Utworzyło to nam odpowiednie tabele w bazie danych, co możemy sprawdzić np. za pomocą PHPMyAdmina.

Testujemy

Pozostało nam teraz sprawdzenie działania Propela w praktyce.
Utwórzmy sobie w głównym folderze naszego projektu plik testowy, np.:
[code]test.php[/code]
w którym przetestujemy Propela.

Na początek musimy dołączyć autoloader oraz plik z konfiguracją dla Propela:
[code]
<?php
// test.php
// dołączamy autoloader Composera
require_once __DIR__.'/vendor/autoload.php';

// dołączamy plik z konfiguracją
require_once __DIR__.'/generated-conf/config.php';
?>[/code]

Dodajemy nowy rekord obiekt

Napiszmy teraz prosty kod dodający do bazy nowego autora:
[code]
$author = new Author();
$author->setFirstName('Adam');
$author->setLastName('Mickiewicz');
$author->save();[/code]
Nasz plik test.php wyglądać więc będzie w całości tak:
[code]
<?php
// test.php
// dołączamy autoloader Composera
require_once __DIR__.'/vendor/autoload.php';

// dołączamy plik z konfiguracją
require_once __DIR__.'/generated-conf/config.php';

// tworzymy nowego autora
$author = new Author();

// ustawiamy imię i nazwisko
$author->setFirstName('Adam');
$author->setLastName('Mickiewicz');

// zapisujemy
if($author->save())
{
    echo 'Dodano autora o id: '.$author->getId();
}
?>[/code]

Wejdźmy teraz na:
[code]http://localhost/propel/test.php[/code]
i zobaczmy, czy wszystko działa:


Voila! Dodaliśmy nowego autora do bazy za pomocą Propela.


Podsumowanie

Jak widzimy, utworzenie nowego rekordu sprowadza się teraz do prostego utworzenia obiektu i wywołania metody save(). I to tyle tytułem wstępu, w kolejnych artykułach nauczymy się korzystać z Propela, pobierać i aktualizować rekordy, obsługiwać relacje itd. Na chwilę obecną potrafimy już przygotować schemat bazy, zbudować prosty model oraz wiemy jak to wszystko mniej więcej działa, więc na wstęp i wprowadzenie powinno nam wystarczyć.

1 komentarz:

Masz sugestię? Znalazłeś błąd? Napisz komentarz! :)

webmaester.pl - profesjonalne projektowanie WWW i webaplikacji
webmaester.pl - profesjonalne projektowanie WWW i webaplikacji