Skip to content

2. Podejście do tworzenia aplikacji MVC w środowisku web/PHP

Przedstawiamy tutaj podejście do tworzenia aplikacji internetowych/PHP zgodnych z architekturą MVC. Ma ono jedynie na celu wskazanie kierunków działania. Czytelnik dostosuje je do własnych preferencji i potrzeb.

  1. Zaczniemy od zdefiniowania wszystkich widoków aplikacji. Są to strony internetowe wyświetlane użytkownikowi. Przy projektowaniu widoków przyjmiemy perspektywę użytkownika. Wyróżniamy trzy rodzaje widoków:
    • formularz wprowadzania danych, którego celem jest uzyskanie informacji od użytkownika. Zazwyczaj zawiera on przycisk służący do wysłania wprowadzonych danych na serwer.
    • strona odpowiedzi, która służy wyłącznie do przekazania informacji użytkownikowi. Często zawiera ona jeden lub kilka linków, umożliwiających użytkownikowi przejście do kolejnej strony aplikacji.
    • strona mieszana: kontroler wysłał do klienta stronę zawierającą wygenerowane przez siebie informacje. Ta sama strona posłuży klientowi do przekazania kontrolerowi nowych informacji pochodzących od użytkownika.
  1. Każdy widok spowoduje utworzenie strony o nazwie PHP. Dla każdej z nich:
    • określimy wygląd strony
    • określimy, które jej części są dynamiczne:
      • informacje przeznaczone dla użytkownika, które kontroler musi przekazać jako parametry do widoku PHP. Proste rozwiązanie wygląda następująco:
        • kontroler umieszcza w słowniku $dReponse informacje, które chce przekazać widokowi V
        • kontroler wyświetla widok V. Jeśli odpowiada on plikowi źródłowemu V.php, wyświetlenie to uzyskuje się po prostu za pomocą instrukcji include V.php.
        • Powyższe wstawienie stanowi wstawienie kodu w kontrolerze. Słownik $dReponse, wypełniony przez kontroler, jest bezpośrednio dostępny z poziomu kodu V.php.
      • Dane wprowadzane, które muszą zostać przekazane do programu głównego w celu przetworzenia. Muszą one stanowić część formularza HTML (tag <form>).
  1. Można schematycznie przedstawić wejścia i wyjścia każdego widoku
  • dane wejściowe to dane, które kontroler musi dostarczyć do strony PHP
  • dane wyjściowe to dane, które strona PHP musi dostarczyć kontrolerowi aplikacji. Są one częścią formularza HTML, a kontroler pobierze je za pomocą operacji typu $_GET["param"] (metoda GET) lub $_POST["param"] (metoda POST).
  1. Często strona końcowa wysyłana do klienta nie jest pojedynczym widokiem, lecz kompozycją widoków. Na przykład strona wysyłana do użytkownika może mieć następującą postać:

Obszar 1 może być paskiem tytułowym, obszar 2 paskiem menu, a obszar 3 obszarem treści. W PHP tę kompozycję można uzyskać za pomocą następującego kodu HTML/PHP:

<table>
    <tr>
        <td><?php include zone1.php ?></td>
    </tr>
    <tr>
        <td><?php include zone2.php ?></td>
        <td><?php include zone3.php ?></td>
    </tr>
</table>

Można uczynić ten kod dynamicznym, wpisując:

<table>
    <tr>
        <td><?php include $dReponse['urlZone1'] ?></td>
    </tr>
    <tr>
        <td><?php include $dReponse['urlZone2'] ?></td>
        <td><?php include $dReponse['urlZone3'] ?></td>
    </tr>
</table>

Taki układ widoków może stanowić jedyny format odpowiedzi przekazywanej użytkownikowi. W takim przypadku każda odpowiedź dla klienta będzie musiała określić trzy elementy URL, które należy załadować do trzech obszarów przed wyświetleniem strony odpowiedzi. Przykład ten można uogólnić, zakładając, że istnieje kilka możliwych szablonów strony odpowiedzi. Odpowiedź dla klienta będzie zatem musiała:

  • określić szablon, który ma zostać użyty
  • określić elementy, które mają zostać w nim zawarte
  • zlecić wyświetlenie szablonu
  1. Napiszemy kod PHP/HTML dla każdego szablonu odpowiedzi. Kod ten jest zazwyczaj prosty. Kod z powyższego przykładu mógłby wyglądać następująco:
<?php
    // inicjalizacje do testów bez kontrolera
...
?>
<html>
    <head>
      <title><?php echo $dReponse['titre'] ?></title>
      <link type="text/css" href="<?php echo $dReponse['style']['url'] ?>" rel="stylesheet" />    
    </head>
  <body>      
    <table>
        <tr>
            <td><?php include $dReponse['urlZone1'] ?></td>
        </tr>
        <tr>
            <td><?php include $dReponse['urlZone2'] ?></td>
            <td><?php include $dReponse['urlZone3'] ?></td>
        </tr>
    </table>
  <body>
</html>

W miarę możliwości należy stosować arkusz stylów, aby móc zmieniać wygląd odpowiedzi bez konieczności modyfikowania kodu PHP/HTML.

  1. Kod PHP/HTML zostanie napisany dla każdego widoku elementarnego. Najczęściej będzie miał następującą postać:
<?php
     // ewentualnie kilka inicjalizacji, zwłaszcza na etapie debugowania
    ...
?>

<balise>
...
         // tutaj postaramy się zminimalizować kod PHP
</balise>

Warto zauważyć, że widok elementarny jest osadzany w modelu. Jego kod HTML zostaje wkomponowany w kod tego modelu. Najczęściej kod modelu zawiera już tagi <html>, <head> oraz <body>. Dlatego rzadko zdarza się, by tagi te występowały w widoku elementarnym.

  1. Można przeprowadzić testy różnych szablonów odpowiedzi i widoków elementarnych
  • Każdy szablon odpowiedzi jest testowany. Jeśli szablon nosi nazwę modele1.php, wywołamy go w przeglądarce pod adresem URL http://localhost/chemin/modele1.php Szablon oczekuje wartości z kontrolera. W tym przypadku wywołujemy go bezpośrednio, a nie za pośrednictwem kontrolera. Model nie otrzyma oczekiwanych parametrów. Aby mimo to umożliwić przeprowadzenie testów, samodzielnie zainicjujemy oczekiwane parametry za pomocą stałych na stronie PHP modelu.
  • W ten sposób testowany jest każdy model oraz wszystkie widoki elementarne. Jest to również moment na opracowanie pierwszych elementów stosowanych arkuszy stylów.
  1. Następnie piszemy logikę aplikacji:
  • Kontroler, czyli program główny, zazwyczaj obsługuje kilka akcji. W żądaniach, które do niego docierają, musi być określona akcja do wykonania. Można to zrobić za pomocą parametru żądania, który nazwiemy tutaj „action”:
    • jeśli żądanie pochodzi z formularza (<form>), parametr ten może być ukrytym parametrem formularza:
<form ... action="/C/main.php" method="post"  ...>
<input type="hidden" name="action" value="uneAction">
...
</form>
  • (ciąg dalszy)
    • jeśli żądanie pochodzi z linku, można go skonfigurować:
 <a href="/C/main.php?action=uneAction">lien</a>

Kontroler może najpierw odczytać wartość tego parametru, a następnie przekazać obsługę żądania modułowi odpowiedzialnemu za przetwarzanie tego typu żądań. W tym przypadku założyliśmy, że wszystko jest kontrolowane przez jeden skrypt o nazwie main.php. Jeśli aplikacja musi obsługiwać akcje action1, action2, ..., actionx, można utworzyć w kontrolerze osobną funkcję dla każdej akcji. Jeśli akcji jest dużo, kontroler może stać się „dinozaurem”. Można również utworzyć skrypty action1.php, action2.php, …,actionx.php, odpowiedzialne za obsługę poszczególnych akcji. Kontroler, który ma obsłużyć akcję actionx, ograniczy się do załadowania kodu odpowiedniego skryptu za pomocą instrukcji typu include „actionx.php”. Zaletą tej metody jest to, że praca odbywa się poza kodem kontrolera. W ten sposób każdy członek zespołu programistów może pracować nad skryptem obsługującym akcję actionx w sposób stosunkowo niezależny. Włączenie kodu skryptu actionx.php do kodu kontrolera w momencie wykonywania ma również tę zaletę, że zmniejsza objętość kodu ładowanego do pamięci. Ładowany jest wyłącznie kod przetwarzający bieżącą akcję. Takie włączenie kodu powoduje, że zmienne kontrolera mogą kolidować ze zmiennymi skryptu akcji. Zobaczymy, że możemy postarać się ograniczyć zmienne kontrolera do kilku ściśle zdefiniowanych zmiennych, których należy następnie unikać w skryptach.

  • Będziemy systematycznie dążyć do wyodrębnienia kodu biznesowego lub kodu dostępu do danych trwałych w oddzielnych modułach. Kontroler jest swego rodzaju kierownikiem zespołu, który przyjmuje żądania od swoich klientów (użytkowników internetowych) i zleca ich wykonanie najbardziej odpowiednim podmiotom (modułom biznesowym). Podczas pisania kontrolera określimy interfejs modułów biznesowych, które należy napisać. Dzieje się tak, jeśli moduły te dopiero mają zostać stworzone. Jeśli już istnieją, kontroler dostosuje się do interfejsu tych istniejących modułów.
  1. Napiszemy szkielet modułów biznesowych niezbędnych dla kontrolera. Na przykład, jeśli kontroler korzysta z modułu getCodes zwracającego tablicę ciągów znaków, na początek wystarczy napisać:
function getCodes(){
    return array("code1","code2","code3");
}
  1. Następnie można przejść do testowania kontrolera i powiązanych skryptów PHP:
  • kontroler, skrypty akcji, modele, widoki oraz zasoby niezbędne dla aplikacji (obrazy itp.) umieszcza się w folderze DC powiązanym z kontekstem C aplikacji.
  • Po wykonaniu tych czynności aplikacja jest testowana, a pierwsze błędy są korygowane. Jeśli main.php jest kontrolerem, a C kontekstem aplikacji, należy wywołać URL http://localhost/C/main.php. Pod koniec tej fazy architektura aplikacji jest gotowa do działania. Ta faza testowania może być trudna, biorąc pod uwagę, że dysponujemy niewielką liczbą narzędzi do debugowania, jeśli nie korzystamy z zaawansowanych i zazwyczaj płatnych środowisk programistycznych. Można skorzystać z instrukcji echo „message”, które zapisują dane w strumieniu HTML wysyłanym do klienta i w związku z tym pojawiają się na stronie internetowej wyświetlanej przez przeglądarkę.
  1. Na koniec piszemy klasy biznesowe, których potrzebuje kontroler. Zazwyczaj jest to klasyczne tworzenie klasy PHP, najczęściej niezależnej od jakiejkolwiek aplikacji internetowej. Najpierw zostanie ona przetestowana poza tym środowiskiem, na przykład za pomocą aplikacji konsolowej. Po napisaniu klasy biznesowej włączamy ją do architektury wdrożeniowej aplikacji internetowej i sprawdzamy, czy została poprawnie zintegrowana. Tak postępujemy w przypadku każdej klasy biznesowej.