Michał Matyas

Prywatny blog webdevelopera traktujący o tworzeniu i projektowaniu stron i serwisów internetowych.

Podstawy frameworka Kohana, część 2

W poprzedniej części “Podstaw” pisałem o strukturze frameworka, podstawowej konfiguracji i uruchomieniu kohanowego odpowiednika “Hello World”. W tej części skupimy się na konkretniejszej konfiguracji naszego podstawowego projektu, nauczymy się jak wygląda kontroler, model i widok i nabierzemy pojęcia jak właściwie to wszystko powinno być ze sobą posklejane.

Więcej na temat konfiguracji

Wspominałem już, że większość plików konfiguracyjnych znajduje się w system/config, a tylko te, które będziemy zmieniać kopiuje się do application. Wbrew pozorom jest to dobre rozwiązanie i nie warto każdego projektu zaczynać od skopiowania wszystkich plików w razie ewentualnej edycji, chociażby dlatego, że nie z całej funkcjonalności będzie wykorzystywana w każdym projekcie. Przykładowo prosty skrypt blogowy raczej nie potrzebuje modułu Payment albo własnej konfiguracji helpera Inflector.

Są jednak takie pliki konfiguracyjne, które śmiało możemy przerzucać bezpośrednio do application/config zaraz po rozpoczęciu pracy nad projektem. Mówiłem o nich już, ale powtórzę - database, config, locale, routes. Nie będę opisywał wszystkich wymienionych plików, ponieważ są same w sobie odpowiedno dobrze pokomentowane (zwłaszcza ten drugi). Skupię się tylko na ostatnim z nich, gdyż jest najciekawszy.

Routes.php, niepozorna nazwa, jednakże bardzo często będzie to najważniejszy plik w całym projekcie. Odpowiada on jak sama nazwa wskazuje za ‘routing’ czyli przekierowywanie odpowiednich adresów do odpowiednich kontrolerów i ich metod. Konstrukcja samego pliku jest bardzo prosta: składa się on z kilku, czasem nawet do kilkudziesięciu przy bardziej skomplikowanych projektach elementów tablicy $config. Zasada jest taka - klucz tablicy to wyrażenie regularne, którym zostanie potraktowany adres, a jej wartość to wewnętrzne URI, do którego zostanie przekierowane pasujące żądanie. Jest też pozycja specjalna, _default, oznaczająca stronę, która ma zostać wyświetlona gdy w adresie nie znajduje się żaden kontroler.

Słowo o wewnętrznym URI - w najprostszym przypadku jest to po prostu konstrukcja kontroler/metoda/zmienna1/…/zmiennaN. Będę o tym mówił nieco szerzej już za chwilę, chwilowo po prostu przyjmijcie, że to tak działa.

W kwestii routingu należy pamiętać, że ważna jest kolejność z jaką przypisujemy wartości. Kohana nie poukłada ich niestety za nas sama, musimy więc pamiętać, by najbardziej ogólne klucze umieszczać najniżej. Posłużę się przykładem z własnego projektu - serwis posiada podstrony z adresem generowanym na podstawie tytułu w bazie danych. Na strony wchodzi się poprzez adres.pl/tytul-podstrony (zauważcie brak widocznego ID - takie było życzenie klienta), jednakże niektóre z podstron są generowane przez odpowiednie kontrolery. Rozwiązałem to przypisując tym kilku kontrolerom routing zbliżony do domyślnego, a resztę traktując bardzo ogólnym wyrażeniem regularnym. Gdybym zrobił to odwrotnie, podstrona admin zostałaby złapana do klucza ogólnego i przekierowana jako main/index/admin, a tego nie chciałem. Może kod wyjaśni więcej:

    $config['_default'] = 'main';
    $config['admin'] = 'admin/index';
    $config['admin/(.+?)'] = 'admin/$1';
    $config['ajax/(.+?)'] = 'main/ajax/$1';
    $config['(.+?)'] = 'main/index/$1';

Jak widać po drugiej linijce, routowanie można wykorzystać również w sytuacji gdy chcemy uniknąć wyświetlania w adresie kontrolera index(). W przypadku bardziej skomplikowanego kodu stosuje się metodę, którą przedstawię za moment.

Tworzenie kontrolera oraz widoku

Przebrnęliśmy już przez podstawy konfiguracji więc czas zabrać się za kontroler. Istnieją dwa sposoby tworzenia kontrolerów w Kohanie - za pomocą rozszerzania klasy Controller oraz drugi, dodatkowy - za pomocą klasy Template_Controller.

Zanim przejdę jednak do opisywania bardziej zaawansowanych aspektów, wrócmy do podstaw. Jak już wiemy, kontroler służy do kontrolowania tego całego bałaganu, który by się utworzył, gdybyśmy trzymali kod w jednym miejscu. Przyjmuje on od użytkowników różne prośby o dane (czy to POST, czy to GET, czy nawet XHR), idzie do modelu, stuka w szybkę, prosi o to i o to, a następnie wraca, podaje to widokowi, a ten wyświetla użytkownikowi to, czego chciał się dowiedzieć. Kontroler w Kohanie zawsze dziedziczy z klasy Controller, bezpośrednio lub pośrednio, powinen też posiadać metodę index(), która wykona się po wejściu na adres kontrolera. Kolejne podstrony danego kontrolera to właśnie kolejne metody, a nazwa metody jest częścią składową adresu (o czym było przed chwilą). Kontroler sam z siebie nie zwraca odpowiedniego widoku, od tego jest osobna klasa - View. O jej wykorzystaniu powiem w dalszej części tego tekstu. Teraz trochę o drugiej (poza bezpośrednim dziedziczeniem z Controller) metodzie tworzenia kontrolerów.

klasa abstrakcyjna - klasa, której instancji nie można utworzyć, ale może być dziedziczona przez normalne klasy. Nie należ jej mylić z interfejsem, który dostarcza jedynie nazwy metod wewnątrz klasy, ale nie może posiadać żadnego kodu.

Przede wszystkim różnice - Template_Controller jest klasą abstrakcyjną, więc nie może zostać bezpośrednio wywołany. Został więc umieszczony razem z innymi kontrolerami, żeby nie tworzyć dodatkowych regułek w samym frameworku, przez co robi trochę bałaganu (który można uniknąć podkatalogami, o czym niżej). Tak naprawdę nie różni się on wiele od standardowego kontrolera i dziedziczy również z Controller, jednakże posiada dodatkową zaletę, którą przydaje się wykorzystywać - pozwala na wykorzystanie gotowego szablonu i rozdzieleniu strony na mniejsze elementy.

Przedstawiony powyżej rysunek ukazuje jeden ze sposobów tworzenia szablonu strony. Osoby korzystające wcześniej z systemów szablonów typu Smarty zapewne już wiedzą do czego zmierzam. Podczas pisania pierwszych aplikacji zapewne korzystaliście z najczęściej pokazanej w kursach metody, tzn. include’owaliście najpierw nagłówek strony, następnie umiesczaliście treść tego co chcecie uzyskać, a na końcu doklejaliście stopkę.

Nie chcę demonizować tego rozwiązania, ale posiada ono szereg poważnych wad. Przede wszystkim wprowadza nam dodatkową warstwę do martwienia się - jeśli chcemy zmienić szablon tylko dla jednego kontrolera to żaden problem, tworzymy header_2.tpl i footer_2.tpl i sprawa załatwiona. Co jednak jeśli chcemy zmienić potem ten szablon w połowie widoków?

Oczywiście można to hackować mniej lub bardziej paskudnymi sposobami, ale pozostaje to jednak hackiem. Poza tym przy tworzeniu kolejnych elementów szablonu okazuje się, że kod staje się niesamowicie powtarzalny - cały czas operujemy przecież na podobnych zestawach elementów. Gdy mamy złożony cały serwis, dla przykładu sklep internetowy, wyświetlanie jednego produktu jest powtórzone w kilkunastu miejscach (np. w kontrolerze do obsługi wyświetlania po kategorii czy w wyszukiwarce). Wprowadzanie jakichkolwiek zmian w wyglądzie, który przewija się przez całą stronę w kilkunastu plikach jest cholernie denerwujące.

Z tego też powodu ktoś wymyślił, a inni podchwycili znacznie lepszy sposób tworzenia stron. Można to porównać do papierowej wycinanki - na obrazku element podpisany layout jest kartką papieru, section to duże skrawki kolorowych kartek, a box to mniejsze, odrębne kawałeczki. Jeśli zamiast posklejać obrazek po prostu położymy na nim szybę (czyli zamiast wsadzić wszystko w jeden plik poskładamy sobie szablon z tych kawałków) to późniejsze wykorzystanie tych samych elementów do stworzenia nowej pracy o podobnej kompozycji nie nastręcza większych problemów.

Niecierpliwym w końcu wyjaśniam - layout jest szkieletem strony HTML, zawiera DOCTYPE, sekcję HEAD gdzie wpisane są adresy do styli i skryptów, ogółem najbardziej podstawowy szablon. Może zawierać też elementy powtarzające się na wszystkich stronach, np. logo projektu, górne menu (chociaż takie rzeczy lepiej zrobić… dobra, bez dywagacji, zaraz napiszę). Section to właściwa część strony, układ elementów, ich dokładniejsze rozmieszczenie, wszystko to co tworzy stronę. Box to właśnie te małe skrawki, które będą częściej wykorzystane. Nie należy jednak popadać w paranoję i ”boksować” wszystkiego. Tabelka, która znajduje się na jednej stronie kandydatem na box nie jest, ale już na przyład wygląd wpisu na blogu owszem.

Ufff, przebrnęliśmy przez widok, jak zwykle przeskakując od tematu do tematu. Wróćmy do kontrolera - mówiłem już, że Template_Controller pozwala na wykorzystanie szablonów, a w konsekwencji i całego tego skomplikowanego układu, o którym mówiłem powyżeej. Znajduje się on w system/controllers, ale nie radzę go kopiować, a wykorzystywać metodę, za pomocą której został stworzony.

Przypomnienie dla nieuważnych w nauce - konstrukcja parent pozwala na odwołanie się do metody rodzica danej klasy. Jeśli nadpisujemy którąś z metod klasy, z której dziedziczymy, ale chcemy jedynie rozszerzyć jej możliwości, możemy wykonać kod z metody rodzica za pomocą parent::nazwametody(). Najczęściej stosuje się to przy nadpisywaniu kontruktora, ale nie tylko.

Przykład - mamy panel administracyjny. Chcemy, żeby po wejściu do panelu użytkownik bez hasła był przekierowywany na inną stronę. Teoretycznie - proste, wystarczy wrzucić to w __construct() i po sprawie. Co jednak jeśli panel składa się z więcej niż jednego kontrolera? Powtarzanie kodu jest złem, co mówi nam ważna zasada DRY (Don’t Repeat Yourself). Znacznie lepszym rozwiązaniem jest stworzenie w tym momencie abstrakcyjnej klasy Admin_Template_Controller dziedziczącej z Template_Controller i posiadającej w konstruktorze wymieniony kod (i co bardzo ważne parent::__construct() na początku lub końcu - łatwo o tym zapomnieć). Możemy wykorzystać tą sytuację również do generowania górnego menu, o czym wspominałem przed chwilą.

Jeśli chodzi o kontrolery w Kohanie to jest jeszcze jedna, bardzo przydata funkcjonalność, o której w dokumentacji jest tylko szybka wzmianka, więc łatwo ją przeoczyć. Otóż Kohana nie zabrania tworzenia katalogów w controllers, a nawet wspiera korzystanie z nich. Przydaje się to bardzo przy grupowaniu właśnie takich rzeczy jak panel administracyjny. Wystarczy stworzyć katalog admin, wrzucić do niego kontrolery od panelu administracyjnego i utworzyć dodatkowo w głównym katalogu kontroler admin.php zawierający to, co pojawi się po wejściu na adres strona.pl/admin . Kontrolery w katalogu będą uruchamiane po wejściu w admin/nazwakontrolera i zachowywały się od tego miejsca jak na grzeczne kontrolery przystało. Również wewnętrzne URI będzie skonstruowane w ten sam sposób, o czym wspominałem wyżej - konstrukcja zmieni się po prostu na katalog/kontroler/metoda/zmienna1/…/zmiennaN.

Jak już pewnie zauważyliście, staram się unikać listingów kodu. Zaciemniają one obraz i powodują bezmyślne kopiowanie, ponieważ prawdziwa nauka powinna być na przykładach. Z tego też powodu kodu kontrolera czy widoku nie umieszczam tutaj nigdzie i go nie tłumaczę, ponieważ bez podstawowej wiedzy o obiektowym programowaniu z frameworkami nie ma co zaczynać, a ktoś kto już tą wiedzę posiada może swobodnie przeanalizować gotowe po instalacji przykłady.

Tworzenie modelu

Najwyższy czas na danie główne - model. Jak już wielokrotnie wspominałem z punktu widzenia danych oraz informacji, model jest najważniejszym elementem każdego projektu napisanego w Kohanie, ale również w innych frameworkach. Właściwie to ON jest aplikacją, ponieważ to w nim znajduje się cała logika aplikacji, obliczenia, pobieranie danych - słowem wszystko to bez czego moglibyśmy stworzyć najwyżej prostą, statyczną stronę.

Model tak samo jak kontroler jest zwykłą klasą, możemy z niego dziedziczyć, tworzyć rozszerzone wersje modeli i tak dalej. Opisywanie tego drugi raz nie ma sensu, ponieważ sposób działania jest ten sam. Niestety w przypadku modeli tak jak jest to z kontrolerami i widokami, nie możemy tworzyć podkatalogów (a przynajmniej ja nie znalazłem na to sposobu). Trochę to moim zdaniem nieprzemyślane rozwiązanie, ale nic na to nie poradzimy. Przynajmniej w ramach ułatwienia życia developerom, Kohana w domyślnej klasie modelu tworzy instancję klasy Database w $this->db, dzięki czemu nie musimy o tym sami pamiętać.

Cenna uwaga jeśli chodzi o model - starajmy się go używać tylko do jednej rzeczy. Kuszące może się wydawać wrzucenie kilkudziesięciu metod obsługujacych różne części strony w jeden model (odpowiednik functions.inc.php czy innego potworka znanego ze starych skryptów i jeszcze starszych kursów), ale to naprawdę nie jest rozwiązanie. Jeśli musimy wykonać operacje, które nie dotyczą logiki aplikacji, a jedynie mają usprawniać pracę nad projektem (np. generowanie linków, generowanie tabelek), znacznie lepiej jest wykorzystać do tego helper albo napisać bibliotekę. Zaśmiecanie kodu jest be.

Temat sposobu pisania modelu, nazywania metod i konwencji z tym związanych jest szeroki jak rzeka i wykracza nieco poza tą serię (PODSTAWY frameworka), ale jestem pewien, że napiszę na ten temat wpis albo dwa gdy poczuję się odpowiednio bezczelny, by zacząć się uważać za alfę i omegę w tym temacie. W międzyczasie polecam przejrzeć po prostu inne klasy zawarte w Kohanie albo wyrobić sobie własną konwencję, która z czasem będzie ewoluować w kierunku bardziej ustandaryzowanego rozwiązania.

Słowa końcowe

Wpis wyszedł nieco dłuższy objętościowo, a mniej bogato w treść niż początkowo planowałem, ale jeśli zacznę już następny temat, równie dobrze mogę zacząć pisać książkę. Gdybym był nauczycielem to w ramach zadania domowego kazałbym pomyszkować w domyślnym kontrolerze oraz widoku, a także modelach z modułu Auth (są dość wdzięczne w tej materii). Poza tym polecam korzystać z oficjalnej dokumentacji Kohany, ponieważ ten tekst powinen być traktowany jako suplement niż jej zastępstwo. Powodzenia w kodzeniu! :)

  1. michal-matyas posted this
More Information