Tworzenie Aplikacji Internetowych dr Wojciech M. Gańcza 2 Klasa GridRenderer Klasa pisząca grid powinna Przyjąć dane Wyświetlić na wyjściu tabelę zawierającą dane Dane możemy przekazać w konstruktorze Albo przy wywołaniu metody render Konstruktorowi zostawmy definicję tabeli (nagłówki, formaty) – dane będą przekazane do metody render Klasa GridRenderer … class GridRenderer { function GridRenderer() { } function render(&$out, &$data) … } Klasa GridRenderer … function render(&$out, &$data) { $out->out(”<table>”); while(!$data->eof()) { $out->out(”<tr>”); $row = $data->get(); foreach ($field in $row) { $out->out(”<td>$field</td>”); } $out->out(”</tr>”); } $out->out(”</table>”); } Klasa DataSource Interfejs klasy określony jest przez to czego się od niej wymaga. Można stworzyć interfejs który jest prostszy – get może zwracać false gdy nie ma danych Wtedy kod korzystający ze żródła danych musi być na taką sytuacje uczulony Ja wolę oddzielne metody (ale to kwestia gustu) Klasa DataSource … class DataSource { function eof() { } function get() { } } Przykładowe źródło danych Do testów wyświetlania przyda nam się jakiekolwiek źródło danych Zauważmy – że dane w czele nie muszą pochodzić z bazy czy pliku! W przykładowym (testowym) źródle możemy użyć wartości zaszytych na sztywno w kodzie źródła Ma to swoje zalety – widok przy każdym uruchomieniu będzie dokładnie taki sam (testy!) Przykładowe źródło danych class ExampleSource { var $row; function ExampleSource() { $this->row = 0; } function eof() { return $this->row == 3; } Przykładowe źródło danych function get() { switch ($this->row) { case 0: return array(1, ”Pierwszy”); case 1: return array(2, ”Btopou”); case 2: return array(3, ”Tres”); }; } } Kilka uwag Aby dostać się do składowych i metod– musimy ZAWSZE używać this Jeśli tego nie zrobimy Dla metod – dostaniemy informację o tym, że taka funkcja (globalna) nie istnieje (a jeśli istnieje – to zostanie zawołana!) Dla składowych – otrzymamy zmienną globalna o takiej nazwie – jeśli taka istnieje lub utworzymy zmienną lokalną – nie będzie informacji o błędzie!!! Podział na pliki Na początek wszystkie klasy możemy definiować w jednym pliku Ale gdy program będzie się komplikował Trudno będzie znaleźć odpowiednią klasę Co z pracą w kilka osób Warto umieszczać po jednej klasie w pliku (i nazywać pliki jak klasy) Program w wielu plikach Kod umieszczony w innych plikach możemy włączać do naszej strony używając funkcji include(…); require(…); require_once(…); To nie są dyrektywy preprocesora tylko funkcje – można przekazywać nazwy plików w zmiennych, albo włączać warunkowo Strona W aplikacji, wiele stron ma podobna konstrukcję. Nagłówek HTML Nagłówek aplikacji Menu Część robocza Stopka Zamknięcia znaczników strony Warto przygotować sobie klasę która je kapsułkuje Klasa Page class Page { var $body; function setBody(&$body) { $this->body = &$body; } function render(&$out) … } Klasa Page … function render(&$out) { $out->out(”<html><head>”); $out->out(”</head><body>”); $this->body->render(&$out) $out->out(”</body></html>”); } Ale jak podpiąć tu klasę rysującą grid z danych Budowa całej strony Klasa tworząca grid potrzebuje przy jego rysowaniu – drugiego parametru Nie można więc wstawić obiektu typu GridRenderer jako ciała strony Co chyba wydaje się … dobre Grid – nie jest ciałem strony – ale może być jej elementem Ciałem strony – powinien być Panel Klasa Panel class GridTestingPanel { function render(&$out) { $dat = new ExampleSource(); $grd = new GridRenderer(); $grd->render(&$out, &$dat); } } Nareszcie pierwsza strona ! require_once(”Output.php”); require_once(”Page.php”); require_once(”GridRenderer.php”); require_once(”ExampleSource.php”); require_once(”ExampleSource.php”); $out = new Output(); $page = new Page(); $panel = new GridTestingPanel(); $page->setBody(&$panel); $page->render(&$out); Po co to wszystko Można prościej – pisząc po prostu kod w HTML przeplatając poleceniami PHP Szybciej otrzymujemy gotową stronę Kod jest szybciej interpretowany Nie robimy sobie bałaganu na dysku Ale gdy aplikacja się rozrasta Zmiany trzeba prowadzać w wielu miejscach Trudno te miejsca znaleźć Po co to wszystko … Dodanie elementu do wszystkich stron – wymaga zmiany klasy Page Zmiana metody pobierania danych – wymaga użycia innego źródła Zmiana widoku, dodanie formatowania czy dodanie innych wariantów wyświetlania (PDF?) wymaga jedynie zmiany renderera Łatwo można testować program Testy Testy są istotne Testujemy zawsze cały kod Testujemy każdą funkcjonalność Testy nie powinny zajmować nam zbyt wiele czasu Potrzebujemy możliwości automatycznego testowania aplikacji Testy automatyczne Testy automatyczne – to nic innego jak funkcje. Każda taka funkcja powinna wyświetlić informacje o tym Jaki test jest wykonywany Czy się powiódł Jeśli nie – to co się nie zgadza Istnieje wiele środowisk testowych, ale na nasze potrzeby napiszemy własne Tester class Tester { var $passed; var $failed; var $out; function Tester() { $this->passed = 0; $this->falied = 0; } Tester function render(&$out) { $this->out = &$out; // tu wołamy wszystkie funkcje // testowe przekazując im $this // jako tester na którym testy są // sprawdzane. // funcje te mogą wołać metodę // test która sprawdza warunek… $out->out(”<p>Passed: ” . $this->passed); $out->out(”<p>Failed: ” . $this->failed); } Tester - test function test($label, $should, $is) { $this->out->out(”Testing $label”); if ($should == $is) { $this->out->out(” … passed<br>”); ++$this->passed; } else { $this->out->out(” … FAILED (found ‘$is’ but should be ‘$should’)<br>”); ++$this->failed; } } Jak uruchomić testy? Tester – ma taki sam interfejs jak Panel – można więc go użyć jako ciało strony Budując stronę testów możemy wykorzystać klasę Page Funkcje testowe warto umieszczać w oddzielnych plikach Ciało pierwszej funkcji testowej możemy po prostu skopiować… Test pierwszej strony require_once(”Output.php”); require_once(”Page.php”); require_once(”GridRenderer.php”); require_once(”ExampleSource.php”); require_once(”ExampleSource.php”); function TestOfFirstPage(&$tester) { $out = new TestingOutput(); $page = new Page(); $panel = new GridTestingPanel(); $page->setBody(&$panel); $page->render(&$out); $tester->test(”First page”, $out->getCRC(), 0); } Teraz wystarczy dodać wywołanie funnkcji do testera Rozbudowa testera Przeszukiwanie katalogu z testami i włączanie wszystkich znalezionych plików. Uruchamianie wszystkich funkcji testowych (jakoś je trzeba wyróżnić) Dodanie funkcji testujących konkretne elementy które powtarzają się w programie (źródeł danych, paneli, …) Inne – w miarę potrzeb W następnym odcinku Jak dostać się do bazy danych Elementy języka SQL Konstruktor bazy Dodawanie danych testowych Źródło danych I może uda się pokazać dane z bazy