Poprzednia część jest tutaj.

PHP i Kohana

Czyli część przez niektórych bardziej oczekiwana ^^. Jak wiadomo język PHP jest niezwykle zwinny i nieobligujący dla programisty (przy okazji nieco “brudny”, ale to szczegół). Ponadto użycie frameworka Kohana (w wersji 2.3.4; wersji 3 nigdy nie używałem w żadnym  większym projekcie) daje dodatkowe możliwości przy jednakowych niewielkich ograniczeniach. Oznacza to dla nas bardzo duże pole do popisu w kwestii generalizowania różnych rzeczy (przede wszystkim kontrolery, widoki). Innymi słowy bardzo przyjemnie robi się tutaj różnego rodzaju panele administracyjne przy względnie niewielkim nakładzie pracy. Mając odrobinę wyobraźni można ograniczyć ilość widoków do minimum. Dużą zaletą jest fakt, że formularz (jeśli nie potrzebuje jakichś drastycznych modyfikacji w html) jest przygotowywany w kontrolerze, po czym jest renderowany do kodu html i wrzucany do jakiegoś widoku generycznego. Tak to powinno właśnie wyglądać. W poprzednim wpisie nie przedstawiłem może wystarczająco dosadnie podejścia ASP.NET w tej kwestii. Podejście to bowiem jest zgoła odmienne. Według zaleceń i dobrych praktyk Microsoftu, każda akcja w każdym kontrolerze ma mieć swój niezależny widok, a ponadto formularze mają być tworzone po stronie widoku tylko. Jest to coś czego bardzo nie lubię, bo po pierwsze jest to nadmiarowa praca do wykonania, do tego skutecznie eliminuje prostą generalizację. PHP oczywiście nie jest doskonałe i rozwiązania zaprezentowane w Kohanie wnoszą poza innowacyjnością też trochę problemów i bugów, które są naprawdę ciężkie w debugowaniu. Zdarza się problem (tu akurat wynikły z modułu formo w Kohanie), że tworzenie bardzo prostego formularza dla tabeli w bazie zawierającej jedno edytowalne pole generuje w ogóle nie działający formularz albo, w gorszym przypadku, dość zagadkowy Internal Error. Niby normalka, ale deadline jest na wczoraj… I mamy problem. Zostawmy jednak te przyziemne problemy i zobaczmy niektóre metodyki, które uznałem za użyteczne i generalnie zbiegające się z tym czym MVC być powinno. Pomijam te rozwiązania, które przedstawiłem w tym poście na temat automatycznego generowania formularzy. Najpierw – rzecz trywialna – przepływ sterowania i renderowania widoków. Kohana daje pełna kontrolę nad tym co, jak i kiedy, wobec tego można skutecznie umiejscowić całe budowanie widoków w kontrolerze (i jest to moim zdaniem miejsce idealne). Ot, przykładowe rozwiązanie:

<?php defined('SYSPATH') or die('No direct script access.'); 

class SampleAdmin_Controller extends Controller  
{ 
    protected $template; 

    public function __construct() 
    { 
        parent::__construct(); 
        $this->template = new View('admin_template');
    } 

    public function index()
    { 
        $listView = new View('admin/admin_list');
        $listView->thead = array('ID', 'Tytuł pol', 'Tytuł ang', 'Data utworzenia', 'Data edycji'); 
        $listView->tbody = $this->model->getAll(); 
        $listView->idName = $this->tableId; 
        $listView->rowValues = array($this->tableId, 'title_pl', 'title_en', 'date_added', 'date_edited'); 
        $listView->link = url::site($this->siteUrl).'/'; 
        $listView->downButtons = '<a class="awesome black" href="'.url::site($this->siteUrl.'/add/').'">Dodaj nowego newsa</a>'; 
        $this->template->content = $listView; 
        $this->template->content->render(); 
        $this->template->render(true); 
    } 
} 

?>

Jak można zauważyć, korzystając z PHP częściej skłaniam się ku MVP niż MVC. Jest to kwestia przyzwyczajenia i kwestia wyboru sposobu rozwiązania problemu; ja preferuję taki, ale jeśli ktoś woli inne podejście to może korzystać też ze stricte MVC. U mnie widoki zawierają tylko prostą logikę, zmienne, które mogą być dowolnie uzupełniane, jakieś proste pętle, warunkowe pokazywanie różnych elementów, itd. Nic rewelacyjnego. Całe sterowanie, pobieranie danych i przypisywanie ich do konkretnych zmiennych – to robota dla kontrolera. Wszelkie grzebanie i składanie zapytań w celu uzyskania danych z jakiegoś źródła (źródeł) to praca modelu. Proste i całkiem logiczne. Ostatnio odkryłem też ciekawą alternatywę – moduł CRUD scaffold do Kohany (w wersji 2.3.4 i 2.4). Jest to odpowiednik ASP.NET Dynamic Data dla mojego ulubionego frameworka PHP. W praktyce oznacza to, że jedyne co musimy zrobić to opisać Model danych i nadpisać kilka zmiennych w kontrolerze. Nie piszemy przy okazji żadnych widoków oraz logiki do zarządzania nimi :) Poniżej zamieszczam kod, który generuje w pełni funkcjonalnego CRUDa do tabel:

<!-- model: -->  
<?php defined('SYSPATH') or die('No direct script access.');

class Address_Model extends ORM  
{ 
    public $formo_defaults = array
    (
        (
            'type'  => 'hidden',
            'required' => false
        ),
        'ulica' => array
        (
            'type'  => 'text',
            'required' => true
        ),
        'kod_pocztowy' => array
        (
            'type'  => 'text',
            'required' => true
        ),
        'nr_domu' => array
        (
            'type'  => 'text',
            'required' => true
        ),
        'nr_mieszkania' => array
        (
            'type'  => 'text',
            'required' => false
        ),
        'miasto' => array
        (
            'type'  => 'text',
            'required' => true
        ),
        'editedBy' => array
        (
            'type'  => 'text',
            'required' => false
        )
    );

    public function __construct($id = NULL)
    {
        $this->table_name = 'addresses';
        $this->primary_key = 'id_address';
        parent::__construct($id);
    }
}
?>

<!-- oraz kontroler: -->

<!--?php defined('SYSPATH') or die('No direct script access.');

class Address_Controller extends Crud_Scaffold_Controller  
{
    protected $model                        = 'address';
    protected $base_url                     = 'address';
    protected $model_friendly_name          = 'Adresy';
    protected $crud_listing_ordering_keys   = array('id_address', 'ulica');
    protected $crud_listing_display_keys    = array('id_address' =--> 'ID', 'ulica' =&gt; 'Ulica');
    protected $orm_primary_key              = 'id_address';
    protected $orm_form_loading_key         = 'id_address';
    protected $orm_reference_key            = 'id_address';
}

W rezultacie otrzymujemy coś podobnego, jak na poniższych obrazkach (styl domyślny z samego modułu). Tylko przed stosowaniem mała uwaga: moduł jest pisany z myślą o obsłudze relacji między tablicami i uwzględnieniu ich na formularzach; jednak to nie zawsze działa – w szczególności w formo, które jest używane pod spodem. Ja osobiście wyłączyłem obsługę habtm (plugin do formo, który jest skrótem od has and belongs to many) i cieszę się jedynie prostym jednotabelowym CRUDem :)
CRUD Scaffolding Kohana Example

CRUD Scaffolding Kohana Example 2

Podsumowanie

Trochę zeszło mi się z drugą częścią artykułu, ale to przede wszystkim z powodu pracy inżynierskiej i biurokracji jaka była z tym bezpośrednio związana, ale dzięki temu wspomniałem na temat CRUD Scaffoldera dla Kohany, którego znalazłem w międzyczasie. Gdyby nie to, pisałbym pewnie znowu na temat ASP.NET MVC 2 i kilku bajerach, jakie znalazłem podczas pisania / tworzenia projektu inżynierskiego, ale to zostawię na inną okazję. Inna sprawa, której nie poruszyłem w tym wpisie, a o której wypadałoby też  wspomnieć to Symfony, które jest generatorem back-endu, front-endu, ale jakoś nigdy nie miałem z nim większej styczności. Jakoś nie tworzę wielkich projektów, przy których Symfony byłoby jedynym sensownym wyjściem i dałoby się użyć wszystkich jego możliwości, ale może kiedyś…