Open Clip Art to kolekcja kilku tysięcy wektorowych rysunków wykonanych w programie Inkscape. Całość, choć podzielona na kategorie i otagowana, jest niewygodna w użyciu. Artykuł opisuje, w jaki sposób wykonać katalog, który ułatwi poruszanie się po kliparcie. Jest to doskonałe ćwiczenie w przygotowywaniu witryn internetowych w PHP/MySQL korzystając z wzorca architektonicznego MVC.
Open Clip Art jest dostępny do pobrania w formie spakowanego archiwum openclipart-0.18-full.zip. Każdy rysunek zawarty w kliparcie jest dostępny w dwóch formatach: SVG oraz PNG. Dodatkowy plik tekstowy zawiera informacje o autorze rysunku oraz słowa kluczowe. Nazwy trzech plików .svg, .png i .txt pojedynczego rysunku są identyczne.
Na przykład trzy pliki zawarte w folderze animals\birds:
dotyczą tego samego rysunku orła. Pierwszy z nich, eagle_01.svg, to rysunek wektorowy, który możemy otworzyć w Inkscape, co zostało przedstawione na rysunku 1. Drugi, eagle_01.png, to miniaturka o wymiarach 80x113, którą możemy otworzyć w przeglądarce WWW (patrz rysunek 2). Trzeci plik, eagle_01.txt zawiera następujące informacje:
...brak dostępu...
Na podstawie słów kluczowych zawartych w pliku tekstowym rysunki katalogu możemy poklasyfikować.
Klipart w wersji openclipart-0.18-full.zip zawiera drobne błędy. W kilku przypadkach brakuje plików tekstowych oraz plików .png. Odczyt plików .txt oraz .png należy poprzedzić sprawdzaniem, czy plik o podanej nazwie istnieje.
Wykonanie katalogu rozpoczynamy od ustalenia funkcjonalności. Zasadniczym zadaniem jest otagowanie wszystkich rysunków i umożliwienie łatwego wybierania wszystkich obrazów związanych z wybranym tagiem. Wyświetlanie tagów zawartych w katalogu realizujemy na trzy sposoby:
Ponieważ Open Clip Art zawiera kilka tysięcy różnych słów kluczowych, więc chmurę z rysunku 3 ograniczamy do tagów, które dotyczą ponad dwudziestu rysunków. W przeciwnym razie chmura ta będzie ogromna i przez to nieczytelna.
Tagi występujące na rysunkach 3, 4 oraz 5 są oczywiście odsyłaczami. Umożliwiają one przejście do strony prezentującej wszystkie rysunki związane z wybranym tagiem. Rysunki te przedstawiamy w postaci regularnej tabelki miniatur widocznej na rysunkach 6 oraz 7 (nazwa tagu jest zawarta w tytule strony). Ponieważ jednak niektóre tagi są powiązane z ogromną ilością obrazów (np. słowo kluczowe computer dotyczy 2184 rysunków!), zatem tabele miniatur musimy poddać stronicowaniu. Rysunki 6 oraz 7 przedstawiają pierwszą i czwartą stronę miniatur dla tagu icon. Poruszanie po stronach ułatwiają numeracja oraz strzałki zaznaczone na rysunku 7.
Po wybraniu dowolnej miniatury przechodzimy do przedstawionej na rysunku 8 strony ze szczegółowymi danymi. Zawiera ona wszystkie tagi rysunku oraz oryginalną miniaturę .png, po kliknięciu której ujrzymy plik SVG (rysunek 9).
Baza danych katalogu będzie zawierała dwie tabele: img oraz tag. Pierwsza z nich jest przeznaczona na obrazy, a druga na słowa kluczowe. Po połączeniu tabel img oraz tag relacją wiele do wielu w bazie danych pojawi się trzecia tabela o nazwie img_has_tag.
Po przygotowaniu modelu widocznego na rysunku 10, generujemy klasy dostępu do bazy danych. Wykorzystujemy do tego skrypt dbframe, który ułatwia połączenie aplikacji DBDesigner i Propel.
Zliczanie liczby obrazów związanych z wybranym słowem kluczowym wykonujemy wykorzystując wyzwalacze (ang. triggers). Po wstawieniu rekordu do tabeli img_has_tag, czyli w obsłudze zdarzenia AFTER INSERT, zwiększamy liczbę obrazów związanych z tagiem. Kompletny kod wyzwalacza jest przedstawiony na listingu 1. Dzięki wyzwalaczowi, to serwer bazy danych odpowiada za aktualizację pola imgs_count w tabeli tag.
...brak dostępu...
Listing 1. Wyzwalacz zliczający liczbę obrazów związanych ze słowem kluczowym
Wypełnianie bazy danych rozpoczynamy od ustalenia nazw wszystkich plików w formacie .svg. Skrypt get-file-list.php rekurencyjne przeszukuje folder z klipartem openclipart-0.18-full/clipart/ i ustala nazwy wszystkich plików o rozszerzeniu SVG. Pełna lista znalezionych plików jest zapisana do pliku files.txt.
Następnie otrzymana lista plików jest w skrypcie wstaw.php przetwarzana element po elemencie. Dla każdego elementu:
Do tabeli img wstawiamy obraz z odpowiednio ustalonymi ścieżkami, zaś do tabeli tag wstawiamy wszystkie słowa kluczowe obrazu. Wstawione słowa łączymy relacją n:m ze wstawionym obrazem.
Zrzut wypełnionej bazy danych, zawierający 42 227 rekordów, jest zapisany w pliku 1-dbframe/input/clipart.sql. Wypełnioną bazę danych możesz odtworzyć wykonując skrypt create-db-filled.bat.
Ostatnim etapem przygotowywania danych do katalogu jest przeskalowanie miniatur. Oryginalne miniatury mają maksymalną szerokość 80 pikseli. Wysokość miniatur jest dobrana proporcjonalnie do szerokości. W przypadku niektórych obrazów wysokość może wynieść nawet kilkaset pikseli. Przygotowanie regularnej tabeli miniatur z tak różnorodnych obrazów byłoby trudne. Dlatego łatwiej najpierw przygotować takie miniaturki, z których każda mieści się w kwadracie o wymiarach 80x80 pikseli.
Tworząc miniatury możemy już wykorzystać zawartość bazy danych. Pobieramy z bazy danych wszystkie obrazy (wywołanie ImgPeer::doSelect()), po czym w pętli dla każdego obrazu sprawdzamy istnienie oryginalnej miniaturki w formacie .png. Jeśli miniaturka istnieje, to w zmiennej $nowa_nazwa ustalamy nazwę dla nowego pliku .png, plik skalujemy (wywołując funkcję skaluj_obraz_xy()). Na koniec przeskalowany obraz zapisujemy do pliku (wywołanie imagepng()). Kompletny skrypt tworz-miniaturki.php jest przedstawiony na listingu 2.
...brak dostępu...
Listing 2. Skrypt generujący miniaturki mieszczące się w kwadracie o wymiarach 80x80 pikseli
Wymiary generowanych miniatur są ustalone w stałych SZEROKOSC oraz WYSOKOSC. Po zmianie podanych wartości należy odpowiednio dostosować aplikację podając liczbę obrazów na stronie. Służy do tego stała ILE_NA_STRONIE w skrypcie index.php. Ponadto w stylach CSS należy podać nową szerokość elementu div przeznaczonego na miniaturkę:
...brak dostępu...
Dodatkowym parametrem aplikacji jest liczba kolumn na stronach z rysunków 4 oraz 5. Jest ona ustalana stałą LICZBA_KOLUMN. Po zmienieniu wartości stałej należy jeszcze zmodyfikować szerokośc odpowiedniego pojemnika w CSS:
...brak dostępu...
Po przygotowaniu kompletu danych przystępujemy do oprogramowania aplikacji. Stosujemy wzorzec architektoniczny MVC (ang. Model View Kontroller), w którym:
Analizując katalog od strony funkcjonalnej (rysunki 3, 4, 5, 6, 8), wyznaczamy następujące różne podstrony serwisu:
Podstronom tym przyporządkowujemy numery akcji oraz adresy URL zgodnie z zawartością tabeli 1.
| Podstrona | Akcja | Adres URL | Przykład |
|---|---|---|---|
| chmura tagów | 2 | index.html | - |
| lista alfabetyczna tagów | 3 | lista-a.html | - |
| lista ilościowa tagów | 4 | lista-i.html | - |
| pojedyncza strona z tabelą miniatur | 5 | tag/<nazwa tagu>/p<numer strony>.html | tag/animal/p4.html |
| szczegółowe dane obrazu | 6 | img/<numer obrazu>.html | img/1234.html |
Tabela 1. Podstrony, ich akcje i adresy URL
Baza danych zawiera trzy tabele: img, tag oraz img_has_tag o polach:
...brak dostępu...
Propel wygeneruje więc sześć klas: Img, ImgPeer, Tag, TagPeer, ImgHasTag, ImgHasTagPeer. Klasy Img, Tag, ImgHasTag będą miały metody setX() oraz getX(), zapewniające dostęp do wszystkich pól (np. getImgId(), getPngUrl(), setImgId(), setPngUrl()) zaś klasy Peer pozwolą na wyszukiwanie rekordów. Na przykład wyszukanie wszystkich tagów wykonamy wywołując:
...brak dostępu...
zaś podczas skalowania, przedstawionego na listingu 2, wykorzystaliśmy klasę ImgPeer:
...brak dostępu...
Kontroler aplikacji jest podzielony na następujące cztery fazy:
Stosujemy przyjazne URL implementowane przy użyciu modułu mod_rewrite. Dyrektywa:
...brak dostępu...
zawarta w pliku .htaccess przekierowuje wszystkie żądania dotyczące plików HTML do skryptu index.php. W pierwszej fazie kontrolera wykorzystując $_SERVER['REQUEST_URI'] w zmiennej $adr ustalamy nazwę pliku, którego dotyczy żądanie.
Następnie, w etapie drugim, przeprowadzamy walidację danych. Tworzymy zmienną $akcja, której wartości będą zgodne z zawartością tabeli 1. Dodatkowa wartość $akcja == 1 służy do obsługi błędów 404. Jest to wartość domyślna.
Po przeprowadzeniu walidacji przechodzimy do etapu trzeciego: wypełnienia szablonu danymi. Instrukcja switch wybiera odpowiedni przypadek na podstawie wartości $akcja. W konkretnym przypadku pobieramy odpowiednie dane z bazy i przekazujemy — jako obiekty Propel-a — do szablonu.
Ostatni etap pracy kontrolera to wyświetlenie szablonu. Zadanie to sprowadza się do wywołania metody display().
Zarys kontrolera jest przedstawiony na listingu 3.
...brak dostępu...
Listing 3. Zarys kontrolera aplikacji
Chmura tagów widoczna na rysunku 3 jest wykonana z wykorzystaniem biblioteki PEAR. Klasa HTML_TagCloud automatyzuje proces tworzenia chmury.
Najpierw pobieramy z bazy danych wszystkie tagi, które dotyczą co najmniej 7 obrazów:
...brak dostępu...
Następnie tworzymy obiekt klasy MyTagsCustomCssTagCloud:
...brak dostępu...
Elementy chmury dodajemy do obiektu $tags_cloud metodą addElement():
...brak dostępu...
Parametrami metody addElement() są:
Gotowa chmura, zwracana przez metody buildHTML() i oraz buildCSS() jest przekazywana do szablonu:
...brak dostępu...
Widok aplikacji składa się z szablonów Smarty. Układ witryny jest zawarty w pliku index.tpl przedstawionym w zarysie na listingu 4. W zależności od wartości zmiennej $akcja, wewnątrz kontenera div#wrapper dołączamy inny plik .tpl.
...brak dostępu...
Listing 4. Zarys widoku aplikacji
Jeśli katalog ma być udostępniany — podobnie jak oryginał openclipart-0.18-full.zip — w postaci spakowanego archiwum plików, to hiperłącza muszą stosować adresy względne, np.:
...brak dostępu...
Ponieważ niektóre z przyjaznych URL-i zawierają znak /:
...brak dostępu...
zatem do tego samego pliku o adresie tag/animals/p34.html będziemy stosowali hiperłącza:
...brak dostępu...
Zmienna $folder widoczna na listingu 4 zawiera prefiks, który należy dodać do hiperłączy. Czasami przedrostek ten jest pusty (np. na stronie index.html), czasami zawiera pojedyncze wyjście w górę ../ (np. na stronie img/1234.html), a czasami — podwójne ../../ (np. na stronie tag/animal/p34.html).
W celu uruchomienia skryptu index.php należy:
| lp. | Przykład |
|---|---|
| 1. | Katalog Open Clip Art — skrypty PHP |
| 2. | Katalog Open Clip Art — wersja offline |
Tabela 2. Przykłady do pobrania
| lp. | Pobierz |
|---|---|
| 1. | dbframe — skrypty ułatwiające pracę z aplikacjami DBDesigner/Propel |
| 2. | Pakiet PEAR::HTML_TagCloud |
| 3. | Pakiet PEAR::Image_Color |
Tabela 3. Pliki do pobrania
| lp. | Adres |
|---|---|
| 1. | Open Clip Art |
| 2. | openclipart-0.18-full.zip — pobieranie z internetu |
| 3. | dbframe. Automatyzacja pracy z aplikacjami Propel i DBDesigner |
| 4. | Model View Controller — hasło w Wikipedii |
Tabela 4. Adresy