Symfony to jeden z najlepszych dostępnych obecnie frameworków w języku PHP. Dzięki jasnej strukturze oraz generatorom kodu przygotowanie kompletnej aplikacji WWW zajmuje kilku minut. Artykuł opisuje krok po kroku przebieg wykonania internetowego katalogu aparatów fotograficznych.
Cel ćwiczenia
Celem opisywanego zadania jest wykonanie aplikacji internetowej, która służy do gromadzenia danych o aparatach fotograficznych:
- nazwa modelu (np. EOS 400D),
- nazwa producenta (np. Nikon),
- rodzaj matrycy (np. CMOS),
- cena (np. 3299 zł).
Wykonana witryna ma składać się z dwóch niezależnych części. Pierwsza, nazywana frontend, będzie służyła do przeglądania bazy danych bez możliwości wprowadzania zmian. Dostęp do frontendu nie wymaga zalogowania. Każda tabela bazy danych będzie zawierała dwa rodzaje podstron: listę wszystkich rekordów tabeli oraz szczegółowe dane jednego wybranego rekordu.
Druga część witryny, tzw. backend, będzie panelem administracyjnym, służącym do edycji zawartości bazy danych. Dostęp do backendu zabezpieczymy formularzem autoryzacyjnym. Edycja rekordów bazy danych będzie możliwa tylko po zalogowaniu. Aplikacja będzie zawierała jedno konto administracyjne o nazwie admin i haśle kartofel.
Krok pierwszy: utworzenie bazy danych
Pracę nad aplikacją rozpoczynamy od utworzenia bazy danych oraz konta dostępu. Zadanie to realizują dwa skrypty:
tworzenie-bazy-aparaty.sql
tworzenie-bazy-aparaty.bat
W pliku tworzenie-bazy-aparaty.bat należy w miejsce AX1BY2CZ3 wprowadzić hasło administratora serwera MySQL na komputerze localhost.
Po wykonaniu skryptu tworzenie-bazy-aparaty.bat na lokalnym serwerze MySQL zostanie utworzona baza danych o nazwie aparaty. Baza ta będzie wypełniona przykładowymi rekordami. Dodatkowo na serwerze MySQL zostanie utworzone konto o nazwie fotograf i haśle pstryk. Będzie ono miało wszelkie uprawnienia do bazy danych aparaty. Konto fotograf jest tworzone następującym zapytaniem SQL:
GRANT
ALL ON aparaty.*
TO fotograf@localhost
IDENTIFIED BY \'pstryk\';
Dostęp do bazy danych zapewnia następujący DSN (ang. Data Source Name):
mysql://fotograf:pstryk@localhost/aparaty
Po wykonaniu skryptu tworzenie-bazy-aparaty.bat sprawdzamy, czy baza danych aparaty została zainstalowana. Powinna zawierać 3 tabele i 33 rekordy, o czym informuje phpMyAdmin.
Krok drugi: pobranie i rozpakowanie pliku sf_sandbox.tgz
Sandbox to spakowane archiwum, przygotowane z myślą o początkujących użytkownikach frameworka Symfony. Zawiera kompletny kod frameworka i pusty projekt o nazwie sf_sandbox. Korzystanie z sandboxa wymaga instalacji Apache z mod_rewrite oraz PHP.
Korzystanie z pakietu Sandbox jest bardzo proste, gdyż sprowadza się do dwóch kroków: pobrania oraz wypakowania pliku {stala}sf_sanbox.tgz{/stala}.
Ze strony projektu Symfony http://www.symfony-project.org kopiujemy plik sf_sandbox.tgz (znajduje się w dziale Installation).
Wypakowujemy go w publicznym folderze serwera Apache (np. htdocs/). Następnie przeglądarką internetową odwiedzamy stronę:
localhost/sf_sandbox/web
Ujrzymy domyślną stronę projektu Sandbox.
Dostosowywanie panelu administracyjnego
Moduły CRUD generowane poleceniem:
symfony propel-generate-crud
dostosowywaliśmy do własnych potrzeb, modyfikując kod PHP (usunęliśmy kilka metod z pliku actions.class.php) oraz szablony (zmieniliśmy pliki {stala}listSuccess.php{/stala} oraz {stala}showSuccess.php{/stala}).
W przypadku modułów administracyjnych tworzonych poleceniem:
symfony propel-init-admin
postępujemy nieco inaczej. Wygląd i zachowanie modułów definiujemy w plikach konfiguracyjnych .yml. Moduł wygenerowany dla każdej tabeli ma własny plik konfiguracyjny. Pliki konfiguracyjne modułów aparat, matryca i producent aplikacji backend znajdują się w folderach:
sf_sandbox/apps/backend/modules/aparat/config/generator.yml
sf_sandbox/apps/backend/modules/matryca/config/generator.yml
sf_sandbox/apps/backend/modules/producent/config/generator.yml
W pliku konfiguracyjnym modułu aparat wprowadzamy zawartość:
generator:
class: sfPropelAdminGenerator
param:
model_class: Aparat
list:
title: Lista aparatóww
display: [ =model, producent, matryca, cena ]
max_per_page: 10
Wpis title to etykieta umieszczana powyżej tabelki, max_per_page ustala liczbę rekordów na stronie, zaś display definiuje kolumny zawarte w tabeli z zestawieniem rekordów. Symbol = przed nazwą kolumny model powoduje, że nazwa modelu aparatu będzie hiperłączem do strony ze szczegółowymi danymi aparatu.
W pliku konfiguracyjnym tabeli Matryca umieszczamy wpisy:
generator:
class: sfPropelAdminGenerator
param:
model_class: Matryca
list:
title: Lista matryc
display: [ =nazwa ]
max_per_page: 4
zaś panel administracyjny tabeli Producent konfigurujemy:
generator:
class: sfPropelAdminGenerator
param:
model_class: Producent
list:
title: Lista producentów
display: [ =nazwa ]
max_per_page: 6
Ponownie uruchamiamy generatory:
symfony propel-init-admin backend aparat Aparat
symfony propel-init-admin backend matryca Matryca
symfony propel-init-admin backend producent Producent
czyścimy pamięć podręczną:
symfony cc
i odwiedzamy przeglądarką panel administracyjny:
http://localhost/sf_sandbox/web/backend.php/aparat
Autoryzacja
Oczywiście dostęp do panelu administracyjnego należy zabezpieczyć.
Najpierw dodajemy pliki konfiguracyjne modułów aparat, matryca oraz producent:
sf_sandbox/apps/backend/modules/aparat/config/security.yml
sf_sandbox/apps/backend/modules/matryca/config/security.yml
sf_sandbox/apps/backend/modules/producent/config/security.yml
W każdym z trzech powyższych plików umieszczamy identyczny wpis:
default:
is_secure: on
Spowoduje to całkowite zablokowanie dostępu do panelu administracyjnego. Jeśli wyczyścimy pamięć podręczną i odwiedzimy panel administracyjny, to ujrzymy komunikat: dostęp wymaga zalogowania. Ponieważ jednak aplikacja backend nie zawiera jeszcze formularza do logowania, zatem cały panel administracyjny będzie niedostępny.
Formularz do logowania
Dodajemy nowy moduł o nazwie security aplikacji backend:
symfony init-module backend security
Następnie przygotowujemy szablon akcji index. W folderze:
sf_sandbox/apps/backend/modules/security/templates/
tworzymy plik o nazwie {stala}indexSuccess.php{/stala}. Wprowadzamy w nim następujący kod:
Autoryzacja
hasErrors()): ?>
Błędna próba logowania! Spróbuj ponownie!
get(\'login\')) ?>
Zwróćmy uwagę na wywołanie funkcji {stala}form_tag(){/stala}. Jej parametrem jest akcja odpowiedzialna za przetwarzanie formularza. Jest to akcja o nazwie login modułu security, co zapisujemy:
security/login
W pliku {stala}sf_sandbox/apps/backend/modules/security/actions/actions.class.php{/stala} dodajemy metody obsługujące akcje login oraz logout. Akcja login zawiera zaszyte na stałe w kodzie dane konta administracyjnego: login == admin oraz password == kartofel:
public function executeLogin()
{
if (
$this->getRequestParameter(\'login\') == \'admin\' &&
$this->getRequestParameter(\'password\') == \'kartofel\'
) {
$this->getUser()->setAuthenticated(true);
return $this->redirect(\'aparat\');
} else {
$this->getRequest()->setError(\'login\', \'incorrect entry\');
return $this->forward(\'security\', \'index\');
}
}
public function executeLogout()
{
$this->getUser()->setAuthenticated(false);
return $this->redirect(\'security/index\');
}
Na zakończenie dodajemy w układzie aplikacji backend (tj. w pliku {stala}sf_sandbox/apps/backend/templates/layout.php{/stala}) instrukcję if wraz z hiperłączem do wylogowania:
isAuthenticated()): ?>
oraz ustalamy stronę główną aplikacji backend (plik {stala}sf_sandbox/apps/backend/config/routing.yml{/stala}):
homepage:
url: /
param: { module: security , action: index }
Uwagi
Opisane ćwiczenie zostało przetestowane na platformie Windows XP/Apache 2.2/PHP 5.2/MySQL 5.0. Wykonanie aplikacji trwa krócej niż jednostka zajęć uniwersyteckich z programowania (tj. 2 x 45 minut).
Jest to nieco inny model pracy od tradycyjnego programowania. Zamiast pisać kod – generujemy go, a otrzymany szkielet dostosowujemy do własnych potrzeb. Stosując trzy generatory:
- propel-build-model do generowania aktywnych rekordów,
- propel-generate-crud do generowania szkieletu aplikacji frontend,
- propel-init-admin do generowania panelu administracyjnego.
możemy otrzymać działający szkielet aplikacji dedykowanej potężnej bazie danych w kilka minut. Takiej wydajności nigdy nie uda się osiągnąć pisząc własny kod.
Wykonując ćwiczenie pamiętajmy, że po wprowadzeniu modyfikacji w plikach .yml musimy wyczyścić pamięć podręczną (polecenie symfony cc). Pamiętajmy też, że Sandbox nie będzie działał, jeśli którykolwiek z folderów zawiera dziwne znaki, np.:
htdocs\mój projekt ver. 0.1.2\sf_sandbox\...
Znakami, które nie powodują kłopotów są litery, cyfry podkreślenia i myślniki.
Tytuły podstron oraz kodowanie znaków można zmienić w plikach konfiguracyjnych view.yml. Plik {stala}view.yml{/stala} możemy umieszczać w folderze konfiguracyjnym aplikacji (np. {stala}sf_sandbox/apps/frontend/config/view.yml{/stala}) jak i modułu (np. {stala}sf_sandbox/apps/frontend/modules/aparat/config/view.yml{/stala}) Pliki .yml mogą stosować kodowanie utf-8.
W przypadku błędów korzystajmy ze środowisk przeznaczonych do debuggowania aplikacji. Mają one adresy:
http://localhost/sf_sandbox/web/frontend_dev.php/
http://localhost/sf_sandbox/web/backend_dev.php/
Plik {stala}symfony-cwiczenie.txt{/stala}, który zawiera listę kolejnych kroków wraz z nazwami modyfikowanych plików, ułatwi powtórne wykonanie ćwiczenia.
Krok piąty: aplikacja backend
Rolę panelu administracyjnego będzie pełniła aplikacja o nazwie backend. Pierwszym krokiem jest utworzenie nowej pustej aplikacji o nazwie backend. W tym celu wydajemy polecenie:
symfony init-app backend
Następnie generujemy trzy moduły służące do edycji zawartości tabel aparat, matryca oraz producent:
symfony propel-init-admin backend aparat Aparat
symfony propel-init-admin backend matryca Matryca
symfony propel-init-admin backend producent Producent
Moduły panelu administracyjnego są dostępne za pośrednictwem URL-i:
http://localhost/sf_sandbox/web/backend.php/aparat
http://localhost/sf_sandbox/web/backend.php/matryca
http://localhost/sf_sandbox/web/backend.php/producent
Globalna nawigacja
Układ aplikacji backend jest zapisany w pliku:
sf_sandbox/apps/backend/templates/layout.php
Dodajemy w nim menu główne:
Ponieważ menu to jest identyczne, jak w aplikacji frontend, możemy skopiować cały plik {stala}sf_sandbox/apps/frontend/templates/layout.php{/stala} i wkleić do folderu {stala}sf_sandbox/apps/backend/templates/{/stala}.
Metody __toString()
Rozszerzamy klasy Aparat, Matryca oraz Producent zawarte w folderze {stala}sf_sandbox/lib/model/{/stala}. W każdej z nich definiujemy metodę {stala}__toString(){/stala}. W klasie Aparat metoda {stala}__toString(){/stala} może zwracać model aparatu:
class Aparat extends BaseAparat
{
function __toString()
{
return $this->getModel();
}
}
zaś w klasach Matryca oraz Producent – nazwę:
class Matryce extends BaseMatryca
{
function __toString()
{
return $this->getNazwa();
}
}
class Producent extends BaseProducent
{
function __toString()
{
return $this->getNazwa();
}
}
Po wprowadzeniu zmian ponownie uruchamiamy generator paneli administracyjnych:
symfony propel-init-admin backend aparat Aparat
symfony propel-init-admin backend matryca Matryca
symfony propel-init-admin backend producent Producent
czyścimy pamięć podręczną:
symfony cc
i odwiedzamy panel administracyjny przeglądarką. Edytor szczegółowych danych aparatu będzie zawierał teraz listy rozwijane z nazwami producentów oraz nazwami matryc (poprzednio listy rozwijane zawierały nic niemówiące identyfikatory – klucze pierwotne id).
Krok trzeci: dostęp do bazy danych
Po pobraniu i rozpakowaniu Sandboksa przechodzimy do pracy nad witryną poświęconą aparatom fotograficznym. Pierwszym krokiem jest zdefiniowanie źródła danych oraz struktury bazy. Należy zmodyfikować zawartość trzech plików:
sf_sandbox/config/databases.yml
sf_sandbox/config/propel.ini
sf_sandbox/config/schema.yml
Pliki o rozszerzeniu .yml zawierają informacje konfiguracyjne w języku YAML. Są to pliki tekstowe, w których wcięcia nadają danym strukturę.
W plikach {stala}databases.yml{/stala} oraz {stala}propel.ini{/stala} należy wprowadzić adres źródła danych:
all:
propel:
class: sfPropelDatabase
param:
phptype: mysql
dsn: mysql://fotograf:pstryk@localhost/aparaty
propel.database = mysql
propel.database.createUrl = mysql://fotograf:pstryk@localhost/aparaty
propel.database.url = mysql://fotograf:pstryk@localhost/aparaty
Natomiast w pliku schema.yml definiujemy strukturę bazy danych. Baza danych zawiera trzy tabele o nazwach aparat, matryca oraz producent. Każda z tabel zawiera klucz pierwotny o nazwie id, który będzie podlegał autoinkrementacji. Tabela aparat jest połączona z tabelami matryca oraz producent relacjami 1:n. Klucze obce relacji otrzymują nazwy matryca_id i producent_id. Opis bazy danych przyjmuje postać:
plik sf_sandbox/config/schema.yml
propel:
aparat:
id:
producent_id:
matryca_id:
model: varchar(20)
cena: varchar(20)
matryca:
id:
nazwa: varchar(20)
producent:
id:
nazwa: varchar(20)
Po przygotowaniu plików {stala}databases.yml{/stala}, {stala}propel.ini{/stala} oraz {stala}schema.yml{/stala} wygenerujemy klasy zapewniające dostęp do bazy danych. W tym celu uruchamiamy wiersz poleceń (Start/Uruchom/cmd), a następnie przechodzimy do folderu sf_sandbox/:
C:
cd \
cd C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\sf_sandbox
Klasy dostępu do bazy danych generuje polecenie:
symfony propel-build-model
Po jego wydaniu w folderze sf_sandbox/lib/model/ pojawią się pliki:
Aparat.php
AparatPeer.php
Matryca.php
MatrycaPeer.php
Producent.php
ProducentPeer.php
Zawierają one klasy wygenerowane przez aplikację Propel dla modelu opisanego w pliku {stala}schema.yml{/stala}. Klasy te mają następujące metody zapewniające dostęp do pól:
Aparat
getId()
getModel()
getCena()
getMatryca()
getMatrycaId()
getProducent()
getProducentId()
Matryca
getId()
getNazwa()
Producent
getId()
getNazwa()
Jeśli w skrypcie PHP dostępna jest zmienna klasy {stala}$aparat{/stala}, to korzystając z powyższych metod możemy wydrukować model aparatu:
echo $aparat->getModel();
nazwę producenta:
echo $aparat->getProducent()->getNazwa();
lub matrycy:
echo $aparat->getMatryca()->getNazwa();
Krok czwarty: aplikacja frontend
Witryna poświęcona aparatom fotograficznym będzie składała się z dwóch niezależnych członów. Jeden z nich – frontend – będzie umożliwiał przeglądanie zawartości katalogu bez możliwości wprowadzania zmian. Drugi – backend – będzie panelem administracyjnym, służącym do edycji zawartości bazy danych. Człony te w terminologii Symfony są nazywane aplikacjami i znajdują się w folderze {stala}sf_sandbox/apps/{/stala}.
Sandbox zawiera jedną (pustą) aplikację o nazwie frontend. (Jeśli zajrzymy do folderu {stala}sf_sandbox/apps/{/stala}, to znajdziemy tam folder frontend/.) Dlatego aplikacji tej nie musimy tworzyć. Przystępujemy do wygenerowania modułów dostępu do trzech tabel bazy danych. W tym celu w wierszu poleceń (w folderze sf_sandbox/) wydajemy komendy:
symfony propel-generate-crud frontend aparat Aparat
symfony propel-generate-crud frontend matryca Matryca
symfony propel-generate-crud frontend producent Producent
Po wykonaniu powyższych poleceń odwiedzamy strony:
http://localhost/sf_sandbox/web/aparat
http://localhost/sf_sandbox/web/matryca
http://localhost/sf_sandbox/web/producent
(Powyższe adresy trzeba ręcznie wpisać w polu Adres przeglądarki). Będą one prezentowały zawartość bazy danych. Moduły wygenerowane poleceniem {stala}propel-generate-crud{/stala} należy teraz dostosować do własnych potrzeb, modyfikując kod PHP oraz szablony.
Wygenerowane moduły CRUD pozwalają na tworzenie, edycję, uaktualnianie oraz usuwanie rekordów. Poszczególne operacje mają następujące adresy URL:
/show/id/X
/update/id/X
/delete/id/X
/create/id/X
W adresach tych X jest identyfikatorem rekordu, zaś
http://localhost/sf_sandbox/web/aparat/show/id/5
http://localhost/sf_sandbox/web/producent/delete/id/1
Polecenie {stala}propel-generate-crud{/stala} tworzy moduł CRUD (Create, Retrieve, Update, Delete) dla podanej tabeli. Pierwszym parametrem jest nazwa aplikacji, drugim – nazwa modułu, a trzecim – nazwa klasy dostępu (wygenerowanej przez Propel):
symfony propel-generate-crud
Wygenerowany moduł zostaje zapisany w folderze:
sf_sandbox/apps//modules/
na przykład:
sf_sandbox/apps/frontend/modules/aparat
Globalne menu
Dostosowywanie aplikacji frontend rozpoczynamy od wykonania menu głównego. Skórka aplikacji frontend jest zawarta w pliku:
sf_sandbox/apps/frontend/templates/layout.php
Zmieniamy jego zawartość, dodając następujące menu główne:
getRaw(\'sf_content\') ?>
Hiperłącza w szablonach Symfony umieszczamy wykorzystując funkcję {stala}link_to(){/stala}. Jej pierwszym parametrem jest etykieta hiperłącza, zaś drugim – adres URL. Jeśli w aplikacji wygenerowano moduł o nazwie lorem i akcję o nazwie ipsum, to hiperłącze przyjmie adres:
Jeśli jako adres podajemy tylko nazwę modułu:
to hiperłącze będzie prowadziło do akcji domyślnej modułu.
W przypadku akcji create, edit, update, delete modułów CRUD poprawnymi adresami są:
/?id=X
na przykład:
Style CSS
Zmieniamy style CSS aplikacji frontend. W pliku:
web/css/main.css
usuwamy całą zawartość i wprowadzamy:
#pojemnik {
width: 800px;
margin: 0 auto;
}
#menu {
width: 300px;
float: left;
}
#content {
width: 400px;
float: right;
}
Odwiedzamy przeglądarką WWW dowolny z modułów aplikacji frontend, np. matryca:
http://localhost/sf_sandbox/web/matryca
Ujrzymy witrynę, która będzie prezentowała zawartość trzech tabel bazy danych. Każda z podstron będzie zawierała menu główne, pozwalające na dostęp do wszystkich tabel.
Dostosowanie wyglądu modułu
Przechodzimy do dostosowania wyglądu modułu aparat. Najpierw w pliku {stala}sf_sandbox/apps/frontend/modules/aparat/actions/actions.class.php{/stala} usuwamy metody:
executeEdit()
executeCreate()
executeUpdate()
executeDelete()
Uniemożliwi to edycję rekordów tabeli aparat modułem CRUD. Następnie zmieniamy szablony modułu CRUD dla tabeli aparat. Są one zawarte w folderze: {stala}sf_sandbox/apps/frontend/modules/aparat/templates/{/stala}.
Usuwamy plik {stala}editSuccess.php{/stala}, zaś w pliku {stala}listSuccess.php{/stala} wprowadzamy kod:
Aparaty
- getModel(), \'aparat/show?id=\'.$aparat->getId() ) ?>
Modyfikację wyglądu modułu CRUD tabeli aparat kończymy wprowadzając w pliku {stala}showSuccess.php{/stala} zawartość przedstawioną na listingu 1. W podobny sposób modyfikujemy moduły CRUD tabel producent oraz matryca.
Aparat: getModel() ?>
Producent: | getProducent()->getNazwa(), \'producent/show?id=\' . $aparat->getProducentId() ) ?> |
---|---|
Model: | getModel() ?> |
Matryca: | getMatryca()->getNazwa(), \'matryca/show?id=\' . $aparat->getMatrycaId() ) ?> |
Cena: | getCena() ?> |
Zmiana strony głównej aplikacji
Ostatnią zmianą w aplikacji frontend jest ustalenie domyślnej strony głównej. W pliku {stala}sf_sandbox/apps/frontend/config/routing.yml{/stala} wpisujemy:
homepage:
url: /
param: { module: aparat, action: index }
Spowoduje to, że po odwiedzeniu adresu:
http://localhost/sf_sandbox/web/
ujrzymy listę aparatów, czyli akcję index (wpis action: index) modułu aparat (wpis module: aparat).
Sprawdzamy działanie aplikacji frontend.