Velmi často je potřeba do aplikace nějakým způsobem napasovat administraci. Přístupů, jak to řešit je více. Někdo preferuje generátory administračních rozhraní a různé CRUD generátory. Některé frameworky (myslím že Django je jedním z nich), takové generátory přímo obsahují. Já jsem si jednoduché administrační rozhraní k blogu napsal sám, není to zas tak náročné. Pokud se používá Zend Framework MVC s doménovými objekty, docela to jde. Je několik možností jak administraci do Zend Framework based projektu začlenit.
- Administrační modul s controllery.
- Administrační controllery v každém modulu.
Představme si aplikaci s moduly pro blog, implicitním modulem a modulem pro galerii.
app/ blog/ controllers/ IndexController.php models/ views/ default/ controllers/ IndexController.php models/ views/ galery/ controllers/ IndexController.php models/ views/ lib/ www/ js/ images/ ... ...
První způsob s použitím modulu by mohl vypadat nějak následovně.
app/ blog/ controllers/ IndexController.php models/ views/ default/ controllers/ IndexController.php models/ views/ galery/ controllers/ IndexController.php models/ views/ admin/ controllers/ BlogArticle.php BlogCategory.php GaleryCategory.php GaleryItem.php lib/ www/ js/ images/ ... ...
Tento způsob se mi moc nezamlouval zejména proto, protože smysl rozdělení aplikace do modulů vidím právě v tom, že moduly mají být jako jednotlivé samostatné části, které lze z aplikace libovolně vyndávat a zandávat. Admin modul ale přináší závislost na všech ostatních modulech, bude muset využívat modely ostatních modulů a mixuje administraci všech modulů dohromady. Proto jsem raději zvolil druhou variantu. V každém modulu vlastní admin controller.
app/ blog/ controllers/ IndexController.php AdminController.php models/ views/ default/ controllers/ IndexController.php AdminController.php models/ views/ galery/ controllers/ IndexController.php AdminController.php models/ views/ lib/ www/ js/ images/ ... ...
Toto řešení jsem použil, ale vadilo mi na tom to, že můj admin controller byl příliš rozsáhlý, staral se o administraci všech věcí blogu - článků, kategorií, komentářů. Jak z toho ven? Rozdělit jeden admin controller na více controllerů ale nějakým způsobem je seskupit jako administrační controllery a to i na úrovni URL a routování, aby se dala např. jednoduchým způsobem provést autentikace.
Zend Framewok nabízí jedno řešení a to umístit controllery do jednoho společného adresáře. Pak se dá použít jako oddělovač adresářů v URL reprezentaci podtržítko, o této vlastnosti controllerů se obecně moc neví. Jak to tedy udělat? Struktura controllerů pro blog bude následující.
app/ blog/ controllers/ IndexController.php Admin/ EntryController.php CategoryController.php models/ views/
Pak je třeba v URL používat podtržítkovou notaci a vystačíme s implicitní routou
:module/:controller/:action
Pokud tedy budeme chtít přidat v administraci kategorii článků bude URL vypadat takto.
/blog/admin_category/add
Tím zavoláme náš Blog_Admin_CategoryController::addAction()
, který se bude hledat v souboru blog/controllers/Admin/CategoryController.php
.
To je přesně to, co jsem potřeboval, jen mě tam ještě trochu vadilo to podtržítko jako oddělovač adresářů. Raději bych, aby tam bylo klasické /
, url by pak vypadala mnohem lépe
/blog/admin/category/add
Upravíme tedy naši routu takto
//blog module admin controller route $this->_router->addRoute('blogAdmin', new Zend_Controller_Router_Route('blog/admin/:controller/:action/:itemId', array('module' =>'blog','controller'=>'admin_index','action'=>null,'itemId'=>null)) );
Tím zároveň nastavíme implicitní admin controller na IndexController
v adresáři Admin
.
Dále použijeme FrontController Plugin a routeShutdown()
metodu, která se bude vykonávat až po skončení routování, v ní zjistíme, zda byla volána naše blogAdmin
routa a pokud ano, tak upravíme jméno controlleru tak, že před jeho jméno přilepíme admin_
. Tím pak v dispatcheru zajistíme volaní našeho správného admin controlleru.
class BlogAdminPlugin extends Zend_Controller_Plugin_Abstract { //============================================================================== public function routeShutdown (Zend_Controller_Request_Abstract $request) { $currentRouteName=Zend_Controller_Front::getInstance()->getRouter() ->getCurrentRouteName(); if ($currentRouteName=='blogAdmin') { //if there isn't admin_ prefix in the controller name, add it if (strpos($request->getControllerName(),'admin_') !== 0) { $request->setControllerName('admin_'.$request->getControllerName()); } } }
Takže pak naše routa
/blog/admin/category/add
Zavolá se Blog_Admin_CategoryController::addAction()
, který se bude hledat v souboru blog/controllers/Admin/CategoryController.php
. Pokud uvedeme routu
/blog/admin
Zavolá se Blog_Admin_IndexController::indexAction()
, který se bude hledat v souboru blog/controllers/Admin/IndexController.php
.
Samozřejmě, že pak v bootstrapu musíme zajistit načtení našeho pluginu. Já kromě pluginu používám i action helper a v něm mám schovanou společnou funkcionalitu všech admin controllerů, ale to už není podstatné. O víkendu to snad dostanu do svn, tam si to budete moci prohlédnout.
Komentáře (7)
Výborné !
Už nějakou dobu hnedám použitelný zdroj informací o ZF a tvůj blog je přesně to, co hledám.
Jen sem chtěl upozornit (i když do ZF zatim 100% nevidim, tak pardon, jestli je to blbost), že uvádíš, že se zavolá EntryController::addAction() a přitom v URL je "category", takžew by se měl volat CategoryController::addAction(), ne?
[1] Samozřejmě že to mám špatně, díky, opraveno
A ještě další chyba (kolik jich ještě dneska bude?), v názvech controllerů se samozřejmě musí objevit jak modul, tak ten adresář aby byl controller v celé aplikaci jendoznačný, takže nikoli jen CategoryController ale Blog_Admin_CategoryController, v článku jsem to opravil
Prosim ta Mirin, ako v bootstrape nastavim include_path aj na modely, ked mam modularnu aplikaciu? Kcel by som totiz vyuzivat Zend_Loader a jeho registerAutoload().
[4] pokud máš modely rozeseté po různých adresářích, tak nejjednodušší cesta je všechny nasázet do include_path, všechno co je v include_path Zend_loader bude natahovat.
Další možnost je asi zvolit pro jména tříd modelů nějakou konvenci a pak podědit Zend_Loader, pokud to nějak půjde (nevím).
Dost často jsou modely společné pro všechny moduly, pak stačí jen jeden adresář pro všechny modely a ten přidat do include_path, tak jsem to měl já.
koubel dik za odpoved. Ja som kcel najst riesenie, ked mam modularnu app a kcem moduly dynamicky pridavat alebo odoberat a pritom to nesmie vyzadovat zmeny v bootstrape. Takisto ukladat modely do jedneho adresara je vylucene.
Snad to pomoze aj niekomu dalsiemu.Proste k existujucej aplikacii dokopirovat napr. modul "gallery" a o nic sa nestarat. galllery by obsahoval svoje modely v sebe.
A riesenie = Controller plugin:
[6] - no vida, pěkný
Komentáře jsou uzavřeny.