Model není pouze databáze
Jedním z nejrozšířenějších omylů mezi pokročilejšími PHP programátory je představa, že model v MVC je vlastně jenom databáze plus nějaké ORM, které v ní umožňuje číst, zapisovat a mazat. To je ale velké nepochopení. Model není jen datový, ale zejména funkční základ celé aplikace.
Modelujeme aplikaci
Mnoho PHP programátorů začíná v poslední době používat některý z MVC frameworků prodělávajících nyní velký rozmach. Přitom jim ale chybí jakýkoliv teoretický základ ohledně MVC architektury. Představu o tom, co vlastně MVC je, si vytvářejí teprve zpětně podle zkušeností se svým frameworkem, povětšinou špatně či neúplně. Chybí jim totiž rozhled za hranice daného frameworku, neřkuli za hranice celého PHP.
Jedním z nejrozšířenějších omylů je představa, že model v MVC je vlastně jenom databáze plus nějaké ORM, které v ní umožňuje číst, zapisovat a mazat. To je ale velké nepochopení. Model není jen datový, ale zejména funkční základ celé aplikace. V modelu je schovaná celá aplikační business logika.
Trocha suché teorie
Model si lze představit jako stavový prostor, jako množinu různých stavů, do kterých se aplikace může dostat. Jakákoliv událost, jakákoliv akce uživatele (přihlášení, odhlášení, vložení zboží do košíku, změna hodnoty v databázi) představuje jen přechod modelu z jednoho stavu do jiného.
Model je černá skříňka, která si uvnitř udržuje aktuální stav a ven nabízí pevně dané rozhraní. Voláním funkcí tohoto rozhraní můžeme zjišťovat či měnit vnitřní stav modelu. Přitom k rozhraní může přistupovat kdokoliv a kdykoliv.
Důležitým požadavkem je, že model musí být ve všech svých stavech konzistentní. Žádná událost jej nesmí dostat do nepovoleného stavu. Jelikož může jeho rozhraní volat kdokoliv a kdykoliv, nelze se spoléhat na jakékoliv vnější předpoklady. Za svou vnitřní konzistenci je model odpovědný sám.
Model jako celek proto musí být důsledně zapouzdřený, poskytovat ven jen své pevně dané rozhraní a nesmí dopustit, aby jakékoliv vnější volání narušilo jeho vnitřní integritu.
Vyměňte view a controller!
Pro lepší představu, co všechno patří do modelu, co by měl model zajišťovat a jak by se měl chovat, pomáhá představit si, že ponecháte stále stejný model, ale kompletně vyměníte controller a view, například:
- místo přijímání dotazů od prohlížeče a odesílání webových stránek budete přijímat a odesílat nějakou SOAP komunikaci,
- budete zpracovávat dávkový soubor, kde budou jednotlivé události prováděné nad aplikací všechny pěkně za sebou,
- k modelu bude přistupovat nějaký vzdálený standalone klient,
- někdo bude jednotlivé funkce rozhraní volat napřímo ručně přes nějaký terminál.
Poslední dva body se sice týkají spíš jiných prostředí, než přímo PHP, ale teď jde úlohu modelu v MVC obecně.
Ve všech těchto případech musí model zajišťovat plnou funkčnost aplikace. Zároveň ho žádné volání nesmí rozhodit, i kdyby třeba v posledním příkladu psal uživatel do terminálu úplné blbostě.
Co patří a co nepatří do modelu
Co tedy u webových aplikací všechno zahrnout do modelu? Co je všechno součástí aktuálního stavu aplikace? Nelze samozřejmě sestavit úplný seznam, tak spíš jenom pár typických příkladů:
- Aplikační data – jejich čtení, zápis, aktualizace, mazání, zpracování, ověřování apod.
- Aktuální uživatel – zda je přihlášený, kdo je přihlášený, ke kterým operacím má či nemá oprávnění apod.
- Obsah košíku – jaké zboží má daný uživatel přihozené do košíku v rámci otevřené relace.
Naopak do modelu rozhodně nepatří věci, které jsou závislé na konkrétním controlleru a view. Například u aktuálního uživatele je to kontrola jeho SID, User-Agenta či IP adresy. Ty jsou dané konkrétním typem přístupu k modelu, v jiném případě je třeba vůbec mít nemusím.
Validace vstupních dat
Vezměme si jako jednoduchý příklad nějakou položku, která může mít
maximálně 30 znaků. To je základní vlastnost dané položky, i samotné
databázové pole bude nejspíš definováno jako VARCHAR(30).
Volitelně mohu samozřejmě kontrolu provádět například v prohlížeči JavaScriptem nebo na straně serveru v controlleru. Ale jenom jako redundantní kontrolu, například pro zvýšení komfortu. Když ale vyměním svůj controller a view za nějaké jiné, tak v těch už najednou žádná kontrola na 30 znaků třeba vůbec nebude.
Ovšem i v takovém případě musí být aplikace odolná proti situaci, že do ní někdo pošle znaků padesát. Hlavní místo, kde se musí omezení kontrolovat vždy a bez výjimky, je proto právě model.
Nejde jen o databázi
Jak jsem již zmínil, model vůbec není jenom pro práci s jednotlivými databázovými tabulkami a záznamy. Rozhraní modelu by naopak v ideálním případě mělo zbytek aplikace odstínit od konkrétní databázové implementace, od konkrétních databázových tabulek a atributů.
Pokud tedy budu chtít založit nového uživatele, tak databázovou implementaci odkrývající přístup může vypadat takto:
$user = new User;
$user->setUsername('pepa');
$user->setPassword('xxx');
$user->store();
Zatímco model, který odstíní zbytek aplikace od implementačních detailů, bude vypadat třeba takto:
$user = User::create('pepa', 'xxx');
Extrémní sporty v přírodě
V článku jsem se snažil popsat čisté systémové pojetí modelu tak, jak je zamýšlen v MVC architektuře. Zmiňovaný přístup pak doceníte u některých pokročilých aplikací, které drtivou většinu své business logiky přesouvají přímo do databáze.
Interface takového modelu je nabízen typicky v podobě úložných procedur. K modelu pak lze přistupovat skutečně odkudkoliv, kde je konektor do dané databáze, od PHP kódu přes aplikace v C++ až po terminálový klient. To už je ale na jiné povídání.
