Witaj, Gościu O nas | Kontakt | Mapa
Wortal Forum PHPEdia.pl Planeta Kubek IRC Przetestuj się!

Frameworki dla PHP, czyli wydajne tworzenie aplikacji

Nudne zadania jeszcze łatwiejsze

Zbudowaliśmy solidną architekturę. Nasz framework może już stanowić całkiem stabilny szkielet przyszłych aplikacji. Mamy elegancki podział na katalogi, scentralizowaną konfigurację, rozdzielone warstwy programu (kontrolery, model z DAO, widok z systemem szablonów) i usługi systemowe (obsługa połączeń do DB). Wszystko to pozwala nam spokojnie myśleć o budowie nawet bardzo rozbudowanych serwisów WWW. Wiemy, w których, jasno określonych miejscach, dodawać nowe funkcjonalności. Dzięki temu projekt nie wymknie się nam spod kontroli.

Dobre fundamenty to podstawa, ale nie zaszkodzi ułatwić sobie pracę. Przyjrzyjmy się jeszcze raz poszczególnym warstwom, tym razem pod kątem usprawnienia i skrócenia procesu tworzenia aplikacji.

Model

Pisanie kodu samych obiektów biznesowych trudno jakoś szczególnie usprawnić. To przecież kod charakterystyczny dla każdego projektu - tworzona przez nas funkcjonalność. Ale nawet w tym obszarze można coś poprawić. Wróćmy do Listingu 4, a dokładniej spójrzmy na konstruktor klasy NewsModel. Widzimy, że wszystkie pola obiektu są podawane jako tablica asocjacyjna. Taki konstruktor jest bardzo wygodny przy pobieraniu obiektów modelu z DAO. Utwórzmy więc klasę bazową dla wszystkich obiektów biznesowych zawierającą właśnie taki domyślny konstruktor. Listing 11 pokazuje klasy: BaseModel i zmodyfikowaną NewsModel.

W trakcie pracy nad rozbudowaną aplikacją z pewnością sporo czasu spędzicie na tworzeniu klas DAO. Czynność ta staje się bardzo deprymująca od momentu, w którym zauważycie, że poszczególne klasy DAO są do siebie bardzo podobne. Nic dziwnego - zadaniem DAO jest opakowanie tzw. operacji CRUD (ang. Create, Read, Update, Delete), czyli tworzenia, pobierania, uaktualniania i kasowania obiektów. W przypadku bardziej złożonych klas biznesowych operacje odczytu mogą być skomplikowane, ale uaktualnianie i kasowanie wygląda bardzo podobnie. Jest to wykonanie jednego zapytania SQL. Zmieniają się tylko nazwy tabel i pola obiektu.

Nikt nie lubi pisać ciągle takiego samego kodu, dlatego od pewnego czasu powstają biblioteki, dzięki którym możemy zapomnieć o wielu paskudnych szczegółach związanych z SQL. Takie biblioteki i narzędzia mapują obiekty do tablic w relacyjnej bazie danych. Określa się je wspólnym mianem narzędzi ORM (ang. Object Relational Mapping). Są zbyt złożone, abyśmy przedstawili je w tym artykule. Zachęcamy jednak do przyjrzenia się takim projektom jak Propel czy Pear::DB_DataObject. Ich zastosowanie we frameworku może diametralnie skrócić czas pisania obiektów DAO, ponieważ nie musimy już pracowicie sklejać poszczególnych zapytań SQL.

Rysunek 2. Foldery w aplikacji opartej o framework

Listing 9. DAO z obsługą AdoDB

<?php
class NewsModelDao
{
    private $_connection;
    public function __construct($connection){
        $this->_connection = $connection;
    }
    public function findAllNews(){
        $query = "SELECT * FROM my_news";
        $result = $this->_connection->Execute($query);
        $result_arr = array();
        while ($line = $result->FetchRow()) {
            $result_arr[] = new NewsModel($line);
        }
        return $result_arr;
    }
}
?>

Listing 10. Kontroler akcji

<?php
require_once(DIR_MODELDAO.'/newsmodeldao.class.php');
require_once(DIR_MODEL.'/newsmodel.class.php');
class newslistaction{
    private $_newsmodeldao;
    public function __construct(){
        $dbconn = DBManager::getConnection();
        $this->_newsmodeldao = new NewsModelDao($dbconn);
    }
    public function
    processRequest(){
        $result_arr['news_list'] = $this->_newsmodeldao->findAllNews();
        $mv = new ModelAndView();
        $mv->setModel($result_arr);
        $mv->setView('news_list.tpl.html');
        return $mv;
    }
}
?>

Listing 11. Klasa bazowa dla obiektów biznesowych

class BaseModel
{
    public function __construct($data_array){
        foreach($data_array as $k => $v){
            $this->$k = $v;
        }
    }
}
Widok

Do tej pory przykładowa akcja zawierała tylko prezentację listy wiadomości, ale realna aplikacja to również obsługa wielu formularzy. Akcja obsługująca taki formularz będzie najczęściej składała się z następujących kroków:

  • walidacja,
  • zamiana danych z $ _ GET i $ _ POST na obiekty biznesowe,
  • zapisanie/uaktualnienie obiektu biznesowego.

O ile zadanie walidacji pozostawimy kontrolerowi (za wyjątkiem użycia JavaScript, które jak najbardziej mieści się w warstwie widoku), a zapisanie obiektów - warstwie modelu (dokładniej - DAO), to warto zadbać o takie konstruowanie widoku formularzy, by przetworzenie przesłanych danych do postaci obiektów było najłatwiejsze. Niech więc nazwa każdego pola formularza ma następującą postać:

name='[nazwa_klasy][nazwa_pola]',

gdzie:

  • nazwa _ klasy - po prostu nazwa klasy biznesowej, do której należy dane pole,
  • nazwa _ pola - pole klasy.

Przy takiej konstrukcji formularza zadanie kontrolera sprowadza się do przekazania zmiennej $ _ POST['nazwa _ klasy'] do konstruktora obiektu biznesowego, np.:

$bizobj = new NewsModel($_POST['NewsModel']);

Niezwykle istotna jest walidacja, ponieważ dane z formularza (bezpośrednio od użytkownika) trafiają do warstwy modelu. Ze szczególną ostrożnością należy podchodzić do stosowania tych rozwiązań w ogólnodostępnej części serwisu. Natomiast w przypadku modułu administracyjnego jest to perfekcyjne podejście.

Tak jak pisanie kolejnych klas DAO jest zajęciem nużącym, tak tworzenie poszczególnych formularzy również trudno zaliczyć do szczególnie fascynujących czynności. Pomijając wszystkie aspekty związane z HTML, formularze są do siebie podobne - często zawierają pola obecne w bazie i obiektach biznesowych. Dla PHP istnieją pakiety (np. PEAR::DB_ DataObject_FormBuilder), które automatyzują proces generowania gotowych formularzy. W praktyce jednak korzystanie z takich generycznych formularzy może być trudne. Często okazuje się, że cały formularz może być stworzony automatycznie... z wyjątkiem jednego pola.

Listing 12. MultiActionController

...
class newsMultiActionController
{
    private $_newsmodeldao;
    public function __construct(){
        $dbconn = DBManager::getConnection();
        $this->_newsmodeldao = new NewsModelDao($dbconn);
    }
    public function list_action(){
        $result_arr['news_list'] = $this->_newsmodeldao->findAllNews();
        $mv = new ModelAndView();
        $mv->setModel($result_arr);
        $mv->setView('news_list.tpl.html');
        return $mv;
    }
    public function addform_action(){
        $mv = new ModelAndView();
        $mv->setView('news_addform.tpl.html');
        return $mv;
    }
}

Listing 13. Front Controller z obsługą MultiActionController

...
try{
    $multiaction_controller = $_GET['module'];
    if ($multiaction_controller != '') {
        $multiaction_controller_file_name = DIR_ACTIONS.'/'.$multiaction_controller.'.module.php';
        if (file_exists($multiaction_controller_file_name)) {
            require_once($multiaction_controller_file_name);
            $multiactionclassname = $multiaction_controller.'MultiActionController';
            $actioncontroller = new $multiactionclassname();
            $action_to_run = $_GET['action'];
            if ($action_to_run!='') {
                $method_name = $action_to_run.'_action';
                if (method_exists($actioncontroller, $method_name)) {
                    $mv = $actioncontroller->$method_name();
                    if ($mv!=null) {
                        require_once(SMARTY_DIR_LIB.'/Smarty.class.php');
                        $smarty = new Smarty();
                        $smarty->tempalte_dir = SMARTY_DIR_TEMPALTES;
                        $smarty->compile_dir = SMARTY_DIR_TEMPALTES_C;
                        $smarty->debugging = SMARTY_DEBUG;
                        $smarty->assign($mv->getModel());
                        $smarty->display($mv->getView());
                    }
                } else {
                    throw new Exception("Brak akcji '$method_name' w module '$multiaction_controller'!");
                }
            } else {
                throw new Exception("Nie podano nazwy akcji do uruchomienia!");
            }
        } else {
            throw new Exception("Brak pliku '$multiaction_controller_file_name' zawierającego kontroler akcji!");
        }
    } else {
        throw new Exception("Nie podano nazwy modułu!");
    }
}
catch(Exception $e){
    print_r($e);
}
Kontroler

Wprowadzenie Front Controllera dało nam ogromne możliwości sterowania czynnościami, które są uruchamiane dla każdej akcji. To bardzo pożyteczny fragment aplikacji, dzięki któremu wprowadzenie obsługi np. zarządzania sesjami czy uwierzytelniania jest dziecinnie proste. Po wykonaniu prac wspólnych sterowanie przekazywane jest do właściwego kontrolera akcji. W przykładzie z Listingu 9 określiliśmy jedną z możliwych strategii wywoływania kontrolera akcji. Nic jednak nie stoi na przeszkodzie, by Front Controller obrał zupełnie inny sposób działania.

Innym, często stosowanym podejściem jest umieszczenie wszystkich akcji z wybranego modułu w jednej klasie. Spójrzmy na Listing 12, na którym znajduje się kod przykładowego kontrolera grupującego wiele akcji (ang. Multi Action Controller). Oczywiście zmianie musi też ulec Front Controller (Listing 13) oraz format URL, np.: http://[URL Front Controllera]? module=[nazwa_modułu]&action=[nazwa_akcji].

Przedstawione rozwiązanie pozwala ograniczyć liczbę tworzonych plików. Otwierając do edycji jeden z nich, widzimy całą funkcjonalność modułu, co ułatwia rozbudowę i pielęgnowanie kodu. Po stworzeniu wielu kontrolerów akcji z pewnością zauważycie, że część kodu jest bardzo podobna i powtarza się dla zestawu akcji w module. Manipulowanie obiektami jest przecież podobne, niezależnie od tego, czy są to wiadomości, czy lista użytkowników. Podobieństwa te ujawniają się szczególne wyraźnie podczas tworzenia różnego rodzaju modułów administracyjnych. Czy można jakoś ułatwić sobie życie? Oczywiście - wystarczy tylko przygotować ogólny kontroler typu Multi Action. Będzie on z pewnością zawierał akcje typu:

  • list - wyświetlenie listy obiektów,
  • addform, add - pokazanie formularza nowego obiektu i jego dodanie,
  • editform, edit - pokazanie formularza edycji obiektu i jego uaktualnienie,
  • delete - skasowanie obiektu.

Parametryzacja takiego kontrolera będzie dość prosta - wystarczy podać nazwę obiektu DAO. Zresztą spójrzcie sami na Listing 14. Czyż pisanie aplikacji nie stało się czynnością prostą i przyjemną?

Listing 14. Ogólny kontroler wielu akcji

abstract class AbstractMultiActionController {
    protected $_modelclassname;
    protected $_daoclass;
    protected $_dbconn;
    protected $_tpl_prefix;
    public function __construct(){
        $this->_dbconn = DBManager::getConnection();
    }
    public function list_action(){
        $result_arr['items_list'] = $this->_daoclass->findAllItems();
        $mv = new ModelAndView();
        $mv->setModel($result_arr);
        $mv->setView($this->_tpl_prefix.'_list.tpl.html');
        return $mv;
    }
    public function addform_action(){
        $mv = new ModelAndView();
        $mv->setModel($_POST);
        $mv->setView($this->_tpl_prefix.'_addform.tpl.html');
        return $mv;
    }
    public function add_action(){
        $biz_obj = new $this->_modelclassname($_POST[$this->_modelclassname]);
        $this->_newsmodeldao->create($biz_obj);
        return $this->list_action();
    }
    ....
}
class newsMultiActionController extends AbstractMultiActionController{
    private $_newsmodeldao;
    public function __construct(){
        parent::__construct();
        $this->_modelclassname = 'NewsModel';
        $this->_daoclass = new NewsModelDao($this->_dbconn);
        $this->_tpl_prefix = 'news';
    }
}
Informacje na podobny temat:
Wasze opinie
Wszystkie opinie użytkowników: (11)
Adam
Czwartek 14 Styczeń 2010 6:22:02 pm - pp-layouts <a.lyskawa_at_gmail.com>

A już myślałem, że jestem szaleńcem rozwijając własny framework. A robię tak dlatego, że wychodzi mi zawsze szybciej napisać brakujący komponent X niż nauczyć się od podstaw nowego frameworka. Do tego jeszcze ta siła przyzwyczajeń. Byłbym chory gdybym miał na przykład mieszać php z html w jednym pliku, a np w ZF to norma.

Fajny art
Piątek 09 Styczeń 2009 10:04:20 am - uve

Fajny art, tylko nie rozumiem jednej rzeczy.

Co jest zawarte w klasie newsmodel.clsss.php ?

Pozdr.

pdf
Wtorek 06 Styczeń 2009 1:40:05 pm - yaotzin <yaotzin1_at_o2.pl>

PDF'a dajcież ...

Art
Piątek 08 Sierpień 2008 12:29:47 pm - Joachim Peters <edaroo_at_gmail.com>

Fajny art, na poziomie :)

Wersja do wydruku
Wtorek 30 Październik 2007 4:09:24 pm - reddy

A czy jest gdzies wersja do wydruku (np. PDF lub calosc na jednej stronie)? Wygodniej czytac z kartki :)

;)
Środa 15 Sierpień 2007 8:41:40 pm - carbolymer

Ciężki artykuł o ciężkim temacie. Listingi zbyt porozrzucane, czasem nie można odnaleźć klas o których jest mowa w tekście. Jest parę literówek w kodzie. Ogólnie jest dobrze.

Dobry artykul
Czwartek 04 Maj 2006 1:45:52 am - SzajbuS <szajbus_at_rambler.com.pl>

Listingi i rysunki troche zle umiejscowione w tekscie, co utrudnia czytanie, ale wartosc merytoryczna wysoka.

Brak druku do PDF
Środa 22 Luty 2006 9:27:25 pm - angel2953

Dlaczego jest brak możliwości pobrania tego artykułu jako pliku PDF ? Lub jeśli ów link istnieje (ja go jakoś nie potrafię zlokalizować) dlaczego jest tak słobo widoczny ?

prosty temat
Sobota 28 Styczeń 2006 6:00:51 pm - emp

po prostu zrob sobie swoje klasy i chierarchie klas i masz szkielety aplikacji i do tego bardzo modularne

Profesjonalizm
Niedziela 15 Styczeń 2006 12:22:17 pm - aztech <scrabblewroclaw_at_op.pl>

Cieszę się, że uwagi jakie zgłosiłem co do wyglądu listingów oraz ich podlinkowania zostały zauważone i szybko wprowadzone. To świadczy o profesjonalnym podejściu osób tworzacych ten wortal.
Brawo!

Brak danych :)
Piątek 13 Styczeń 2006 10:32:09 pm - ..:: pingu ::.. <pingu_at_interia.pl>

W PDF'ie ten artykuł wyglada duzo lepiej :P

Szkoda ze czasem trzeba przejsc na nastepna strone aby zobaczyc listing :(

Mentax.pl    NQ.pl- serwery z dodatkiem świętego spokoju...   
O nas | Kontakt | Mapa serwisu
Copyright (c) 2003-2017 php.pl    Wszystkie prawa zastrzeżone    Powered by eZ publish Content Management System eZ publish Content Management System