Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Framework, kilka ciewawych(może nie?) sugestii
Forum PHP.pl > Forum > PHP > Object-oriented programming
envp
Witam, piszę sobie frameworka. Na razie jest dosyć 'mały' i niedopracowany. Wszystko oparte jest na MVC. No ale dobra do rzeczy. Przemyślałem kilka spraw i wymyśliłem kilka ciekawych rzeczy, a mianowicie:

1) Forwardowanie akcji.

Forwardowanie akcji, jest bardzo przydatną rzeczą w każdym frameworku, ale nie spotkałem się jeszcze z forwardowaniem na kilka akcji 'z góry', a myśle że to jest dośc przydatne, bo możemy poprzez jedną akcje zaplanować co i jak sie stanie. Wiadomo, że jeśli mamy akcje 'dodajNewsa' to rzeby zrobić 'dodajNewsaiPokaz' musimy tworzyć nową akcję, tak samo 'dodajiWyloguj' , a co bedzie jak zrobimy 'dodajWylogujPrzejdznaStroneGlowna' ? to jest tworzenie akcji, które już mamy(np. wyloguj) i dodawanie do nich jednej linijki (stare proceduralne kopiowanie kodu?). A jak mamy forwarodwanie na kilka akcji z 'góry' to jest super bo tworzymy sobie tylko 'Dodaj' 'Wyswietl' 'Wyloguj' 'PrzejdzNaStroneGlowna'. (Może przykład wyłuskany, ale nie umiem na szbyko wymyśleć innego). Ja rozwiązałem to sobie w taki sposób, że mam klase 'ActionCollectioner', która implementuje sobie Iterator :

  1. <?php
  2. class ActionCollectioner implements Iterator
  3. {
  4.  
  5. private $_aActions;
  6. private $_bValidIterationPointer = true;
  7.  
  8. public function __construct(){
  9. $this->_aActions = array();
  10. }
  11.  
  12. public function rewind()
  13. {
  14. reset($this->_aActions);
  15. }
  16.  
  17. public function current()
  18. {
  19. return current($this->_aActions);
  20. }
  21.  
  22. public function key()
  23. {
  24. return key($this->_aActions);
  25. }
  26.  
  27. public function next()
  28. {
  29. if(next($this->_aActions) === false)
  30. $this->_bValidIterationPointer = false;
  31. else
  32. $this->_bValidIterationPointer = true;
  33. }
  34.  
  35. public function valid()
  36. {
  37. return $this->_bValidIterationPointer;
  38. }
  39.  
  40. public function pushAct(DispatcherToken $oDispatcherToken)
  41. {
  42. if(!($oDispatcherToken instanceof DispatcherToken))
  43. {
  44. throw new ActionCollectionerException('Parameter You given to pushObj must be instance of DispatcherToken.');
  45. }
  46. array_push($this->_aActions, $oDispatcherToken);
  47. }
  48. }
  49. ?>


w Routerze umieściłem sobie pętlę foreach(), do której podaje mój ActionCollectioner:

  1. <?php
  2. class Router Implements IRouter
  3. {
  4.  
  5. private $_sURI = null;
  6. private $_aParams = null;
  7. private $_sCtrlName = null;
  8. private $_sActName = null;
  9. private $_oDispatcher = null;
  10. private $_oActionCollectioner = null;
  11.  
  12. public function __construct(Dispatcher $oDispatcher, ActionCollectioner $oActionCollectioner)
  13. {
  14. $this->_sURI = $this->_formatQuery();
  15. $this->_oDispatcher = $oDispatcher;
  16. $this->_oActionCollectioner = $oActionCollectioner;
  17. }
  18.  
  19. private function _formatQuery(){
  20. return substr($_SERVER['REQUEST_URI'],strlen($_SERVER['SCRIPT_NAME'])-9,strlen($_SERVER['REQUEST_URI']));
  21. }
  22.  
  23. public function route()
  24. {
  25.  
  26. if (strstr($this->_sURI, '?')) {
  27. $this->_sURI = substr($this->_sURI, 0, strpos($this->_sURI, '?'));
  28. }
  29. $this->_aParams = explode('/', trim($this->_sURI, '/'));
  30. $this->_sCtrlName = $this->_aParams[0];
  31. $this->_sActName = isset($this->_aParams[1]) ? $this->_aParams[1] : null;
  32.  
  33. if (!strlen($this->_sCtrlName))
  34. {
  35. $this->_sCtrlName = _DEFAULT_CTRL_NAME;
  36. $this->_sActName = _DEFAULT_ACT_NAME;
  37. }
  38.  
  39.  $aActParams = array();
  40.  for ($i=2; $i<sizeof($this->_aParams); $i=$i+2) {
  41.  $aActParams[$this->_aParams[$i]] = isset($this->_aParams[$i+1]) ? $this->_aParams[$i+1] : null;
  42.  }
  43.  
  44.  $oCurrentAction = new DispatcherToken($this->_sCtrlName, $this->_sActName, $aActParams);
  45.  
  46. //----------------------------------------------------------tutaj klade pierwsza akcje do mojego kontenera
  47.  
  48.  $this->_oActionCollectioner->pushAct($oCurrentAction); 
  49.  
  50.  foreach ($this->_oActionCollectioner as $iKey => $oActionToken)
  51.  {
  52.  $oCurrentAction = $oActionToken;
  53.  if (!$this->_oDispatcher->isDispatchable($oCurrentAction)) {
  54.  $oCurrentAction = new DispatcherToken('Error404', _DEFAULT_ACT_NAME, $aActParams);
  55.  }
  56.  
  57.  if (!$this->_oDispatcher->isDispatchable($oCurrentAction)) {
  58.  throw new RouterException('Request could not be mapped to a route and Error404 Controller has not been crea
    ted.'
    );
  59.  }
  60.  $this->_oDispatcher->executeAction($oCurrentAction);
  61.  
  62.  }
  63.  return $oCurrentAction;
  64.  }
  65.  
  66.  
  67. }
  68. ?>


Dispatcher odpala kontroller akcji, a w nim możemy skorzystać z metody rodzica pushNextAction, która odwołuje sie do FrontController'a (który jedyny we frameworku jest singletonem) trzymającego dostęp do ActionCollectioner:

  1. <?php
  2. class DoSmth extends Controller
  3. {
  4.  
  5.  
  6. public function _index(){
  7.  
  8.  
  9. $oView = $this->getView('DoSmth');
  10. $oModel = $this->getModel('DoSmth');
  11.  
  12. $oView->setAttribute('all',$oModel->showMeAll());
  13. $oView->setAttribute('imie', $this->imie);
  14.  
  15. $this->pushNextAction('SaveNews');
  16. $this->pushNextAction('Logout');
  17. $this->pushNextAction('Index','ShowNews', array('Param1'=>'costam'));
  18. return($oView);
  19. }
  20. ?>


oczywiście pokusiło mnie żeby sprawdzić, co się dzieje jeśli przekieruje akcje z widowkiem, na akcje z widokiem - i co wyświetla sie tylko pierwsza akcja z widowkiem - czemu - zastosowanie funkcji require_once() w systemie, który przetwarza widok (pokazuje tylko raz, a że mam Template i i subTemplate - czyli główny szablon www i 'srodkowa tresc' zasysa raz Main.php i już dalej nie). Dodam, że korzystam z szablonów php. Jedyne co pozostaje to stworzyć MAX_FORWARDS, aby akcje bez widoku sie nie zapętlały.

2) Kolejna sprawa - Model.
Zauważyłem, że w niektórych frameworkach Baze danych rejestruje się we FrontControllerze... Pyanie po co, to chyba gryzie się z ideologią MVC (może jest wygodne dla programisty, który tworzy aplikacje na tym frameworku, ale mało elastyczne). Ja postanowiłem problem rozwiązać w inny sposób, a mianowicie:

mam rodzica dla każdego modelu:

  1. <?php
  2. abstract class Model
  3. {
  4. protected $_oDataSource;
  5.  
  6. protected function setDataSource($sSourceClassName,array $aParameters=array(),$sPath = null)
  7. {
  8. if(!NyssSystem::getLibraryPath($sSourceClassName)){
  9. if(!NyssSystem::fileIsReadable($sPath))
  10. throw new ModelException('File '.$sPath.' is not readable');
  11. require_once($sSourceFile);
  12.  
  13. if(!class_exists($sSourceClassName))
  14. throw new ModelException('Can not create DataSource, class does not exist');
  15. $this->_oDataSource = new $sSourceClassName($aParameters);
  16. }
  17.  else $this->_oDataSource = new $sSourceClassName($aParameters);
  18. }
  19.  
  20. protected function getDataSourceParameters()
  21. {
  22.  require_once(_DIR_APPLICATION.'_configure.php');
  23.  return($aDataSourceParameters);
  24. }
  25.  
  26.  
  27.  
  28.  
  29.  
  30. }
  31.  
  32. }
  33. ?>


dodam iż funkcja setDataSource wygląda takowo iż używam magicznego __autoload() więc nie trzeba podawać ścieżki do DataSource, aby działało - wystarczy umieścić go w katalogu core, który na początku jest skanowany (proszę mi nie mówić, że spowalnia to aplikacje, bo czas wykonania różni sie o okolo 0.005 s, a jest bardzo wygodne) No dobra, ale do rzeczy teraz mam model :

  1. <?php
  2. class DoSmthModel extends Model
  3. {
  4.  
  5.  public function __construct()
  6.  {
  7.  $this->setDataSource('DatabaseMySQL',$this->getDataSourceParameters());
  8.  }
  9.  
  10.  public function showMeAll()
  11.  {
  12. $oMysql = $this->_oDataSource;
  13.  
  14. $oMysql->query('SELECT * from costam WHERE option_id=1');
  15.  
  16. return($oMysql->fetch());
  17.  }
  18.  
  19.  
  20. }
  21. ?>


i myśle, że to jest rozwiązanie optymalne, bo jak zeche korzystać z pliku, a nie z bazy to robie:
  1. <?php
  2. $this->setDataSource('FileReader',$this->getDataSourceParameters());
  3. ?>


No tak więc to by było na tyle, proszę o komentarze do moich pomysłów i z góry dziękuję za pomoc. Pozdrawiam... Kamil
Prph
Witam,

Ad 2.

IMHO, model nie powinien byc dziedziczony. Na pewno nie z klasy Model.
Dlatego, ze 2 modele moga tak znaczaco sie roznic, ze nie ma mniejmniejszego sensu. I co wazne - model wie skad powibiera dane. Wie ze jest to baza danych mysql na serwerze tym i tym. Taka ideologia mvc.

Oczywiscie robic w kazdym modelu nowy obiekt bazy danych, wczytywanie konfiguracji dla polaczenia z baza itp jest bardzo niewygodne. Mozna zrobic dla ulatwienia Model_MySQL i z niego dziedziczyc.

Adrian.
envp
  1. <?php
  2. $this->setDataSource('DatabaseMySQL',$this->getDataSourceParameters());
  3. ?>


mój model wie skąd czytać...
NuLL
Cytat
Mozna zrobic dla ulatwienia Model_MySQL i z niego dziedziczyc.

I co questionmark.gif Bedzie przepisywal wszystkie modele kiedy bedzie chcial jechac na Postgresie questionmark.gif snitch.gif laugh.gif
Cysiaczek
Popieram NuLL'a - zrób większą abstrakcję do źródeł danych.

Pozdrawiam
mariuszn3
Za poprzednikami.. zapytania do bazy danych powinieneś przynajmniej wydzielić do osobnej klasy.
Poza tym tak jakoś niechlujnie to opisałeś masa błędów stylistycznych, literówek, anglojęzycznych wstawek (kiedy istnieją polskie odpowiedniki).. nie wiem może dla wielu jest to trendi dżezi.. ale na pewno to ogranicza czytelność i trudniej jest się połapać w tym co chcesz przekazać.. wszystko zależy od tego jaki był cel tej publikacji.
Jedna drobna rzecz.. jaki ma cel taki kod:
Kod
public function pushAct(DispatcherToken $oDispatcherToken)
  {
    if(!($oDispatcherToken instanceof DispatcherToken))
    {
      throw new ActionCollectionerException('Parameter You given to pushObj must be instance of DispatcherToken.');
    }
    array_push($this->_aActions, $oDispatcherToken);
  }

Przecież jeśli $oDispatcherToken nie będzie instancją DispatcherToken php na wstępie wywali Ci fatalny błąd. W żadnym wypadku wyjątek nie zostanie wyrzucony.
Prph
Cytat(NuLL @ 28.08.2006, 23:39:55 ) *
I co questionmark.gif Bedzie przepisywal wszystkie modele kiedy bedzie chcial jechac na Postgresie questionmark.gif snitch.gif laugh.gif


Kto jak kto, ale Ty chyba wiesz, ze SQL postgre i SQL MySQL jednak sie roznia...
Wiec jak napisze Model ktory korzysta z mysql, a pozniej musi zmienic baze, to najpierw zmieni zapytania :]

Adrian.
envp
Cytat(mariuszn3 @ 29.08.2006, 11:39:00 ) *
Za poprzednikami.. zapytania do bazy danych powinieneś przynajmniej wydzielić do osobnej klasy.
Poza tym tak jakoś niechlujnie to opisałeś masa błędów stylistycznych, literówek, anglojęzycznych wstawek (kiedy istnieją polskie odpowiedniki).. nie wiem może dla wielu jest to trendi dżezi.. ale na pewno to ogranicza czytelność i trudniej jest się połapać w tym co chcesz przekazać.. wszystko zależy od tego jaki był cel tej publikacji.
Jedna drobna rzecz.. jaki ma cel taki kod...


ok ten fragment kodu jest zbędny.

A co do opisu - pisane na szybko - to nie miał być artykuł.

Pozdrawiam. Kamil
splatch
Modele proponuję załatwić przez interfejsy + dao.

Tzn.
Interfejsy Author, AuthorDAO.
Metody Author - setName, getName, setId, getId, save
Metody AuthorDAO - getAuthorByName, getAuthorById, getAll

Do tego klasy implementujące ten interfejs:
SQLAuthorDAO oraz SQLAuthor z implementacją opartą na PDO.

Tworzenie modeli możesz załatwić w kontrolerze. W ten sposób, możesz dodać model obsługiwany przez inną technologię (np dane z XML a nie z bazy).
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.