Autor Zpráva
Tori
Profil
Dobré ráno,
uvažuji nad použitelnou strukturou webu, která by byla postavená na něčem jednodušším než na frameworku (např. skládání ze skriptů), ale zároveň dodržovala logické rozdělení M-V-C částí a využívala aspoň některé knihovny typu DB vrstvy nebo šablonového stroje. Podmínkou je, aby jako celek to bylo pochopitelné a upravitelné i pro člověka, který s frameworky nemá vůbec zkušenost a z OOP zná spíš jen základy syntaxe.
Hlavně neumím odhadnout, kde je ta hranice mezi „musím se něco doučit, ale zvládnu to pochopit“ a „nepochopitelným“ frameworkem. Pro jednotlivé komponenty/knihovny je také pokaždé jiný poměr mezi jejich přínosem a složitostí, např.:
• šablonový stroj - složitost mírná až střední, přínos velký
• DB vrstva (konkrétně dibi) - složitost střední, ale nemusím používat fetchAssoc a přínos taky velký
• router - složitost na pochopení IMHO větší, jeho přínos ji nevyváží (předpokládám menší web s jednoduchými URL),
... atd.

Jaké jsou vaše názory, zkušenosti?
Jan Tvrdík
Profil
Tori:
Nedávno jsem tady jednu jednoduchou strukturu vymyslel. Kdybych měl jít o stupeň výš (směrem k MVC), tak dostanu

Controller: orders.php

<?php
// soubor, který se postará o připojení k databázi, definici společných funkcí...
require_once __DIR__ . '/common.php';

// načtení potřebné části modelu
require_once __DIR__ . '/model/orders.php';
require_once __DIR__ . '/model/foobar.php';

// tady už jsme v controlleru a připravujeme přechod na view
$orders = getOrders(...);
...

// vykreslení šablony
renderTemplate('templates/orders.latte', array(
    'title' => 'Administrace – Objedávky',
    'orders' => $orders,
));


Model: model/orders.php

<?php
function getOrders(...)
{
    $orders = dibi::fetchAll('
        SELECT ...
        FROM [orders]
        WHERE ...
    ');

    return $orders;
}


View: templates/orders.latte

<table>
{foreach $orders as $order}
    <tr>
        ...
    </tr>
{/foreach}
</table>


Velmi snadno lze přidat také nějaký front controller (index.php + mod_rewrite), takže odpadne nutnost načítat pořád soubor common.php.
Tori
Profil
Jan Tvrdík:
Děkuju, něco takového jsem myslela. A jak by to vypadalo ještě o kousek blíž k MVC - dá se ještě udržet procedurální styl? Resp. kolikrát asi (a jak) je to ještě možné posunout směrem k MVC, než si člověk řekne, že to už nemá smysl a rovnou vezme objektový FW? Právě tyhle možnosti mezi krajními póly skládání skriptů a frameworkem mě zajímají.
Jan Tvrdík
Profil
Tori:
A jak by to vypadalo ještě o kousek blíž k MVC - dá se ještě udržet procedurální styl?
Až nebudou prosté funkce stačit, tak nic nebrání začít používat v modelu třídy. Nicméně tak jako tak začne postupně růst počet těch společných funkcí (resp. později tříd) v common.php a tento soubor funkcí / tříd není nic jiného, než jednoduchý framework. S každou přidanou funkcí (metodou) se budeš blížit plnohodnotným frameworkům.
span
Profil *
Vcelku inšpirativny serial PHP: Create Your Own MVC (Part 1).
Lamicz
Profil
Ja bych osobně využil možností OOP, ušetří to práci a zbaví Tě např. require/include. Nic komplikovanýho - dedičnost, autoload, továrny, přes magické metody debug.
Kcko
Profil
Lamicz:
Pochybuji, že by Tori, to Tebou zmíněné neovládala. Svůj topic směruje někam jinam ...
Tori
Profil
Děkuju za rady, na to video se ještě podívám.

Měla bych ještě jeden související dotaz:
Budiž web skládaný ze skriptů, MVC struktura jako ve [#2], jen kontrolery jsou v adresáři controllers. Předpokládejme, že používám zrovna svůj router a mám definovaný callback
$router->setCallbackMask('controllers/<page>.php, templates/<page>.php, templates/@layout.php');
, tj. pro URL /zobrazClanek/nejaky-titulek-clanku se budou postupně includovat skripty
controllers/zobrazClanek.php
templates/zobrazClanek.php
templates/@layout.php
V některých situacích je ale potřeba některý z volaných skriptů vyměnit, nejspíš šablonu nebo layout. Ve frameworku bych použila nějaké $this->view->setTemplate('nazevJineSablony.latte') nebo $this->setView(new AnotherView);, ale jak to udělat bez té zastřešující FW struktury, když můžu mít libovolný počet / strukturu includ.souborů?

V routeru se mi callbacky (= buď soubory k includování nebo skutečné callbacky na metodu/funkci) ukládají do fronty, kterou pak procházím a volám je přes include / call_user_func.
Mohla bych si je např. pojmenovat:
$router->setCallbackMask('ctrl = controllers/<page>.php, view = templates/<page>.php, layout = templates/@layout.php'); a pak některý z nich předefinovat a buď pokračovat ve frontě nebo ji procházet znovu od začátku:
// teď jsem v kontroleru. Vyměním šablonu:
$router->replaceCallback('view', 'templates/jinaSablona.php');
// a pokračuju dál (= zavolá se nová šablona a pak původní layout):
$router->proceed(); 

// anebo vyměním i kontroler:
$router->replaceCallback('ctrl', 'controllers/jinyCtrl.php');
$router->replaceCallback('view', 'templates/jinaSablona.php');
// nebo totéž jedním příkazem:
// $router->replaceCallback('controllers/jinyCtrl.php, templates/jinaSablona.php, templates/@layout.php');
// a projdu celou frontu znovu od začátku:
$router->restart();

Otázka zní: je to dobrý přístup, anebo bych to měla radši řešit nějak jinak? Může mít kontroler až tak velkou zodpovědnost, aby mohl kompletně změnit stardardní zpracování požadavku? Je možné, že pak budu mít na stejné URL dva různé obsahy, ale někde v administraci to je snad, doufám, snesitelné. Jde mi hlavně o minimalizaci opakování kódu.
Jan Tvrdík
Profil
Tori:
Otázka zní: je to dobrý přístup, anebo bych to měla radši řešit nějak jinak?
Tohle se mi nelíbí. Router nemůže tušit, jaká šablona se má použít, protože sám požadavku nerozumí. Router by měl mít pozici informačního centra na náměstí. Ty ženské, co tam sedí, skoro nic neumí, ale umí (měly by umět) poradit, za kým jít dál. Tedy router nemůže nikdy plně rozumně požadavku a dělat takové množství rozhodnutí dopředu. Jeho funkce má být, že on poradí, kdo by tomu požadavku mohl rozumět. A tím někým je v MVC architektuře controller. Je zodpovědností controlleru požadavek zpracovat dál a rozhodnout, jestli uživateli poslat JSON, obrázek nebo nějakou šablonu. Controller jen ten, kdo vybírá, jaká šablona se vykreslit (voláním metody renderTemplate).

Co se týče layoutu, tak tam už je to trochu na hraně. Většinou je praktické nechat výběr layoutu taky na controlleru (s tím, že se použije nějaké automatické dohledáváním s možností explicitně vybrat jiný layout). Co se týče MVC architektury, tak by ale zodpovědnost vybrat layout měl mít spíš až view, což třeba umí dobře řešit latte, kde si layout může stanovit šablona sama, tj. soubor templates/zobrazClanek.latte může vypadat třeba takto:
{layout layouty/pro-zeny.latte}
{block #content}
    <h1>{$article->title}</h1>
    {!$article->contentHtml}
{/block}
Tori
Profil
Jan Tvrdík:
Router nemůže tušit, jaká šablona se má použít, protože sám požadavku nerozumí.
Vycházela jsem z tohoto příspěvku: Mám použitelný skript na pěkné URL? , kde by místo
include "clanek.phtml"; // includuje se to, co řekne router
mohlo být

$router->setCallbackMask('<stranka>.phtml');
$router->delegate();
.. no a pak jsem nějak pokračovala logicky dál k oddělení zpracování od zobrazení, a následně k MVC. Ale asi ne na všechno je teda použitelný stejný způsob.

router nemůže nikdy plně rozumně požadavku a dělat takové množství rozhodnutí dopředu. Jeho funkce má být, že on poradí, kdo by tomu požadavku mohl rozumět.
To sice ano. Jenže pořád předpokládám, že tenhle router by nikdo nepoužil na velký web. Takže to infocentrum je na malém městě a zmíněné paní můžou celkem dobře odhadnout, že turista (požadavek) to vezme okolo náměstí k morovýmu sloupu, synagoze, muzeu slavného rodáka (nebo lid.řemesel), někde po cestě se staví v hospodě a pak pojede do háje, protože tam jiného nic k vidění není. :-) Analogicky - pokud mám takto rozdělený web, tak většina požadavků nejspíš půjde naznačenou cestou. Další častá možnost bude přesměrování z kontroleru na jinou URL (úspěšné akce v administraci), a tohle je pro dořešení těch zbývajících situací (většinou asi chyby, nebo prostě nějaké nečekané věci, na které se nehodí přesměrování).
Nehádám se, v zásadě souhlasím, jen jsem měla pocit, že v tomhle kontextu by to mohlo být jinak.

Co se týče MVC architektury, tak by ale zodpovědnost vybrat layout měl mít spíš až view
To jo. Ale pokud nepoužívám Latte, ale PHP šablonu, tak to ve view (= templates/názevStránky.php) můžu říct takhle:
$nadrazenyRidiciObjekt->zmenLayout('jinyLayout');
nebo takhle:
include TPLDIR.'/jinyLayout.php';
exit; // aby se nespustil výchozí layout.php
Přičemž to první mi připadá lepší, nerada vidím exity v kódu jinde než za přesměrováním.
Jan Tvrdík
Profil
Tori:
Vycházela jsem z tohoto příspěvku
To, co popisuje Alphard není rozhodně MVC, protože tam není v podstatě ani controller ani model :) Resp. ten jeho clanek.phtml je model+controller+view v jednom a index.php slouží jako front controller + tvoří layout. Prostě to není rozumná architektura. Až bude potřebovat některá stránka použít jiný layout nebo vracet něco jiného, než HTML, tak narazí.

většina požadavků nejspíš půjde naznačenou cestou
Proto je dobré mít napsané automatické dohledávání šablon a layoutu, ale controller musí mít možnost se rozhodnout jinak. Generování RSS na webu není nic exotického.

pokud nepoužívám Latte, ale PHP šablonu, tak to ve view (…) můžu říct takhle:
Otázkou je, proč v takhle jednoduché architektuře potřebuješ mít nějaký „nadřazený řídící objekt“, když můžeš v tom controlleru zavolat.

renderTemplate(); // výchozí šablona, výchozí layout, bez parametrů
renderTemplate($params); // výchozí šablona, výchozí layout, s parametry
renderTemplate('customTemplate'); // jiná než výchozí šablona, výchozí layout, bez parametrů
renderTemplate('customTemplate', $params); // jiná než výchozí šablona, výchozí layout, s parametry
renderTemplate('customTemplate', 'customLayout', $params); // jiná než výchozí šablona, jiný layout, s parametry

Pokud budeš mít nějaký „nadřazený řídící objekt“, tak bude sloužit stejně jako abstact class Controller v běžných MVC frameworcích, tj. bude nutně součástí controller vrstvy. A jediné, co ti on může usnadnit, že pokud bys někdy vykreslovala šablonu bez parametrů, tak tu funkci renderTemplate(); zavolá on sám. Protože ale takových případů moc není (většinou controller předává šabloně nějaké parametry), tak mi toto zjednodušení přijde jako zbytečná komplikace architektury.

Přičemž to první mi připadá lepší, nerada vidím exity v kódu jinde než
Na tu druhou variantu rovnou zapomeň, řídit životní cyklus aplikace pomocí exit je skutečně blbost.


Možná by bylo jednodušší ty parametry renderTemplate otočit, že?
function renderTemplate($params = array(), $tpl = NULL, $layout = NULL)
Tori
Profil
Jan Tvrdík:
Otázkou je, proč v takhle jednoduché architektuře potřebuješ mít nějaký ‚nadřazený řídící objekt‘, když můžeš v tom controlleru zavolat...
A v tomhle případě už bude ok, pokud si fce renderTemplate vytáhne parametr z routeru a podle toho nastaví výchozí šablonu, chápu správně?
edit: to je vlastně asi blbost, view by musel vědět, jakým způsobem se podle požadavku volá kontroler (= podle které části routy)... ještě zkusím najít něco lepšího

Vaše odpověď

Mohlo by se hodit

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: