Tacsiazuma
Tacsiazuma A letscode.hu alapitója, több, mint egy évtized fejlesztői tapasztalattal. Neovim függő hobbi pilóta.

PHP Model-View-Controller

Annyira divatos kifejezés az MVC manapság, ellenben amikor valaki megpróbálja elmagyarázni, akkor eszembe jut, hogy ezt talán lehetne másképp is értelmezni. Az MVC a címben is szereplő hármast, a model-view-controller triumvirátusát jelenti, amiből a kliens minden esetben egy-egy view-al találkozik.

A lényege ennek, hogy a kódunkat feldaraboljuk három nagyobb különálló részre, melyek egymással állnak kapcsolatban.

A controller feladatát roppant egyszerűen tudnánk leírni, a kliens kéréseit lefordítja a model nyelvére és amit a model visszaad, azt pedig beletölti a hozzá tartozó view-ba.

A model esetünkben a business logic, vagyis minden ami az adatainkkal kapcsolatos. Ezek különálló részek, több osztályt kell elképzelni, melyek a modelen belül egymással interakcióban állnak.

A view általában template-kből (és a hozzájuk tartozó helperekből áll). Ez is több osztályt jelent, amik szintén egymással állnak kapcsolatban, de a feladatuk viszonylag egyszerű. Ők produkálják a kimenetet. Tehát sem a controllerben, sem a modelben nem szabad egyetlen echo vagy épp print (és társai) -t elhelyezni. Az nem az ő feladatuk.

De vegyünk egy átlagos workflow-t. Mint mondtam, mi minden esetben egy view-al találkozunk, ami egy adott “action”-höz tartozik.

Na és amikor a www.facebook.com-ot hívom meg, akkor is van view, akkor mi az az action okoska?

(A facebook más elvek alapján építi fel a rendszerét, amit fluxnak hívnak, de ez jó példa lehet).

Az ebben az esetben az indexAction (hogy Zendüljünk kicsit). Tehát a kliens (bármi csak ne IE) elküldi a kérést a webszervernek. A controller látja, hogy nem adtunk át paramétert, se semmit (na jó, legyen valami session cookie, hogy mégse a login oldalt lássuk), tehát az index oldalt akarjuk látni (legalábbis szerinte, azért a routing általában ennél bonyolultabb) és meghívja a modelt, azzal, hogy “add hát ide a hírfolyam elemeit, meg úgy az alap user related (oldalak, friendlist, stb) dolgokat”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

class frontController {
// ez az osztály ami értelmezi magát a lekérdezést, URI paraméterek, sütik, hasonlók alapján és meghívja a szükséges controller szükséges metódusát, ezáltal routing feladatot végez
}
/***************************** külön fájl *****************************/
class facebookController {
// a facebook alkalmazásunk (mintha lenne több is :D) kontrollere és az index oldalhoz tartozó metódus
          public function indexAction($id) {

          $user =  $this->userMapper->getUserById($id); // az adott mapperek idekerülhetnek a konstruktorból, dependency injectionnel, öröklődéssel, service locator, stb. ez nem ennek a leckének a része        
          $news =  $this->postMapper->getPostsByUid($uid); // rémesen leegyszerüsítve
// meghívjuk az indexview-t és átadjuk neki a változókat
          $view = new IndexView(['news' => $news, 'user' => $user ]); 
          $view->display(); // jelenítsük meg
          }
}
/ ***************************  külön fájl ***************************/
class IndexView {
          public function __construct($params) {
               foreach ($params as $key => $value) { // végigjárjuk a kapott tömböt és hozzáadjuk a példányváltozóinkhoz a kapott néven
                   $this->$key = $value // a $key ez esetben egy variable variable, vagyis ha $key == 'user', akkor később $this->user-ként tudunk rá hivatkozni  
               }
          }
          public function display() {
          // itt include-olhatjuk a template fájljainkat, mivel a scope az adott objektum, ezért a $this->user-t és a $this->news-t lehet használni benne

          }
 }

Ezt azért kevésbé string módjára végzi, hanem pl. meghívja a usermapper nevű osztály getUserById metódusát és átadja neki az ID-t. (Előtte akár lefordíthatja a kapott session ID-t, vagy bármi alapján kereshet). A usermapper belenyalja magát az adatbázisba (vagy cache-be, de ne bonyolítsuk), megtalálja az adott sort v. sorokat, aztán egy hydrator segítségével a sor egyes mezőit beletölti egy User objektumba ( $u->setUid($resultset[‘uid’]), vagy akárhogy, csak egy szimpla példa volt) és ezt az objektumot visszaadja a controllernek.

A controller ezután meghívj egy másik modelt, ami az üzenőfalért felelős és lekérik az adott felhasználóhoz tartozó bejegyzéseket és azokat is objektumként (vagy épp azok tömbjeként) visszaadja. Ezt eljátszhatja a friendlisttel, bármivel. Az, hogy mire van szükségünk az adott controller (igen, akár több is lehet, de megint bonyolítjuk… ) adott metódusában lesz benne. Ez a metódus lesz felelős az alkalmazásunk index oldalának irányításáért.

Tehát ha megvannak a szükséges információk, akkor itt az ideje megjeleníteni. Van egy nagyon szép kis template-ünk, ami legyen .phtml (php és html) fájl. Sőt, lehet több is, hiszen az oldalainkon a header és footer gyakorta állandó.

1
2
3
// header.phtml
<html>
<head><title>Index</title></head> // ide is beszúrhatunk mindenfélét, headscript-et, a címet, css-t, csupán ezeket is át kell adnunk a view-nak (és például helperek segíthetnek az odavezető URL-t összerakni, stb.)
1
2
3
4
5
// indexcontent.phtml
<body>
<?php foreach ($this->news as $post): ?> // mivel az osztályon belül include-oltuk, ezért hozzáférünk a $this scope-hoz
// és itt már csak a saját fantáziánkon múlik, hogy a kapott objektumot hogy érjük el és hogy rendezzük a kinézetét.
<?php endforeach; ?>
1
2
3
// footer.phtml
// ide pedig kerülhetnek a body script-ek, vagy bármi, amit szeretnénk.
</body></html>

Remélem a leírás használható volt, bármi kérdés van, netán hibák/hiányosságok, tessenek szólni, vagy kézzel inteni.

</body></html>

comments powered by Disqus