Ostatnio zacząłem czytać o obiektowym php5, MVC, frameworkach i postanowiłem zacząć pisać kod, który byłby dla mnie wygodny oraz co ważne - nadawał się do powtórnego wykorzystania.
Opiszę to co do tej pory zrobiłem. Proszę Was, znaczniej bardziej doświadczonych programistów, o uwagi na temat mojego podejścia.
Zaznacze, że swoją pracę oparłem do tej pory na artykule Frameworki z php.pl, framework CakePHP oraz o wypowiedzi forumowiczach w topikach MVC.
Charakterystyka ogólna:
Drzewo projektu
Kod
|- actions/ (tu wrzucam kontrolery w postaci class.NazwaKontrolera.php)
|- conf/
|- models/ (tu będą modele, do tej pory nic tam nie mam)
|- lib/
| +-- core/ - tu są moje pliki wykorzystywane w całej aplikacji
| +-- class.Dispatcher.php
| +-- class.Controller.php
|- templates/ - szablony Smarty
|
|- index.php
|- conf/
|- models/ (tu będą modele, do tej pory nic tam nie mam)
|- lib/
| +-- core/ - tu są moje pliki wykorzystywane w całej aplikacji
| +-- class.Dispatcher.php
| +-- class.Controller.php
|- templates/ - szablony Smarty
|
|- index.php
Oczywiście nie jest to finalny widok drzewa projektu. Ale nie to jest ważne.
Jak działa mój framework?
index.php uruchamia Dispatcher, który parsuje URL. Jeżeli następuje zgłoszenie index.php/Akcja1/Parametr1/Parametr2/, to dispatcher załącza kontroler Akcja1, a następnie kontroler tej akcji wywołuje na sobie (w zasadzie na potomku) metodę Parametr1 z parametrem Parametr2.
Przykład: strona.pl/index.php/Uzytkownik/Pokaz/1234. Dispatcher przekaże kontrolę do kontrolera Uzytkownik. Ten natomiast włączy metodę Pokaz(1234);
Generalnie wygląda to właśnie tak. Z tego co czytałem, to chyba całkiem logiczne rozwiązanie. Oczywiście mogę się mylić...
Pliki
Plik index.php <?php require_once('conf/conf.Core.php'); require_once('lib/core/Logger/class.Logger.php'); // to nas nie interesuje - zwyklu silnik logowania require_once('lib/core/class.Dispatcher.php'); try { Logger::register('file:///tmp/log.core.txt'); $objDispatcher = new Dispatcher(); $objDispatcher->handle(); } catch (Exception $exception) { } ?>
Plik class.Dispatcher.php <?php require_once('lib/core/class.Controller.php'); require_once('Smarty.class.php'); class Dispatcher { private $_strUrlParameters; // zawiera parametry url, np: dla strona.pl/index/a/b/c/d zawiera a/b/c/d/ private $_arrUrlParameters; // tutaj parametry juz są w tablicy public function __construct() { $strUrlParameters = $_SERVER['PATH_INFO']; } public function handle() { $this->_parsePath(); // zapusuje parametry url do tablicy $strControllerName = $this->_getControllerName(); // zwroci pierwszy elementy w tablicy parametrow (indeks w tablicy oczywiscie 0) //Następnie tworzy kontroler na podstawie nazwy akcji i go uruchamia $strControllerFile = 'actions/class.'.$this->_getControllerName().'Controller.php'; { require_once($strControllerFile); $strControllerClasName = $this->_getControllerName().'Controller'; $objController = new $strControllerClasName($this->_getParametersForController()); $objController->run(); $objSmarty = new Smarty(); $objSmarty->tempalte_dir = 'templates'; $objSmarty->compile_dir = 'var'; $objSmarty->assign($objController->getModelData()); // kontroler powinien zwrocic dane dla Smarty. $objSmarty->display($objController->getView()); } else { throw new Exception('Core, dispatcher: Action '.$strActionToRun.' does not exist'); } } private function _parsePath() // rozbija parametry url na tablice { $this->_arrUrlParameters = $arrParameters; } private function _getControllerName() // zwraca nazwe akcji ktora nalezy wykonac - tutaj to pierwsza pozycja w tablicy zawierajacej parametry url { $strControllerName = $this->_arrUrlParameters[0]; return $strControllerName; else throw new Exception('Core, dispatcher: There is not action to run'); } private function _getParametersForController() { for($i = 1; $i < $intNumOfPathParameters; $i++) $arrPrametersForController[] = $this->_arrUrlParameters[$i]; return $arrPrametersForController; } } ?>
Plik class.Controller.php <?php require_once('Collection/class.Collection.php'); class Controller { private $_strControllerName = null; // nazwa kontrolera - wyorzystuje sie do wybrania widoku i modelu private $_arrControllerParameters = array(); // parametry dla kontrolera. jezeli url wygladal nastepujaco: strona.pl/index.php/Uzytkownik/Pokaz/12345 to zawiera tablice [Pokaz][1234] protected $_strPageName; // tytul strony (dla htmla) private $_objModelsCollection; // kolekcja modeli, dziala jak tablica asocjacyjna - malo wazne private $_strView; // widok - tutaj to nazwa szablonu smarty public function __construct($arrParameters = null) { if(!isset($this->_strControllerName)) // pobiera nazwe swojej klasy - wykorzystuje sie to do wyboru widoku oraz modelu { { } $this->_strControllerName = $arrResults[1]; } $this->_arrControllerParameters = $arrParameters; $this->_objModelsCollection = new Collection(); $this->_objModelsCollection->addItem($this->_strControllerName.'Model'); $this->_strView = $this->_toLowerCase($this->_strControllerName).'.tpl'; } public function getModelData() // zwraca tablice dla Smarty->assign() { return $this->_arrViewVars; } public function getView() // zwraca szablon dla smarty->display(); { return $this->_strView; } public function run() // sedno kontrolera. Jezeli przekazano dodatkowy parametr w url, np: strona.pl/index.php/Uzytkownik/Pokaz to wywola sie funkcja Pokaz, jezeli nie wywola funckje domysla, ktora defi
niuje juz potomek tej klasy { if($this->_getMethodToRun()) { } else $this->runDefaultMethod(); } protected function _setPageTitle($strTitle) { $this->_strPageTitle = $strTitle; $this->_setVar('TITLE', $strTitle); } protected function _setVar($strVarName, $mixValue) { $this->_arrViewVars[$strVarName] = $mixValue; } private function _toLowerCase($strString) { } private function _getMethodToRun() // sprawdza parametr url, czy ma wykonac jakas metode - patrz wyzej { { if(method_exists($this, $this->_arrControllerParameters[0])) return($this->_arrControllerParameters[0]); } return false; } private function _getParameterForMethod() // zwraca tablice parametrow dla metody ktora nalezy wykonac. Jezeli url wygldal:
strona.pl/index.php/uzytkownik/pokaz/12345 to wykona metode pokaz kontrolera uzytkownik przekazujac do pokaz() 12345. { { { for($i = 1; $i < $intNumOfParameters; $i++) $arrPrametersForMethod[] = $this->_arrControllerParameters[$i]; return $arrPrametersForMethod; } } } } ?>
Plik class.HelloController.php - przykładowy kontroler dla akcji Hello <?php // To jest przykladowy kontroler. Jak widac mozna wywolac strone tak: strona.pl/index.php/Hello/Hi albo Hello/Bye. Mozna przekazac argument: Hello/Bye/Adrian przez co akcja porzegna Adriana. class HelloController extends Controller { public function hi() { $this->_setPageTitle('Witamy Cię'); $this->_setVar('HEADER', 'Witaj, to działa!'); } public function bye($strName) { $this->_setPageTitle('Pa pa pa'); $this->_setVar('HEADER', 'Pa pa '.$strName.', do następnego razu!'); } public function runDefaultMethod() { $this->hi(); } } ?>
Będę Wam ogromnie wdzięczny za uwagi. Pozdrawiam serdecznie,
Adrian.