Mohebo Framework

Download

Mohebo Framework 0.3
(111.7 KiB)

Dokumentacja

Spis treści

  1. Wstęp
  2. Instalacja
  3. Schemat działania Mohebo Framework
  4. Pierwsza aplikacja - Hello World!
  5. Kontroler
  6. Konfiguracja za pomocą MoheboConfig
  7. Router
  8. Internacjonalizacja
  9. Widok
  10. Model
  11. MoheboInput
  12. Przykładowa aplikacja napisana w Mohebo Framework
  13. Na zakończenie

1. Wstęp

Ten krótki podręcznik ma na celu szybkie i jednocześnie dokładne zapoznanie się z możliwościami Mohebo Framework. Całość(tj. ta dokumentacja) jest opublikowana na licencji GNU GPL.

O Mohebo Framework

Mohebo Framework powstał w sierpniu 2008 jako alternatywa dla aktualnie istniejących, dużych i ociążałych frameworków. Głównymi założeniami jest prostota tworzenia aplikacji oraz ich wydajność. Aby zwiększyć prędkość Mohebo Framework nie wykorzystuje domyślnie żadnych bibliotek ORM lub skomplikowanych parserów szablonów. W przyszłości jest jednak planowane włączenie Doctrine oraz PDO jako jedne z adapterów bazy danych. Framework nie posiada obsługi plików ini, po, yaml etc, lecz jest możliwość jej łatwej implementacji(Jeżeli będzie zapotrzebowanie oczywiście dopiszę odpowiednie klasy). Konfiguracja oraz tłumaczenia są oparte o pliki xml.

Układ katalogów

./application - katalog główny aplikacji
./application/cache - pamięć podręczna, zawartość generowana dynamicznie
./application/config - pliki konfiguracyjne aplikacji
./application/sets - moduły aplikacji
./application/sets/NAZWA_MODUŁU/controllers - pliki kontrolerów
./application/sets/NAZWA_MODUŁU/models - pliki modeli
./application/sets/NAZWA_MODUŁU/views - pliki widoków
./application/translations - tłumaczenia
./library - rdzeń, oraz biblioteki Mohebo Framework
./library/Mohebo - Mohebo Framework

Jak czytać tę książkę

Kolejność czytania rozdziałów

Zalecane jest czytanie kolejnych rozdziałów po kolei. Większość z nich jest jednak całkowicie rozdzielna więc przeskoczenie kilka rozdziałów w przód nie powinno znacznie dezorientować czytelnika.

Założenia

Autor zakłada, iż czytelnik pracuje pod systemem UNIX, Linux, Mac OS lub innym bazującym na Uniksie oraz posiada dostęp do konsoli lub jako posiadacz systemu z rodziny Windows zna odpowiedniki poleceń uniksowych.

Wszystkie przykłady w tym podręczniku określają lokalizację Mohebo Framework jako http://localhost/MoheboFramework

Czytelnik powinien znać przynajmniej główne założenia wzorca MVC.

Konwencje użyte w tym podręczniku

Nazwa interfejsu

Nazwa funkcji lub metody

Nazwa stałej

Nazwa zmiennej

Nazwa klasy

Nazwa klasy, funkcji lub stałej wkomponowanej w PHP.

Adres strony www

Ścieżka do pliku

Nazwa parametru lub znacznika

Rezultat wykonania kodu php lub zapytania SQL

Kod wpisywany bezpośrednio w konsoli

2. Instalacja

Prawa dostępu

Mohebo Framework nie wymaga żadnych skomplikowanych procesów instalacyjnych. Niezbędne do działania jest jednak nadanie praw zapisu w niektórych katalogach przechowujących cache.

chmod 777 -R ./cache

.htaccess

Aby Mohebo Framework sprawnie obsługiwał przyjazne linki ważne jest określenie odpowiedniej ścieżki do Mohebo Framework w pliku .htaccess

1
2
3
4
5
6
7
<IfModule mod_rewrite.c>
 RewriteEngine&nbsp;On
RewriteBase&nbsp;/MoheboFramework/
RewriteCond&nbsp;%{REQUEST_FILENAME}&nbsp;!-f
RewriteCond&nbsp;%{REQUEST_FILENAME} !-d
RewriteRule  /MoheboFramework/index.php [L]
</IfModule>

Konfiguracja baz danych

Jeśli twórca aplikacji przewiduje użycie bazy danych MySQL powinien podać dokładne informacje na temat domyślnej bazy danych edytując plik ./apllication/config/model/mysqli/default.php

1
2
3
4
5
6
7
8
9
10
<?php
$config 
= Array(
'host'        => 'localhost',
'user'        => 'myuser',
'password'    => 'mypassword',
'database'    => 'mydatabse',
'prefix'    => 'mf_',
'charset'    => 'utf8'
);
?>

W przypadku użycia większej ilości baz danych wystarczy stworzyć kolejne pliki konfiguracyjne. Więcej na ten temat w rozdziale dot. tworzenia modeli.



3. Schemat działania Mohebo Framework

Mohebo Framework posiada jeden centralny punkt - klasę Core - która pośredniczy w dostępie do wielu bibliotek oraz danych konfiguracyjnych. Poniżej schemat działania przykładowej aplikacji.

Aby obraz był bardziej czytelny pominiętych zostało kilka relacji(np. klasa View może wykonywać odwołania do klasy Translate za pośrednictwem klasy Core).

4. Pierwsza aplikacja - Hello World!

Pominę w tym przykładzie pełny opis poszczególnych komponentów. Niech będzie to przykład pokazujący jak w 1 minutę stworzyć podstronę własnego serwisu. Stwórzmy plik application/sets/default/controllers/Hello.php o treści:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class HelloController extends MoheboController
{
    public function 
indexAction()
    {
        echo 
'Hello world!';
    }

    public function 
sunnyAction()
    {
        echo 
'Hello sunny world!';
    }
}
?>

Teraz po wywołaniu adresu http://localhost/MoheboFramework/hello naszym oczom ukaże się napis Hello world!, a po wywołaniu http://localhost/MoheboFramework/hello/sunny napis Hello sunny world!.

5. Kontroler

W skrócie: kontroler to klasa, która pobiera odpowiednie dane, filtruje, przetwarza i przekazuje je do widoku(Ten opis jest troszkę przekłamany. Wzorzec MVC troszkę inaczej działa, lecz dalsze zmiany będą wprowadzone dopiero w przyszłych wersjach). W zależności od wpisanego adresu wywoływana jest określona akcja kontrolera. Domyślny schemat adresu wygląda tak:
http://localhost/MoheboFramework/KONTROLER/AKCJA

Budowa kontrolera


Każda akcja jest reprezentowana za pomocą metody klasy kontrolera. Aby uniknąć przypadkowego wywołania jednej z metod nie będącej akcją do nazw metod akcji dodajmy ciąg Action. Domyślną akcją jest index dlatego każdy kontroler musi posiadać metodę indexAction() reprezentującą tę akcję.
Stwórzmy kontroler article zawierający akcję show
Implementacja naszego przykładu

1
2
3
4
5
6
7
8
9
10
11
12
class ArticleController extends MoheboController
{
    public function 
indexAction()
    {
        echo 
'Wyświetlanie listy artykułów.';
    }

    public function 
showAction()
    {
        echo 
'Wyświetlenie jednego artykułu.';
    }
}

Ten krótki kod pozwoli nam na uruchomienie dwóch prostych akcji za pomocą adresów http://localhost/MoheboFramework/article oraz http://localhost/MoheboFramework/article/show.
Dzięki dziedziczeniu z klasy MoheboController nasz kontroler otrzymuje dostęp do konfiguracji oraz metod umożliwiających w prosty sposób dołączanie kolejnych elementów frameworka.

Metody dziedziczone z MoheboController

Przejdźmy do analizy metod dziedziczonych z klasy MoheboController
bool $this->load(string $type, string $name, string $accessName = $name, mixed $option = null)
Metoda ta służy do ładowania kolejnych komponentów naszej aplikacji. Pierwszy parametr to typ wczytywanej rzeczy:


Drugi to nazwa wczytywanego elementu. Trzeci - nazwa dostępowa elementu za pomocą której będziemy się odwoływać do wczytanej klasy(domyślnie jest to po prostu nazwa elementu). Ostatni, czwarty parametr to opcje dodatkowe jak np. nazwa bazy danych przekazywana do modelu.
Załadowanie modelu naszej klasy articles mogłoby wyglądać np. tak:

1
$this->load('model','article');

Po tej operacji otrzymujemy prosty dostęp do obiektu modelu po przez $this->model połączony z ustaloną nazwą dostępową.

1
$this->model->article->metodaModelu();

Analogicznie postępujemy z innymi elementami(za wyjątkiem bibliotek, do których dostęp jest bezpośredni)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// widok
$this->load('view''showArticle');
$this->view->showArticle->display();

// pomocnik
$this->load('helper''mojHelper');
$this->helper->mojHelper->generujCos();

// z inna nazwa dostępową
$this->load('helper''mojHelper''nazwaDostepowa');
$this->helper->nazwaDostepowa->generujCos();

// uruchamianie blibliotek
$this->load('lib''biblioteka');
$myObj = new klasaBiblioteki();

W przypadku powodzenia metoda zwraca true, w przypadku porażki false

UWAGA: Od wersji 0.2 użycie metody load() jest niepotrzebne. Framework odnajdzie odpowiednie pliki automatycznie.

1
2
3
4
5
6
7
8
9
10
11
12
13
// widok
$this->view->showArticle->display();

// pomocnik
$this->helper->mojHelper->generujCos();

// z inna nazwa dostępową
$this->load('helper''mojHelper''nazwaDostepowa');
$this->helper->nazwaDostepowa->generujCos();

// uruchamianie blibliotek
$this->load('lib''biblioteka');
$myObj = new klasaBiblioteki();

Wiele osób jest przyzwyczajona, że obiekty modeli, widoków itp. nie są przechowywane w centralnym miejscu tylko zwracane. W tym celu powstała metoda get()
bool $this->get(string $type, string $name, string $accessName = $name, mixed $option = null)
Wszystkie parametry są identyczne jak w metodzie load(). Inny jest jednak sposób dostępu do obiektu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// widok 
$myObj $this->get('view''showArticle');
$myObj->display();

// pomocnik 
$myHelper $this->get('helper''mojHelper');
$myHelper->generujCos();

// z inna nazwa dostępową 
$myHelper2 $this->get('helper''mojHelper''nazwaDostepowa');
$myHelper2->generujCos();

// podczas wywoływania funkcji get tworzona jest centralna 
// kopia $this->typ->nazwaDostepowa;
// zmiany w zwróconym obiekcie nie mają jednak na nią żadnego wpływu 

// przywrócenie "czystej" postaci naszego pomocika 
$myHelper2 $this->helper->nazwaDostepowa;

W przypadku powodzenia metoda zwraca true, w przypadku porażki false

Uwaga: podczas wywoływania metody get() Mohebo Framework nie tworzy referencji do centralnego obiektu $this->typ->nazwa. Tworzona jest kopia tego obiektu. Jeśli zostanie wywołane dynamiczne przekierowywanie po przez zmianę kontrolera(więcej na ten temat dowiesz się w dalszych rozdziałach tego podręcznika) dostęp do zmian będzie już niemożliwy.

Elementy dodatkowe


Aby ułatwić pracę Mohebo Framework posiada kilka klas, których obiekty są tworzone automatycznie. Są to: MoheboInput, MoheboRouter, MoheboConfig oraz MoheboTranslate. Dostęp do nich otrzymujemy po przez bezpośrednie odwołanie się do $this->input, $this->router, $this->config lub $this->translate. Więcej o tych elementach dowiesz się w dalszych rozdziałach tego podręcznika.

6. Konfiguracja za pomocą MoheboConfig

W trosce o prostotę konfiguracji powstała klasa MoheboConfig. Mohebo Framework domyślnie pobiera konfigurację z adresu ./application/config/default.xml jednak może to ulec zmianie po przez odpowiednią konfigurację klasy routera(o czym w następnym rozdziale). Domyślnie ./application/config/default.xml zawiera:

1
2
3
4
5
6
7
8
<config>
    <app>
        <language>pl</language>
        <view>
            <theme>framework</theme>
        </view>
    </app>
</config>

Plik ten możemy dowolnie rozszerzać o własne elementy konfiguracyjne. Do każdej z tych właściwości odwołujemy się w bardzo prosty sposób wywołując w naszym kontrolerze kod:

1
2
3
4
// wyswietlenie jezyka
echo $this->config->app->language;
// wyswietlenie naszej skórki
echo $this->config->app->view->theme;

Klasa ta posiada 2 przydatne metody:

void assignData(array $data)
Pierwsza z nich wczytuje do konfiguracji tablicę przekazaną w parametrze. Druga wczytuje zawartość wskazanego pliku.
bool loadFile(string $file)

Aby określić jakiś parametr konfiguracji wykonujemy:

1
$this->config->nazwa->nazwa2->nazwa3 'moja wartosc';

7. Router

Odczyt konfiguracji

Główna konfiguracja routera znajduje się w katalogu ./application/config/router. Domyślnym plikiem konfiguracyjnym jest ./application/config/router/default.php. Możemy jednak stworzyć inne pliki odpowiadające różnym hostom . Dzięki temu dla poszczególnych hostów strony mogą działać zupełnie inaczej. Przykładowo dla hostu mohebo.com tworzymy plik ./application/config/router/mohebo.com.php dla localhost - ./application/config/router/localhost.php itd.

Plik konfiguracyjny

Plik konfiguracyjny routera może zawierać kilka specyficznych ustawień, wszystkie powinny znajdować się w prostych tablicach php.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php 
$config 
= Array( 
// parametry wymagane 
'basepath' => '/MoheboFramework/'// katalog główny aplikacji 
'explodechar' => '/'// znacznik oddzielający kolejne parametry adresu 
'maincontroller' => 'hello'// kontroler główny(uruchamiany gdy adres nie posiada żadnego parametru) 
'errorcontroller' => 'error' // kontroler domyślny(uruchamiany gdy ten podany w adresie nie zostanie odnaleziony)
// parametry dodatkowe 
'set' => 'myControllers' 
); 

$links = Array (); 
$subdomains = Array(); 
?>

Adres naszej strony to http://localhost/MoheboFramework/, więc katalogiem głównym jest /MoheboFramework/. W przypadku adresu bezpośredniego np. http://mohebo.com jako basepath podajemy /. Parametr explodechar Określa znak(lub zestaw znaków), które będą rozdzielać parametry naszego adresu. Niektórzy są przyzwyczajeni do adresów http://example.com/hello,sunny więc taka składnia również jest dostępna.

Parametr dodatkowy: set określa zestaw kontrolerów, widoków oraz plików konfiguracji. Dzięki temu można stworzyć dwa kontrolery hello wyświetlające 2 różne strony w zależności od wpisanego hostu. Jest to szczególnie przydatne podczas tworzenia dużych portali(np. farmy blogowej) gdzie różne subdomeny wymagają różnego rodzaju konfiguracji.

W przypadku nie odnalezienia kontrolera w podanym zestawie kontroler jest ładowany z zestawu domyślnego default

Aby zobrazować użycie set ustawmy jego wartość na mojzestaw. Stwórzmy katalog ./application/sets/mojzestaw/controller/ a w nim plik php z kontrolerem o nazwie hello2.

Obsługa subdomen

Dzięki tablicy $subdomains w pliku konfiguracyjnym możemy w prosty sposób określić alias kontrolera dla subdomeny.

Ustawienia subdomeny nie działają dla hostu localhost ze względu na jego specyficzny adres(brak kropki). Aby przetestować poniższą konfigurację edytuj plik /etc/hosts dodając do swojej puli adresów np. host example.com)

Przypuśćmy, że posiadamy dwie subdomeny http://hello.example.com oraz http://hellosunny.example.com. Aby określić jakie kontrolery mają uruchamiać edytujmy nasz plik konfiguracyjny.

1
2
3
4
$subdomains = Array( 
    
'hello'         => Array('hello'), 
    
'hellosunny'    => Array('hello','sunny'
);

Teraz po wywołaniu adresu http://hello.example.com/MoheboFramework/ router go odczyta tak jakby miał postać http://example.com/MoheboFramework/hello. Po wywołaniu http://hellosunny.example.com/MoheboFramework/ - http://example.com/MoheboFramework/hello/sunny

Obsługa aliasów

W podobny sposób działają aliasy. Edytuj plik konfiguracyjny.

1
2
3
$aliases = Array( 
'hsw' => Array('hello''sunny'
);

W ten sposób zostaje stworzony alias hsw. Teraz po wywołaniu adresu http://localhost/MoheboFramework/hsw router interpretuje go tak jakby użytkownik wpisał http://localhost/MoheboFramework/hello/sunny.

W tablicach $subdomains oraz $aliases można przechowywać więcej parametrów dzięki czemu wygenerowana zostanie większa część adresu.

Odczyt parametrów adresu

Gdy mamy już wybrany kontroler oraz metoda musimy w jakiś sposób zinterpretować pozostałe parametry adresu. Są one przechowywane w obiekcie klasy MoheboRouter w zmiennej values. Jest to zwykła tablica z kolejnymi elementami adresu(ale już bez nazwy kontrolera i metody).

Aby ułatwić dostęp do tych wartości MoheboRouter posiada ciekawą funkcję generującą tablicę parametrów.

array generateVariables(array $data)

Użycie z poziomu kontrolera jest dosyć proste

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class HelloConstroller extends MoheboController 

    public function 
indexAction() 
    { 
    } 

    public function 
sunnyAction() 
    { 
        
$urlData $this->router->generateVariables
            Array( 
'day''date' ), 
            Array( 
'archive','/^([0-9]{4})-(0[1-9]|1[0-2])-00$/'true), 
            Array( 
'nr''numeric' ), 
            Array( 
'title''string' 
        ); 
        echo 
'<pre>'
        
print_r($urlData); 
        echo 
'</pre>'
    } 
}

Jako parametr funkcji przekazujemy tablicę zawierającą właściwości kolejnych zmiennych. Pierwszy parametr na nazwa dzięki której będziemy identyfikować zmienną w zwróconej tablicy $urlData. Drugi to typ zmiennej. Możemy podać jedną z poniższych wartości:

Dla kolejne parametry są wykorzystywane tylko dla wyrażeń regularnych. W trzecim(domyślnie false) określamy czy wynik chcemy otrzymać w postaci tablicy. A w czwartym(również domyślnie false) czy chcemy otrzymać jedynie wartość \1 odnalezioną przez wyrażenie.

Wpiszmy teraz nasz adres http://localhost/MoheboFramework/hello/sunny/mojTytul/23/2006-05-00/1989-06-13 w wyszukiwarce. Jako wynik otrzymamy:

1
2
3
4
5
6
7
8
9
10
11
12
13
Array 

    [day] => 1989-06-13 
    [archive] => Array 
        ( 
            [0] => 2006-05-00 
            [1] => 2006 
            [2] => 05 
        ) 

    [nr] => 23 
    [title] => mojTytul 
)

Router w magiczny sposób sam dobiera odpowiednie zmienne do odpowiednich wartości. Jeśli zmienimy kolejność parametrów w adresie np. na http://localhost/MoheboFramework/hello/sunny/2006-05-00/23/1989-06-13/mojTytul/cos-dla-wyszukiwarki wynik będzie identyczny. Co więcej możemy dodać kompletnie niepotrzebne wartości które mogą być cenne z punktu widzenia SEO.

Kolejność parametrów jest ważna w przypadku gdy chcemy wygenerować kilka parametrów tego samego typu.

Odczyt parametrów specjalnych

Domyślny router frameworka Mohebo posiada wbudowany mechanizm wykrywania parametrów specjanych. W adresie wyglądają one w następujący sposób: http://mohebo.com/parametr:wartosc/parametr2:wartosc2. Ich użycie nie wpływa w żaden sposób na działanie pozostałych elementów routera więc świetnie nadają się one do przesyłania informacji o języku lub numerze aktualnie przeglądanej strony w przypadku stronnicowania wyników.

Dla przykładowego adresu http://mohebo.com/articles/page:3/ wartość parametru page możemy odczytać w następujący sposób:

1
2
echo $this->router->getOption('page');
//  3

Generowanie linków

Aby nie wpisywać linków ręcznie możemy skorzystać z metody generateLink() jako parametr podając tablicę z kolejnymi wartościami adresu oraz(opcjonalnie) tablicę parametrów specjalnych). Jeśli chcemy aby link zawierał pełny adres(z http:// oraz hostem strony) należy skorzystać z metody generateUrl().

1
2
3
4
5
6
7
8
9
10
11
class HelloConstroller extends MoheboController

    public function 
indexAction() 
    { 
        echo 
$this->router->generateLink(Array('pierwszy''drugi''trzeci'23'1989-06-13')); 
        
// wynik: /framework/pierwszy/drugi/trzeci/23/1989-06-13 
        
echo '<br/>'
        echo 
$this->router->generateUrl(Array('pierwszy''drugi''trzeci'23'1989-06-13'), Array('page' => 2'lang' => 'en');
        
// wynik: http://localhost/framework/pierwszy/drugi/trzeci/23/1989-06-13/page:2/lang:en 
    

}

W aktualnej wersji Mohebo Framework nie wykrywa, czy serwer ma wyłączoną obsługę .htaccess. Możliwe jest jednak ręczne określanie adresów o składni: http://localhost/MoheboFramework/index.php?/pierwszy/drugi/trzeci/23/1989-06-13. Router będzie działał normalnie.



8. Internacjonalizacja

Tłumaczenia fraz

Do tłumaczenia fraz Mohebo Framework może wykorzystać kilka różnych formatów plików. Na razie istnieje jednak tylko adapter XML. Pliki językowe umieszczamy w katalogach odpowiadających danym językom np. ./application/translations/pl dla języka polskiego, ./application/translations/de dla niemieckiego.

Składnia plików jest dosyć prosta.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<translations> 
    <language> 
        <original>en</original> 
        <translation>pl</translation> 
    </language> 

    <message> 
        <original>Monday</original> 
        <translation>Poniedziałek</translation> 
    </message> 
    <message> 
        <original>Tuesday</original> 
        <translation>Wtorek</translation> 
    </message> 
</translations>

Nazwy znaczników są chyba na tyle jasne, że ich tłumaczenie można pominąć. Najpierw określamy język oryginalnego tekstu i język tłumaczenia a następnie wypisujemy kolejne tłumaczenia. Znacznik główny - <translations> w rzeczywistości można wymienić na inny(tj. jego nazwa jest nieistotna).

Jeżeli dane tłumaczenie jest niepewne możemy do znacznika message dopisać znacznik fuzzy

1
2
3
4
5
6
7
<translations>
    <message> 
        <original>Tuesday</original> 
        <translation>Wtorek</translation> 
        <fuzzy>true</fuzzy> 
    </message> 
</translations>

Zapiszmy plik jako ./application/translations/pl/naszplik.xml. Wywołanie powyższego tłumaczenia z poziomu kontrolera wyglądałoby następująco:

1
2
3
4
// określamy język na jaki mamy tłumaczyć frazy 
$this->translate->setLanguage('pl');  
$this->translate->loadFile('naszplik.xml'); 
echo 
$this->translate->translate('Tuesday')

Wynikiem będzie oczywiście napis Wtorek

Wywołanie metody setLanguage() jest w tym przypadku zbędne jeśli wcześniej zadeklarowaliśmy odpowiedni język w naszym pliku konfiguracyjnym(patrz rozdział 6.).

Umieszczenie zmiennych w tłumaczeniach również jest bardzo proste.

1
2
3
4
    <message> 
        <original>Hello %name%. Today is tuesday.</original> 
        <translation>Witaj %name%. Dzisiaj jest wtorek.</translation> 
    </message>

1
echo $this->translate->translate('Hello %name%. Today is tuesday.', Array('name' => 'Mike'));

Po wywołaniu strony otrzymamy napis Witaj Mike. Dzisiaj jest wtorek.

Możemy też zastosować bardziej złożone operacje związane z liczbą mnoga w języku polskim.

1
2
3
4
5
6
7
8
9
10
11
    <message> 
        <original>There are %all_dogs% dogs.</original> 
        <translation>Tam są %all_dogs% psy</translation> 
        <lastchar p="all_dogs">2|3|4</lastchar> 
    </message> 

    <message> 
        <original>There are %all_dogs% dogs.</original> 
        <translation>Tam jest %all_dogs% psów.</translation> 
        <lastchar p="all_dogs">0|1|5|6|7|8|9</lastchar> 
    </message>

Dodaliśmy w tym przykładzie dodatkowy znacznik lastchar, który wymaga określonego znaku na końcu naszej zmiennej, której nazwę określiliśmy w parametrze p.

Dzięki temu sprawdzany jest ostatni znak naszej cyfry co daje nam wynik:

Oczywiście bibliotekę można rozszerzyć o własne znaczniki edytując odpowiednie plik ./library/Mohebo/Translate.php

Dla osób, które nie lubią ręcznie edytować plików XML tworzony jest specjalny edytor. Więcej informacji pod adresem http://forum.php.pl/index.php?showtopic=101222(Edytor wymaga przepisania)



9. Widok

Widoki w Mohebo Framework działają w dość prosty sposób. Kontroler ładuje odpowiednią klasę widoku, która wczytuje poszczególne szablony, tworzy cache i zwraca gotowy kod HTML. Aktualnie istnieje tylko jeden widok o nazwie xhtml zarządzający dokumentami z prostymi wstawkami php. W przyszłości planowane są również widoki xml, pdf i inne.

Wszystkie widoki implementują ten sam interface więc zmiana widoku na np. xml nie będzie wymagała żadnych zmian w kodzie php.

Wywołanie przykładowego widoku wygląda następująco

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$data  = Array( 
    
'name'       => 'Michał'
    
'surname'    => 'Środek'); 

// załadowanie widoku xhtml 
// jeżeli chcielibyśmy to zrobić w sposób jawny 
// $this->load('view', 'xhtml'); 

// ustawienie skórki "framework" 
$this->view->xhtml->setTheme('framework'); 
// przekazanie tablicy ze zmiennymi do widoku 
$this->view->xhtml->assign($data); 
// załadowanie oraz wyświetlenie szablonu "hello/index" 
$this->view->xhtml->loadAndDisplay('hello/index');

Wywołanie metody setTheme() jest zbędne jeśli wcześniej zadeklarowaliśmy odpowiednią skórkę w naszym pliku konfiguracyjnym w gałęzi <app>(patrz rozdział 6.).

Zawartość szablonu możemy także zapisać do zmiennej wykorzystując do tego metodę load().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$data  = Array( 
    
'name'        => 'Michał'
    
'surname'    => 'Środek'); 

// załadowanie widoku xhtml 
// jeżeli chcielibyśmy to zrobić w sposób jawny 
// $this->load('view', 'xhtml'); 
// ustawienie skórki "framework" 
$this->view->xhtml->setTheme('framework'); 
// przekazanie tablicy ze zmiennymi do widoku 
$this->view->xhtml->assign($data); 
// załadowanie oraz wyświetlenie szablonu "hello/index" 
$content $this->view->xhtml->load('hello/index'); 
echo 
$content;

Zawartość pliku szablonu ./application/theme/framework/hello/index.php może wyglądać następująco:

1
2
3
4
<p> Nazywam się <?php echo $this->name ?> <?php echo $this->surname ?> :).</p> 
<p> <?php _t('My name is %name% %surname%.', Array('name' => $this->name'surname' => $this->surname)); ?></p>
<p> <a href="<?php _link('hello','sunny'?>">Link 1</a></p> 
<p> <a href="<?php _url('hello','sunny'?>">Link 2</a></p>

Oczywiście jeżeli w powyższym przykładzie załadowalibyśmy odpowiedni plik z tłumaczeniem, fraza "My name is Michał Środek" zostałaby przetłumaczona

Budowa plików szablonowych jest bardzo prosta. Są to zwykłe pliki php z kodem HTML i kilkoma wstawkami php. Do zmiennych szablonowych odnosimy się bezpośrednio za pomocą echo lub innych funkcji php. Istnieje kilka funkcji pomocniczych:



10. Model

Modele odpowiadają za odczyt danych z baz danych, plików i innych miejsc oraz ich zwrócenie do controllera. Aktualnie istnieje tylko jeden model systemowy MoheboMysqliModel z którego mogą dziedziczyć modele aplikacji aby odczytać odpowiednie informacje z bazy danych.

MySQLi

Konfiguracja bazy MySQL jest opisana w rozdziale 2.

Aby stworzyć własny model o nazwie nowy stwórzmy plik ./application/sets/default/model/NowyModel.php o treści:

1
2
3
4
5
6
7
8
9
10
11
12
<?php 
class NowyModel extends MoheboMysqliModel

    public function 
getData() 
    { 
        
$result $this->db->query('SELECT * FROM '.$this->prefix.'tabela'); 
        while(
$row =  $result->fetch_assoc()) 
            
$data['records'][] = $row

        return 
$data
    } 
}

Zauważ, że nazwa klasy/pliku modelu to nazwa modelu z dodanym ciągiem Model.

Wewnątrz klasy NowyModel możemy korzystać z obiektu mysqli, który jest zapisany w $this->db. Jeżeli chcemy użyć prefiksu tabel(który został zadeklarowany w pliku konfiguracyjnym) jest on dostępny jako zmienna $this->prefix.

Wywołanie tego modelu z poziomu kontrolera wyglądałoby następująco:

1
2
3
// załadowanie modelu 
// $this->load('model','nowy'); 
$mojeDane $this->model->nowy->getData();


W przyszłych wersjach Mohebo Framework przewidziana jak obsługa PDO(od wersji 0.5 lub 0.6) oraz Doctrine.


11. MoheboInput

Mohebo Framework zawiera klasę MoheboInput(ładowana automatycznie do $this->input), która wspomaga filtrowanie oraz sprawdzanie poprawności danych.

Walidatory

Przejdźmy od razu do przykładu:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// deklarujemy listę wymagań 
$validators = Array( 
    
'*'            =>    Array( 
                    
'required' => true 
                    
), 
    
'title'        =>    Array( 
                    
'minlength' => 5
                    
'maxlength' => 80 
                    
), 
    
'subtitle'    =>    Array( 
                    
'minlength' => 
                    
), 
    
'subtitle2'    =>    Array( 
                    
'minlength' => 25
                    
'required' => false
                    ) 
); 
// wrzucamy nasze wymagania do klasy input 
$this -> input -> setValidators($validators); 

//deklarujemy dane wejściowe 
$dane = Array( 
    
'title'        => 'Jakiś tytul'
); 
// wrzucamy nasze dane do klasy input 
$this -> input -> assignData($dane); 

// sprawdzamy poprawność danych 
echo '<h1>Proba pierwsza</h1>'
echo 
'title => '.(integer) $this -> input -> isValid('title').'<br/>'
echo 
'subtitle => '.(integer) $this -> input -> isValid('subtitle').'<br/>'
echo 
'subtitle2 => '.(integer) $this -> input -> isValid('subtitle2').'<br/>'

// zmieniamy wartości danych 
$dane = Array( 
    
'title'        => 'Jakiś tytul'
    
'subtitle'    => 'Jakiś tytul 2'
    
'subtitle2'    => 'Za krótki tytuł'
); 

// wrzucamy nasze dane do klasy input 
$this -> input -> assignData($dane); 

// ponownie sprawdzamy poprawność danych 
echo '<h1>Proba druga</h1>'
echo 
'title => '.(integer) $this -> input -> isValid('title').'<br/>'
echo 
'subtitle => '.(integer) $this -> input -> isValid('subtitle').'<br/>'
echo 
'subtitle2 => '.(integer) $this -> input -> isValid('subtitle2').'<br/>';

Wynikiem wykonania powyższego kodu będzie:

1
2
3
4
5
6
7
8
9
Proba pierwsza 
title => 1 
subtitle => 0 
subtitle2 => 1 

Proba druga 
title => 1 
subtitle => 1 
subtitle2 => 0

Przeanalizujmy kolejne kroki. Najpierw deklarujemy grupę walidatorów i przekazujemy je do obiektu $this->input. Klucze tablicy odpowiadają nazwom zmiennych(za wyjątkiem klucza *, który deklaruje wymagania globalnie, dla wszystkich zmiennych) , które przekażemy później za pomocą metody assignData(). Dostępnych jest kilka wymagań dot. wartości naszej zmiennej:

Dosyć specyficznym parametrem jest parametr type. Może on przyjmować jedną z dostępnych wartości lub obiekt określający nowy typ(o tym niżej).

Dostępne typy to:

Po zadeklarowaniu danych oraz parametrów, wg. których te dane mają być sprawdzane możemy przejść do sprawdzania. Służy do tego funkcja isValid().

Jeśli jako parametr przekażemy jej nazwę zmiennej, sprawdzi poprawność tylko dla niej i zwróci wynik. Gdy pominiemy parametr funkcja zwróci wartość logiczną poprawności wszystkich danych.

Obsługa błędów

W przypadku wykryciu błędnych wartości klasa MoheboInput generuje odpowiednią tablicę $this->input->errors zawierającą komunikaty błędów dla poszczególnych zmiennych.

1
print_r($this->input->errors);

Wywołanie tego kodu dla naszego przypadku spowodowało by wyświetlenie:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Array 

    [subtitle2] => Array 
    ( 
        [0] => Array 
        ( 
            [0] => "%value%" is too short(%lenght% chars). Required min. %minlength% chars. 
            [1] => Array 
            ( 
                [minlength] => 25 
                [length] => 17 
                [value] => Za krótki tytuł 
            ) 
        ) 
    ) 
)

Tak przygotowany komunikat wraz z parametrami, idealnie nadaje się do użycia w klasie językowej MoheboTranslate.

Planowane jest wprowadzenie prostszej obsługi błędów klasy MoheboInput w widoku aplikacji.

Deklarowanie własnych typów

Klasę można rozszerzać, dzięki czemu możliwe jest sprawdzanie różnych innych, bardziej skomplikowanych zależności. Jako przykład posłużę się klasa do sprawdzania peselu.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class SprawdzPesel extends MoheboValidate implements MoheboValidateInterface

    public 
$errors = Array(); 
    public 
$value

    public function 
isValid() 
    { 
        
$pa str_split($this -> value); // pesel array 
        
return (($pa[0] + $pa[1]*$pa[2]*$pa[3]*$pa[4] + $pa[5]*$pa[6]*$pa[7]*$pa[8] + $pa[9]*$pa[10])%10 == 0);
    } 

    public function 
isMale() 
    { 
        
$pa str_split($this -> value); // pesel array 
        
return $pa[9]&== 1
    } 

    public function 
isFemale() 
    { 
        
$pa str_split($this -> value); // pesel array 
        
return $pa[9]&== 0
    } 
}

Klasa naszego walidatora musi implementować interface MoheboValidateInterface(w skrócie musi zawierać metodę isValid()). Oprócz metody głównej isValid() nasza klasa może zawierać kilka metod dodatkowych. Jeśli któraś z nich będzie potrzebowała wartości zmiennej, jest ona cały czas przechowywana w $this->value. Nasza klasa może także zwracać błędy do $this->errors.

Użycie naszej klasy SprawdzPesel:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
 
/* 
Wymagania: 
- title nie jest wymagany, jeśli jest podany musi być w formacie: "(numer). (tytuł)" 
   oraz o minimalnej długości 10 znaków 
- email nie jest wymagany, jeśli jest podany musi byc poprawnym adresem e-mail 
- pesel jest wymagany, osoba musi byc urodzona miedzy 1940 a 1950 rokiem 
- liczba nie jest wymagana. Jeżeli zostanie przekazana musi być liczbą całkowitą 5 lub 25 
*/ 
$validators = Array( 
    
'title'    =>   Array( 
                    
'minlength' => 10
                    
'regexp' => '/^d+. .+$/i' 
                    
), 
    
'email'    =>   Array( 
                    
'type' => 'email' 
                    
), 
    
'pesel'    =>   Array( 
                    
'type' => new SprawdzPesel() 
                    
'between' => Array(40000000000,50000000000), 
                    
'required' => true 
                    
), 
    
'liczba'   =>   Array( 
                    
'type' => 'integer'
                    
'in' => Array(5,25), 
                    ) 
); 

$dane = Array( 
    
'title'        => '23. Tytuł naszego artykułu'
    
'email'        => 'adres@example.com'
    
'pesel'        => 44051401458
    
'liczba'        => 45 
); 

// wrzucamy nasze dane do klasy input 
$this -> input -> assignData($dane); 
$this -> input -> setValidators($validators); 

// Sprawdzmy poprawność danych 
echo 'Wszystkie => '.(integer) $this -> input -> isValid().'<br/>'
echo 
'pesel => '.(integer) $this -> input -> isValid('pesel').'<br/>'
echo 
'Czy mężczyzna => '.(integer) $this -> input -> isMale('pesel').'<br/>'
echo 
'Czy kobieta => '.(integer) $this -> input -> isFemale('pesel').'<br/>';

Filtry

Na podobnej zasadzie działają filtry. Dzięki nim możemy w prosty sposób przekształcić wartości zmiennych.

Użycie jest bardzo proste:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$filters = Array( 
    
'title' => Array( 
        
'alphanumeric' => true
    ), 
    
'pesel' => Array( 
        
'numeric' => true 
    

); 

$dane = Array( 
    
'title'        => '23. Tytuł naszego artykułu'
    
'pesel'        => '44051401458d' 
); 

$this -> input -> setData($dane); 
$this -> input -> setfilters($filters); 

// Pobranie przefiltrowanej zmiennej title 
echo $this -> input -> get('title'); 

// Pobranie przefiltrowanych wszystkich zmiennych 
print_r($this -> input -> getFilteredData());

Dostępne filtry:

Obsługa filtrów jest na wstępnym poziomie implementacji i ma wiele braków. Więcej opcji przewiduję w przyszłych wersjach Mohebo Framework



12. Uprawnienia

Framework Mohebo posiada system uprawnień oparty o przyznawanie lub zabranianie praw dostępu do poszczególnych akcji, całych kontrolerów, a nawet zbiorów kontrolerów. Konfiguracja polega(w aktualnej wersji, w przyszłych planuję to usprawnić) na ręczej edycji pliku ./application/config/privileges/default.php

1
2
3
4
$privileges = Array( 
    Array(
'panel',    '*'01ALLOW),    // wszystkie kontrollery "panel" są dostępne dla użytkownika o ID = 1
    
Array('panel',    '*'00DENY)    // wszystkie kontrollery "panel" są zabronione dla wszystkich 
);

Jak widzisz konfiguracja to zbiór tablic o kolejnych wartościach

Jeżeli określiliśmy ID użytkownika oraz ID grupy jednocześnie to wpis jest aktywny tylko dla użytkownika o podanym ID jeżeli należy od do tej grupy(czyli ID grupy również się zgadza). Jeżeli ID grupy oraz ID użytkownika zostały oznaczone jako 0 to dany wpis dotyczy wszystkich użytkowników. Kolejność wpisów nie ma znaczenia(następuje automatyczne sortowanie). Zawsze są one ustalane od przypadków najbardziej ogólnych do najbardziej szczegółowych. Domyślnie wszystko jest dostępne dla wszystkich.

W powyższym przykładzie, mimo odwrotnej kolejności, framework najpierw zablokowaliśmy dostęp(2 wpis) wszystkim użytkownikom do kontrolerów panel(a więc również do wszystkich istniejących kontrolerów pochodnych typu panel-news, panel-users etc.) a następnie nada prawa(wpis 1) dostępu do tych kontrolerów dla użytkownika o ID równym 1.

Klasa ta jest najnowszym elementem frameworka. Przewiduję liczne zmiany oraz jej szybką rozbudowę.



13. Przykładowy kontroler w Mohebo Framework podsumowujący podręcznik

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php 
class ArticleController extends MoheboController
{
    public function 
indexAction()
    {
        
// zaladowanie pliku z tlumaczeniem
        
$this -> translate -> loadFile('article.xml');

        
// pobranie listy artykułów
        
$data $this -> model -> article -> getArticles();

        
// przekazanie danych do widoku i wyswietlenie całości
        
$this -> view -> xhtml -> assign($data);
        
$this -> view -> xhtml -> loadAndDisplay('article/list');
    }

    public function 
showAction()
    {
        
// pobranie identyfikatora z adresu
        // np. http://localhost/MoheboFramework/article/53/The-Matrix-Reloaded.html
        
$urlData $this -> router -> generateVariables(Array(
            
'id'    =>    'numeric'
        
));

        if(isset(
$urlData['id']))
        {
            
// zaladowanie pliku z tlumaczeniem
            
$this -> translate -> loadFile('article.xml');

            
// zaladowanie danych z modelu
            
$data $this -> model -> article -> getArticle($urlData['id']);

            
// przekazanie danych do widoku i wyswietlenie całości

            
$this -> view -> xhtml -> assign($data);
            
$this -> view -> xhtml -> loadAndDisplay('article/show');
        }
        else
            
$this -> router -> redirect(); // nie przekazano ID - przekierowanie na stronie główną
    
}
}
?>

Obserwuj mój blog: srodek.info. Staram się umieszczać tam artykuły związane z użyciem Mohebo Framework.

14. Na zakończenie

Prośba o wyrozumiałość

Aktualna wersja 0.3 Mohebo Framework jest drugim publicznym wydaniem. Jestem świadom, iż nie zawiera ona wielu potrzebnych(jak np. autoryzacja, system wyszukiwania), ciekawych(jak np. openID) i zapowiadanych opcji. Daję słowo, że te braki postanowię jak najszybciej załatać ;). Potrzebuję jednak konstruktywnej krytyki aby odpowiednio określić priorytety zadań.

O autorze

Głównym autorem i pomysłodawcą jest Michał Środek. Wszelkie propozycje zmian, zgłoszenia błędów, propozycje współpracy etc. proszę kierować na adres: michal(piękna brązowa małpka) srodek info

Licencja

Mohebo Framework jest rozprowadzany na licencji New BSD

Copyright (c) 2008-2010 Michał Środek
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.



Generation Time: 0.292792 sec