Autor wpisu: cojack, dodany: 23.10.2009 09:08, tagi: php, mvc
Tak więc chciałbym rozwinąć swoją myśl w poprzednim wpisie dotyczącym mvc w php, o co mi tu chodzi oraz czy temat i kategoria ma się jakoś do treści tutaj przedstawionej. Otóż uważam że tak, wzorzec mvc jak samo rozwiniecie jego skrótu nam mówi, model, widok, kontroler. Chodzi o prezentacje kodu złożoną z warstw, i połączeniem tego z sobą, dobrze wiemy że kontroler czyli nasz cały mózg operacji powinien wywoływać metody z modelu do pobierania danych z bazy danych, tak że end user nie ma że tak powiem bezpośredniego dostępu do tej że warstwy prezentacji kodu, chyba że jesteśmy na tyle upośledzeni że nie zabezpieczymy sobie katalogów i struktura naszych katalogów pozwala użytkownikowi na przeglądanie zawartości katalogów gdzie mamy poskładane nasze klasy. W różnych książkach można spotkać różnie przedstawione formy zapisu nazewnictwa plików, *.phpm itp ale nie o tym chcę pisać, ponieważ dla mnie to powinno być na tyle intuicyjne i poprawne by nam później było łatwiej się odnaleźć w utworzonej przez nas aplikacji.
Wracając do tematu, bo wydaje mi się że trochę od niego odbiegłem, chciałbym rozwinąć swoją myśl przedstawioną w poprzednim poście (czuje jakbym się powtarzał…). Pisałem o tym że użytkownicy Doctrine dostali narzędzie i ogromnej mocy do utworzenia aplikacji zorientowanej obiektowo oraz o wzorzec MVC, i w cale nie chcę się z tego wycofać a poprzeć tą tezę argumentami.
Do czego bym zachęcał? Otóż by każdy zainteresowany pobrał sobie sandbox’a doctrine z strony projektu, standardowo linki na samym dole. Sandbox jest w pewnym stopniu za nas skonfigurowany, i jeżeli nie czujemy potrzeby modyfikacji jego ustawień proponowałbym zostawić układ taki jaki jest, chociażby dla samego tego artykułu. Ja go osobiście trochę przerobiłem na wzór układu katalogów z symfony ale nie ważne jest to teraz.
Pisałem o tym że nie będziemy musieli ładować w kontrolerach modułów a same widoki (widok), tak zakładam że jeden widok dla jednego kontrolera, jeden model dla jednego kontrolera. Wychodząc z założenia DRY ( Don’t Repeat Yourself ), jedna metoda dla jednej i tej samej akcji, bo po co się powtarzać?
Nie chciałbym by ten topic zszedł do tematu konfiguracji sandboxa, dlatego o konfiguracji Doctrine w innym temacie.
A teraz jak to nam uprzyjemnia życie? A dajmy na to że mamy jakiś kontroler, np HandlerNews
class HandlerNews extends EventHandler { private $_tpl; private $_handle; private $_lang; public function __construct($tpl,$event,$trans) { $this->_tpl = $tpl; $this->_handle = $event; $this->_lang = $trans->getLanguage(); } public function handledEvent($route) { if( $route[2] != '' && method_exists($this,$route[2]) ) { $this->_{$route[2]}(); } else throw new Exception ('Brak akcji'); } private function _show() { $news = Doctrine::getTable('News')->getNews(Route::getId(), $this->_lang); $this->tpl->assign('news',$news); } }
No i nasza metoda getNews wyglądała by mniej więcej tak (plik NewsTable.php):
class NewsTable extends Doctrine_Table { public function getNews( $id, $lang ) { $query = $this->createQuery( 'n' ) ->where( 'n.id = ?', $id) ->andWhere( 'n.lang = ?', $lang) ->execute(); return $query; } }
Oto całe nasze mvc, mamy model, który jest organizowany za pomocą doctrine, mamy plik kontrolera który sobie pobiera z bazy danych dane, wrzuca je do templatki no i jakoś tam musimy wykombinować żeby jeszcze templatke wczytywać, ale to już inna kwestia.
No więc jak widać implementacja takiego wzorca MVC nie jest trudna, i nie uprzykrzajcie sobie ludzie życia wymyślając nie wiadomo co, nie wiadomo jak złożone struktury katalogów jak to jest proste jak budowa cepa
Ja wiem że równie dobrze możemy sobie utworzyć klasę w której będziemy mieli standardowe zapytania do bazy danych np na PDO, z zwykłą składnią SQL’ową. Nikt nie broni, a nawet dlaczego nie? np:
Autor wpisu: JoShiMa, dodany: 23.10.2009 01:15, tagi: php, sql
Autor wpisu: JoShiMa, dodany: 22.10.2009 15:22, tagi: php
Autor wpisu: JoShiMa, dodany: 21.10.2009 22:01, tagi: php, sql
Autor wpisu: Vokiel, dodany: 20.10.2009 15:01, tagi: php
Pisząc nawet niewielkie aplikacje webowe często ilość klas rozrasta się do pokaźnej liczby. Rozwiązaniem problemu jest automatyczne dołączanie plików klas w momencie próby utworzenia obiektu danej klasy. Powszechnie stosuje się dwa rozwiązania: __autoload() oraz spl_autoload_register()
Aby możliwe było wykorzystanie którejś z funkcji należy sprawdzić czy są dostępne.
W przypadku funkcji __autoload()
sprawa wydaje się prosta – przede wszystkim wymagane jest PHP 5.0 (nie jest dostępna w przypadku korzystania z PHP jako CLI). W przypadku spl_autoload_register()
wymagane jest PHP w wesji >=5.1.2.
Główne różnice
__autoload() | spl_autoload_register() | |
---|---|---|
Wiele funkcji ładujących | NIE | TAK |
Obsługa wyjątków | NIE | TAK |
Możliwości rozwiązań
Możliwości rozwiązań może być tyle ilu programistów. Wszystko zależy od tego, czego wymagamy od projektu. Możemy skorzystać z prostego dołączania klas w jednej lokalizacji, rozbudować o większą liczbę lokalizacji, dodać możliwość zastosowania różnych postfiksów, sprawdzania czy klasa/interfejs zostały już wywołane, dodać możliwość przeszukiwania katalogów w głąb, stworzyć mapę klas i interfejsów i odwoływać się do nich jak do zwykłej tablicy. Możliwości jest na prawdę wiele.
Wersja super lite __autoload()
Jeśli mamy do czynienia z małą stroną, możemy ujednolicić nazewnictwo klas, przechowywać je w jednej lokalizacji i użyć najprostszego dołączania:
set_include_path($DOCUMENT_ROOT. 'classes'); // lub ini_set('include_path', $DOCUMENT_ROOT. 'classes'); function __autoload($classname) { require_once($classname.".class.php"); }
Jednak powyższe rozwiązanie może zostać wykorzystane tylko w bardzo prostych projektach, nie daje żadnej elastyczności, możliwości konfiguracji, zmian, narzuca szereg ograniczeń.
Wersja bardziej rozbudowana __autoload()
W przypadku korzystania chociażby z interfejsów w nazewnictwie interfejsA.interface.php
poprzednia metoda już nie zadziała (zresztą nie zadziała w prawie żadnym wypadku poza podstawowym ). W pierwszej kolejności możemy rozszerzyć lokalizacje o dodatkowe katalogi:
set_include_path( get_include_path() .PATH_SEPARATOR."/classes/" .PATH_SEPARATOR."/interfaces/" .PATH_SEPARATOR."/modules/" );
Dzięki temu pliki klas będą dołączane z wielu lokalizacji, nie tylko z jednej. Problem pojawia się w momencie próby użycia klasy, której nie ma. Rozszerzmy funkcję __autoload
o dodatkowe funkcjonalności:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function __autoload($classname){ try { if (class_exists($classname, false) || interface_exists($classname, false)){ return; } $class = explode('_', strtolower(strval($classname))); $deeps = count($class); $file = 'class'; for ($i=0;$i<$deeps;$i++){ $file .= '/'.$class[$i]; } $file .= '.class.php'; if (!file_exists($file) || !is_readable($file) || class_exists($classname, false) || interface_exists($classname, false)){ throw new Exception('Class cannot be found ( ' . $classname . ' )'); }else{ require_once($file); } } catch (Exception $e){ echo $e->getMessage()."\n"; } } |
Dodaliśmy kilka funkcjonalności:
- Sprawdzanie czy klasa bądź interfejs zostały już zdefiniowane
- Następnie dodaliśmy możliwość zagnieżdżania klas w głąb katalogów. Wymaganiem tutaj postawionym jest nazewnictwo klas z użyciem dolnego podkreślenia jako separatora katalogu
katalog_nazwaKlasy.class.php
lubkatalog__podkatalog_nazwaKlasy.class.php
- Sprawdzenie czy plik istnieje, czy jest możliwy do odczytu
Jednak nadal nie możemy przechwycić wyjątku blokiem try-catch, zatem przy stosowaniu tej metody wymagana jest pewność iż wszystkie wywoływane klasy mają swoje odpowiedniki w plikach.
Wersja lite spl_autoload_register()
Sam proces dołączania będzie identyczny jak wersji lite dla __autoload() z tą różnicą, że wywołanie loadera będzie inne.
1 2 3 4 5 6 7 8 9 | function classLoader($classname){ $file = $DOCUMENT_ROOT.'/class/'.$classname.'.class.php'; if (file_exists($file) && is_readable($file) && !class_exists($class, false)){ require_once($file); }else{ throw new Exception('Class cannot be found ( ' . $classname . ' )'); } } spl_autoload_register('classLoader'); |
Możliwość korzystania z funkcji w spl_autoload_functions nie jest jedyną, którą mamy do dyspozycji. Równie dobrze, możemy skorzystać z klas. Prosty przykład na pokazanie zasady funkcjonowania: