Mechanizm routingu mgr Tomasz Xięski, Instytut Informatyki, Uniwersytet Śląski Katowice, 2011



Podobne dokumenty
URL, URI różnice i podobieństwa

Backend Administratora

Baza danych i ORM mgr Tomasz Xięski, Instytut Informatyki, Uniwersytet Śląski Katowice, 2011

app/ - folder zawiera pliki konfiguracyjne dla całej aplikacji src/ - folder zawiera cały kod PHP aplikacji

app/ - folder zawiera pliki konfiguracyjne dla całej aplikacji Można wybrać sposób zapisu konfiguracji: YML, XML, PHP

Laboratorium 7 Blog: dodawanie i edycja wpisów

Podstawy technologii WWW

REFERAT O PRACY DYPLOMOWEJ

Modele danych walidacja widoki zorientowane na model

Laboratorium 6 Tworzenie bloga w Zend Framework

Instrukcja laboratoryjna

Ciekawym rozwiązaniem służącym do obsługi zdarzeń dla kilku przycisków w ramach jednej aktywności może być następujący kod:

ASP.NET MVC. Podstawy. Zaawansowane programowanie internetowe Instrukcja nr 3

Tworzenie szablonów użytkownika

Obiektowy PHP. Czym jest obiekt? Definicja klasy. Składowe klasy pola i metody

Baza danych sql. 1. Wprowadzenie

Kontrola sesji w PHP HTTP jest protokołem bezstanowym (ang. stateless) nie utrzymuje stanu między dwoma transakcjami. Kontrola sesji służy do

Widoki zagnieżdżone, layout. 1. Wprowadzenie Repozytoria danych

PHP 5 język obiektowy

Wyrażenie include(sciezka_do_pliku) pozwala na załadowanie (wnętrza) pliku do skryptu php. Plik ten może zawierać wszystko, co może się znaleźć w

Podręcznik użytkownika Wprowadzający aplikacji Wykaz2

3.4. Opis konfiguracji layoutów.

Skanowanie OCR w aplikacji Kancelaria Komornika. Instrukcja dla użytkownika

Laboratorium 4: Routing

Wprowadzenie do Doctrine ORM

Aplikacje WWW - laboratorium

,Aplikacja Okazje SMS

Funkcje dodatkowe. Wersja 1.2.1

2017/2018 WGGiOS AGH. LibreOffice Base

etrader Pekao Podręcznik użytkownika Strumieniowanie Excel

Elektroniczny Urząd Podawczy

Podstawy technologii WWW

Podręcznik użytkownika Publikujący aplikacji Wykaz2

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

CMS - INFORMACJE. *** Mirosław Kuduk E mail: tel. kom DODATKOWE FUNKCJE - PANEL ADMINISTRATORA

Zawartość. Wstęp. Moduł Rozbiórki. Wstęp Instalacja Konfiguracja Uruchomienie i praca z raportem... 6

media Blitz wydajne sytemy szablonów

Podstawy JavaScript ćwiczenia

Programowanie urządzeń mobilnych. projekt 6 ( )

Ekran główny lista formularzy

Budowa aplikacji ASP.NET współpracującej z bazą dany do obsługi przesyłania wiadomości

Data wydania: Projekt współfinansowany przez Unię Europejską ze środków Europejskiego Funduszu Społecznego

Podstawy technologii WWW

5. Mechanizm szablonów.

Baza danych sql. 1. Wprowadzenie. 2. Repozytaria generyczne

Ćwiczenie: JavaScript Cookies (3x45 minut)

Microsoft.NET: ASP.NET MVC + Entity Framework (Code First)

Instrukcja użytkownika

1.Formatowanie tekstu z użyciem stylów

Projekt Hurtownia, realizacja rejestracji dostaw produktów

Specyfikacja HTTP API. Wersja 1.6

SQL (ang. Structured Query Language)

Nowe funkcje w programie Symfonia Mała Księgowość

Walidacja danych w ASP.NET MVC

Budowa aplikacji ASP.NET z wykorzystaniem wzorca MVC

LK1: Wprowadzenie do MS Access Zakładanie bazy danych i tworzenie interfejsu użytkownika

Dokumentacja użytkownika systemu

UWAGA!!! Przed przystąpieniem do zamknięcia roku proszę zrobić kopie bezpieczeństwa

PRZEWODNIK PO ETRADER ROZDZIAŁ XII. ALERTY SPIS TREŚCI

Oracle PL/SQL. Paweł Rajba.

Funkcje dodatkowe. Wersja 1.2.1

Program szkolenia: Symfony, nowoczesny framework PHP

System imed24 Instrukcja Moduł Analizy i raporty

Część II Wyświetlanie obrazów

Kadry Optivum, Płace Optivum

Instrukcja użytkownika BIP

Sprawdzenie czy połączenie przebiegło poprawnie if (mysqli_connect_errno()) { echo Błąd; Połączenie z bazą danych nie powiodło się.

PWI Instrukcja użytkownika

Temat: Ułatwienia wynikające z zastosowania Frameworku CakePHP podczas budowania stron internetowych

Instrukcja portalu TuTej24.pl

Instrukcja obsługi dla Wnioskodawcy

dokumentacja Edytor Bazy Zmiennych Edytor Bazy Zmiennych Podręcznik użytkownika

Systemy operacyjne. Laboratorium 9. Perl wyrażenia regularne. Jarosław Rudy Politechnika Wrocławska 28 lutego 2017

Moduł Handlowo-Magazynowy Przeprowadzanie inwentaryzacji z użyciem kolektorów danych

ELEKTRONICZNA KSIĄŻKA ZDARZEŃ

Konfiguracja parametrów pozycjonowania GPS /5

MS Word Długi dokument. Praca z długim dokumentem. Kinga Sorkowska

Atrybuty bloki z atrybutami, wyciągi atrybutów.

Umowy Instrukcja użytkownika systemu GW-MAX

1. INFORMACJE O DOKUMENCIE 2. WPROWADZENIE

Rozdział 5: Style tekstu

Podręcznik Integracji

Dodawanie operacji dodatkowych w WAPRO Mag.

CMS- kontakty (mapa)

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Przewodnik użytkownika (instrukcja) AutoMagicTest

Dokumentacja użytkownika systemu Miniaplikacja Urządzenie autoryzujące

Jako lokalizację, w której będzie kontynuowana praca w przyszłym roku szkolnym, warto wybrać tę, w której zgromadzonych jest więcej danych.

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

UONET+ - moduł Sekretariat. Jak wykorzystać wydruki list w formacie XLS do analizy danych uczniów?

PHP: bloki kodu, tablice, obiekty i formularze

Dokumentacja systemu NTP rekrut. Autor: Sławomir Miller

UMOWY INSTRUKCJA STANOWISKOWA

Instrukcja obsługi Multiconverter 2.0

Transkrypt:

Mechanizm routingu mgr Tomasz Xięski, Instytut Informatyki, Uniwersytet Śląski Katowice, 2011

URL (Uniform Resource Locator) unikalny identyfikator wskazujący lokalizację zasobu sieciowego. URI (Uniform Resource Identyficatior) unikalny identyfikator zasobu sieciowego. Standardowo URI strony www (np. http://www.onet.pl) jest utożsamiany z jej URL. Przykładowe URI (tożsame z URL) http://www.tomaszx.pl/zajecia/systemy-sieciowe/ http://www.google.com/search?q=systemy+wielowarstowe& ie=utf-8&oe=utf-8 Rozróżnienie URI i URL jest istotne m. in. z punktu widzenia tzw. sieci semantycznej (Semantic Web). Wiedz ludzka powinna być zapisana w ustandaryzowanym formacie, a każdy zapis ma swój jednoznaczny identyfikator, odnoszący się do bytów rzeczywistych i abstrakcyjnych.

URL interfejs zasób <=> użytkownik Wady tradycyjnych URL: o Link postaci test.pl/?id=5&shva=1 nie mówi nic na temat zasobu na jaki wskazuje. o Ujawniają wewnętrzną strukturę aplikacji możliwe ryzyko bezpieczeństwa. Mechanizm routingu dedykowany framework wbudowany w Symfonie, służący zarządzaniu wewnętrznymi i zewnętrznymi URL ami. Gdy przychodzi żądanie wyświetlenia danej strony mechanizm routingu parsuje URL a i konwertuje go na wewnętrzny identyfikator URI. /job/show/id/1 job/show?id=1

Generalnie wewnętrzne URI są projektowane według następującego wzorca: MODULE/ACTION?key=value&key_1=value_1&... W przykładowym URI: /job/show/id/1 job nazwa modułu, show nazwa akcji, id/1 - parametr Routing jest mechanizmem dwukierunkowym zmiana URLi nie niesie ze sobą konieczności zmiany wewnętrznej implementacji URI.

Przyporządkowanie URI URL jest definiowane w pliku konfiguracyjnym routing.yml. Plik ten opisuje tzw. ścieżki. Każda ścieżka ma swoją nazwę (np. homepage), wzorzec w postaci wyrażenia regularnego (/:module/:action/*) i ewentualne parametry. Podczas żądania dostępu do zasobu, mechanizm routingu próbuje dopasować wzorzec do adresu URL. Kolejność ścieżek jest bardzo ważna! Pierwsza ścieżka która zostanie dopasowana wygrywa. ścieżki

Gdy pojawia się żądanie mające URL postaci <adres_serwera>/job, pierwszą ścieżką która zostanie dopasowana jest ścieżka nazwana default_index. We wzorcu słowo poprzedzone znakiem dwukropka (:) oznacza zmienną, więc zapis /:module oznacza dopasuj adres w postaci /cokolwiek. Wówczas zmienna specjalna module będzie miała wartość job. Wartość tą można pobrać wykorzystując polecenie: $request->getparameter('module') W podanym przykładzie została również zdefiniowana wartość jaką przyjmie zmienna action. Wobec tego dla każdego adresu URL, który zostanie dopasowany do tej ścieżki, będzie miał parametr action ustawiony na index.

Żądanie w postaci /job/show/id/1 zostanie dopasowane do wzorca /:module/:action/*. Znak gwiazdki (*) we wzorcu oznacza dowolną liczbę par klucz-wartość rozdzielonych znakiem ukośnika /. Ostatecznie zatem zostanie dokonane następujące przyporządkowanie: module = job action = show id = 1 Aby stworzyć odnośnik URL postaci /job/show/id/1 w kodzie szablonu należy wywołać funkcję url_for(): url_for('job/show?id='.$job->getid()) Można również osiągnąć ten sam efekt wykorzystując nazwę ścieżki: url_for('@default?module=job&action=show&id='.$job->getid())

Po wejściu na adres naszej aplikacji jesteśmy witani domyślną stroną Contratulations. Aby to zmienić należy lekko zmodyfikować odpowiedzialną za to zachowanie ścieżkę na: Jedyną potrzebną zmianą jest zmiana modułu z default na ad. Ale prócz tego dobrze jest zmodyfikować wszelkie inne odwołania szablonu (apps/frontend/templates/layout.php ) tak aby korzystały one z wprowadzonej przez nas ścieżki.

Zmienimy adresy w formacie: http://localhost:8080/ad/show/id/1 na bardziej przyjazny zapis: http://localhost:8080/ad/1/kupie+patelnie W tym celu należy dodać nową ścieżkę do pliku routing.yml: Oprócz tego należy zmodyfikować funkcję url_for() w pliku indexsuccess.php, aby poprawnie generowała ona linki w takim formacie. Jak to zrobić? Można też zdefiniować format URL korzystając z tablicy:

System routingu ma wbudowane mechanizmy do walidacji URL. Każda zmienna zdefiniowana we wzorcu może zostać poddana walidacji wykorzystując wyrażenia regularne. Odpowiada za to sekcja requirements. Powyższy zapis narzuca wymaganie, aby zmienna id była typu numerycznego. W przeciwnym wypadku ścieżka nie zostanie dopasowana i następuje próba analizy wzorca innej ścieżki.

Każda ścieżka zdefiniowana w pliku routing.yml jest wewnętrznie reprezentowana jako obiekt klasy sfroute. Klasę tą można zmienić korzystając z parametru class. Wiadomym jest, że protokół HTTP ma wiele metod m.in. GET, POST, HEAD itp. Można wprowadzić restrykcje, by ścieżka została dopasowana tylko jeśli użyto określonej metody protokołu HTTP w żądaniu. Ograniczenie to wymaga zmiany klasy ścieżki na sfrequestroute i podaniu określonych metod jako wartości parametru sf_method.

Jako, że wewnętrzny URI dla funkcji url_for() w pliku indexsuccess.php jest dość długi, można to obejść zmieniając klasę ścieżki ponownie, tym razem na sfdoctrineroute. Klasa sfdoctrineroute została stworzona by operować na ścieżkach będących obiektami Doctrine. Ścieżka ad_show_user jest teraz powiązana z StudAdAd, więc na dostęp do wszystkich pól tej klasy. Można zatem uprościć wpis w pliku indexsuccess.php na:

Rozwiązanie prawie działa. Ale zamiast: http://localhost:8080/ad/1/kupie-patelnie jest http://localhost:8080/ad/1/kupie+patelnie Wynika to z występowania spacji w tytule ogłoszenia Kupie patelnie. Dlatego należałoby stworzyć funkcję naprawczą, która zamieni wszystkie znaki nie będące literami bądź cyframi znakiem myślnika (-). Proszę otworzyć plik lib\model\doctrine\studadad.class.php i dopisać następującą funkcję:

Następnie proszę utworzyć plik lib\model\doctrine\naprawiacz.class.php z następującą zawartością: Oraz zmodyfikować utworzoną wcześniej ścieżkę: To już prawie koniec pozostała tylko jeszcze jedna mała sprawa

Ścieżka potrafi nie tylko wygenerować adres URL na podstawie zadanego obiektu [tak jak to było robione do tej pory za pomocą url_for() ], natomiast jest ona również zdolna znaleźć obiekt powiązany z określonym adresem URL. Podczas parsowania żądania przychodzącego, mechanizm routingu przechowuje powiązany z nim obiekt ścieżki, by można go było wykorzystać w akcjach. Należy zatem zmienić funkcję executeshow() aby operowała na obiekcie klasy Naprawiacz. StudAd\apps\frontend\modules\ad\actions\actions.class.php

Uzyskaliśmy już przyjazny adres do akcji wyświetlania danego ogłoszenia. Ale zostały jeszcze inne metody (index, new, edit, create, update, delete), które są nadal obsługiwane przez domyślną ścieżkę: Proszę zatem zmodyfikować plik routing.yml :

Jako, że zostały stworzone ścieżki dla wszystkich możliwych adresów URL, nie ma potrzeby korzystania już ze ścieżek domyślnych (z prefiksem default). Można je więc zakomentować lub usunąć.

Aktualnie wszystkie ogłoszenia są wyświetlane Jaki warunek (WHERE) należałoby by wprowadzić, by były wyświetlane tylko ogłoszenia nie starsze niż 30 dni?

Będąc w środowisku developerskim, uzupełniają się automatycznie logi odnośnie wszelkich działań w aplikacji, w tym widoczne są wszelkie zapytania SQL kierowane do odpowiedniego systemu bazodanowego. Wszelkie dzienniki znajdują się w katalogu /log. Znak? w zapytaniu oznacza, że Doctrine jest odpowiedzialne za podstawienie tam rzeczywistej wartości w bezpieczny sposób. Można korzystać też z paska logów - szybciej:

Ok wyświetlają się już ogłoszenia nie starsze niż 30 dni. A co z polem expires_at? Jakie wpisać zapytanie, aby tylko aktywne ogłoszenia były widoczne (wykorzystując dane zawarte w expires_at?) Proszę uzupełnić plik \data\fixtures\ads.yml dodając do niego jeden wpis należący do kategorii sprzedam, z datą wygaśnięcia równą 2011-08-11. Następnie proszę przeładować dane (doctrine:data-load) oraz sprawdzić czy dodany wpis się pojawił. Proszę zrobić najpierw kopię zapasową pliku ads.yml just in case!

Poprzedni kod działa dobrze, ale miesza warstwy kontrolera i modelu (obiekt Doctrine_Query należy do modelu). Należy stworzyć zatem dodatkową metodę w klasie lib\model\doctrine\studadadtable.class.php: Przemycono klauzulę ORDER BY Oraz zmienić metodę executeindex na:

Aby zrealizować tą funkcjonalność należy dodać metodę getwithads() do pliku lib\model\doctrine\studadcategorytable.class.php, której zadaniem będzie pobranie wszystkich tych kategorii, które mają przypisane przynajmniej jedno aktualne ogłoszenie. Jakie złączenie SQL należałoby wykonać, aby zrealizować tą funkcjonalność? Należy jeszcze zmodyfikować plik apps\frontend\modules\ad\actions\actions.class.php:

Proszę zmodyfikować plik lib\model\doctrine\studadcategory.class.php dodając do niego następującą metodę getactiveads(): Następnie proszę zmodyfikować metodę getactiveads() w pliku lib\model\doctrine\studadadtable.class.php

Należy zmodyfikować funkcję getactiveads() z klasy StudAdCategory w sposób następujący: Ale wówczas liczba rezultatów na stronę zostanie ustawiona na stałe (10). Dobrze jest więc zdefiniować parametr globalny w pliku apps\frontend\config\app.yml: Oraz pobrać jego wartość w odpowiednim miejscu, w pliku apps\frontend\modules\ad\templates\indexsuccess.php:

Żeby zobaczyć różnice, można albo przekopiować odpowiednią liczbę razy każde ogłoszenie, albo korzystać z mechanizmu dynamic fixtures. Pliki *.yml mogą zawierać pewne części kodu php, które zostaną poddane interpretacji przed dodaniem informacji do bazy danych. Aby więc zduplikować kilka ogłoszeń wystarczy dodać następujący fragment do pliku ads.yml: Znacznik <?php musi rozpoczynać linię albo być częścią wartości. Jeśli znacznik?> kończy linie, należy zawsze dodać znak nowej linii (\n).

Znając link do nieaktywnego ogłoszenia nadal można je podejrzeć. W tym celu należy zmodyfikować w pliku routing.yml odpowiednią ścieżkę: Teraz w pliku lib\model\doctrine\studadadtable.class.php należy zdefiniować metodę retrieveactivejob, która doda warunek na pole expires_at do zapytania SQL:

Chcąc wyświetlić wszystkie ogłoszenia z danej kategorii dobrze jest zrobić osobną ścieżkę do tego celu: Widać, że trzeba będzie zaprojektować nowy moduł, oraz stworzyć wirtualny akcesor (bo takiego pola nie ma w bazie danych) dla zmiennej slug. Edytujemy więc plik lib\model\doctrine\studadcategory.class.php: Zmienna musi się nazywać slug ze względu na późniejsze kroki

Aby dodać link, który będzie się wyświetlał jeśli w danej kategorii jest więcej niż 10 dokumentów edytujemy szablon indexsuccess.php: Należy jeszcze zdefiniować metodę countactiveads(), bo chcemy by link wyświetlał się tylko gdy przekroczony zostanie limit 10 ogłoszeń.

Dodajemy do pliku lib\model\doctrine\studadcategory.class.php metodę countactiveads(): Funkcja ta korzysta z innej funkcji nazwanej countactiveads(), ale zdefiniowanej w klasie StudAdAdTable. Należy zatem ją tam dodać. Aby jednak to zrobić, dobrze jest przemodelować pozostałe funkcje tej klasy, aby nie zapisywać nadmiarowego kodu i trzymać się logiki modelu MVC.

Proszę zmodyfikować odpowiednie metody klasy StudAdAdTable na następujące:

Proszę wejść na stronę z ogłoszeniami. Nazwy kategorii powinny być klikalne: A na dole strony powinna być liczba ogłoszeń z danej kategorii do wyświetlenia (również klikalna).

Aby stworzyć moduł dla kategorii proszę wykorzystać polecenie: symfony generate:module frontend category Zostanie stworzony pusty moduł. Warto w tym momencie dokonać zmiany struktury bazy danych. Teraz będziemy się częściej odwoływać do pola z nazwą kategorii, więc musi ono być bezpieczne aby móc zostać wykorzystane jako adres URL. Zaimplementowaliśmy wcześniej metodę getsafename, ale jest prostszy sposób, który oszczędzi również kłopotów z tą funkcjonalnością. Trzeba jednak zmienić strukturę bazy danych i użyć modyfikatora Sluggable.

Zmieńmy plik config\doctrine\schema.yml w sposób następujący: Następnie należy wydać polecenie: php symfony doctrine:build --all --and-load -- no-confirmation celem przebudowy całej bazy danych. Można teraz usunąć metodę getsafename() z klasy StudAdCategory.

Należy teraz podmienić zawartość pliku apps\frontend\modules\category\actions\actions.class.php na następującą: Spowoduje to dodanie brakującej akcji (dla kliknięcia na nazwę kategorii bądź licznik). W wyniku usunięcia niepotrzebnej metody executeindex() można również usunąć związany z nią automatycznie wygenerowany plik indexsuccess.php. Należy jeszcze stworzyć plik szablonu dla akcji Show().

Kiedy chcemy współdzielić jakąś część szablonu, dobrze jest zdefiniować tzw. partial. Partial to dołączana część szablonu, którego nazwa zaczyna się od znaku podkreślenia (_). Utwórzmy więc partial o nazwie _list.php w ścieżce: apps\frontend\modules\ad\templates\_list.php. Niech owy partial zawiera całą sekcje <table> wspólną dla szablonów z dwóch modułów (ad i category). Aby dołączyć partial do szablonu należy skorzystać z funkcji pomocniczej include_partial(): <?php include_partial( ad/list', array( ads' => $ad))?> Pierwszy argument funkcji include_partial() to nazwa w formie nazwa_modulu/nazwa_partial_bez_podkreslnika. Drugi argument to tablica parametrów jakie należy przekazać do partial a.

Jedyna zmiana www.tomaszx.pl/materialy/symfony/_list.txt

Proszę zamienić cała sekcje <table></table> na: Partiale działają bardzo podobnie do funkcji PHP include(), z tą różnicą, że mają wbudowane mechanizmy do obsługi pamięci podręcznej cache. Proszę sprawdzić czy po dodaniu obsługi partials, wszystko jest nadal wyświetlane poprawnie (zarówno ogłoszenia na głównej jak i same kategorie. www.tomaszx.pl/materialy/symfony/indexsuccess.txt

Spróbujmy podzielić wszystkie kategorie na 20 per stronę. Symfonia posiada dedykowaną klasę do tego celu: sfdoctrinepager. Aby jej użyć należy zmodyfikować plik akcji apps\frontend\modules\category\actions\act ions.class.php:

Konstruktor sfdoctrinepager przyjmuje parametr określający maksymalną liczbę ogłoszeń na stronę. Należy go zdefiniować w pliku apps/frontend/config/app.yml: Ponadto metoda setquery() korzysta z innej, niezdefiniowanej metody getactiveadsquery(). Należy ją więc dodać do pliku lib\model\doctrine\studadcategory.class.php:

Dodanie metody getactiveadsquery() spowodowało, że można również dokonać refactoringu metod getactiveads() oraz countactivejobs(), aby korzystały z nowostworzonej. Teraz trzeba jeszcze tylko zaktualizować szablon.

Proszę uaktualnić szablon znajdujący się w pliku apps\frontend\modules\category\templates\sho wsuccess.php zgodnie z zawartością: www.tomaszx.pl/materialy/symfony/showsucces s.txt Należy dokonać ewentualnych zmian w nazwach funkcji jeśli są one różne od prezentowanych. Następnie proszę sprawdzić, czy dodana funkcjonalność działa jak należy (kategorie się właściwie wyświetlają itp.)

Proszę korzystając z funkcji pomocniczej use_stylesheet() dołożyć pliki stylu, oraz zmienić na tej podstawie wygląd wszystkich stron aplikacji. Jeśli zajdzie taka potrzeba zmodyfikuj odpowiednie pliki *.php Wymagania: Na stronie z ogłoszeniami oraz po wejściu do danej kategorii ma się wyświetlać jedynie tytuł ogłoszenia, jego skrócona treść (do np. 50 znaków) oraz data ważności. Liczba wyświetlanych znaków treści ma być modyfikowalna w pliku konfiguracyjnym aplikacji. Po kliknięciu na nazwę danego ogłoszenia, mają się wyświetlić szczegóły z nim związane (Nazwa, słowna kategoria do której przynależy, pełny opis, mail, telefon, strona www, data utworzenia oraz data ważności), dokładnie w tej kolejności. Proszę popracować nad samym wyglądem tych stron. Mile widziane: zmiana koloru tła wiersza tabeli w zależności od tego czy to wiersz parzysty czy nieparzysty, odpowiednie odstępy między polami, odpowiednio dobrana czcionka (pogrubienie, krój czcionki). Podobnie proszę zmienić wygląd strony do Edytowania/Dodawania nowych ogłoszeń.