

Mohebo Framework 0.2
(183KiB)
Ten krótki podręcznik ma na celu szybkie i jednocześnie dokładne zapoznanie się z możliwościami Mohebo Framework. Całość jest opublikowana na licencji GNU GPL.
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 Smarty jako jeden z możliwych do użycia widoków. Framework nie posiada obsługi plików po, ini, yaml etc. lecz jest możliwość jej łatwej implementacji. Konfiguracja oraz tłumaczenia są oparte o pliki xml.
./application - katalog główny aplikacji
./application/cache - pamięć podręczna, zawartość generowana dynamicznie
./application/config - pliki konfiguracyjne aplikacji
./application/controller - pliki kontrolerów
./application/model - pliki modeli
./application/translations - tłumaczenia
./system - rdzeń, oraz biblioteki Mohebo Framework
./system/core - główny rdzeń Mohebo Framework
./system/functions - funkcje rozszerzające braki php
./system/helper - pomocniki
./system/lib - biblioteki wspomagające pracę
./system/model - sterowniki modeli
./system/view - pliki widoków
./theme - pliki szablonów
Zalecane jest czytanie kolejnych rozdziałów po kolei. Większość z nich jest jednak całkowicie rozdzielna więc przeskakiwanie o kilka rozdziałów przód nie powinno znacznie dezorientować czytelnika
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/framework.
Czytelnik powinien znać przynajmniej główne założenia wzorca MVC
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
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 ./cacheAby Mohebo Framework sprawnie obsługiwał przyjazne linki ważne jest określenie odpowiedniej ścieżki do Mohebo Framework w pliku .htaccess
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
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/controller/default/hello.php o treści:
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!.
3. Schemat działania Mohebo Framework
W skrócie: kontroler to klasa, która pobiera odpowiednie dane, filtruje, przetwarza i przekazuje je do widoku. 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
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
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.
Przejdźmy do analizy metod dziedziczonych z klasy MoheboController
bool $this -> load(string $type, string $name, [string $accessName, [mixed $option]])
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:
Po tej operacji otrzymujemy prosty dostęp do obiektu modelu po przez $this -> model połączony z ustaloną nazwą dostępową.
Analogicznie postępujemy z innymi elementami(za wyjątkiem bibliotek, do których dostęp jest bezpośredni)
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.
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, [mixed $option]])
Wszystkie parametry są identyczne jak w metodzie load(). Inny jest jednak sposób dostępu do obiektu
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.
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.
4. Pierwsza aplikacja - Hello World!
6. Konfiguracja za pomocą Mohebo Config
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:
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:
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:
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 routera może zawierać kilka specyficznych ustawień, wszystkie powinny znajdować się w prostych tablicach php.
Krótkie wytłumaczenie. Adres naszej strony to http://localhost/MoheboFramework/ więc katalogiem głównym jest /MoheboFramework/. Ważny jest tutaj znak / na końcu. 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.
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.
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
W podobny sposób działają aliasy. Edytuj plik konfiguracyjny.
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
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
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:
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.
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:
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
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.
6. Konfiguracja za pomocą MoheboConfig
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.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
Zapiszmy plik jako ./application/translations/pl/naszplik.xml. Wywołanie powyższego tłumaczenia z poziomu kontrolera wyglądałoby następująco:
Wynikiem będzie oczywiście napis Dzisiaj
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.
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.
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 pliki w ./system/lib
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)
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
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().
Zawartość pliku szablonu ./theme/MoheboFramework/hello/index.php może wyglądać następująco:
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:
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 MoheboMysqli z którego mogą dziedziczyć modele aplikacji aby odczytać odpowiednie informacje z bazy danych.
Konfiguracja bazy MySQL jest opisana w rozdziale 2.
Aby stworzyć własny model o nazwie nowy stwórzmy plik ./application/model/nowy.php o treści:
Zauważ, że nazwa klasy 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:
W przyszłych wersjach Mohebo Framework przewidziana jak obsługa modeli ORM.
Mohebo Framework zawiera klasę MoheboInput(ładowana automatycznie do $this -> input), która wspomaga filtrowanie oraz sprawdzanie poprawności danych.
Przejdźmy od razu do przykładu:
Wynikiem wykonania powyższego kodu będzie:
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.
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.
Wywołanie tego kodu dla naszego przypadku spowodowało by wyświetlenie:
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.
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.
Klasa naszego walidatora musi implementować interface MoheboInputValidator(w skrócie musi zawierać metodę isValid()). Oprócz metody głównej isValid() nasza klasa moje 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:
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:
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
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
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.
13. Przykładowy kontroler w Mohebo Framework podsumowujący podręcznik
Aktualna wersja 0.2 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ń.
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
Mohebo Framework jest rozprowadzany na licencji MIT
Copyright (c) 2008-2010 Michał Środek
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14. Przykładowy kontroler w Mohebo Framework podsumowujący podręcznik