Simple HMVC-widget in Kohana3

/ Просмотров: 21112

Русскоязычные читатели могут пропустить этот пост, поскольку это всего лишь попытка перевода моего прошлого виджетного туториала.

I’d like to present my approach to what can be the possible way to make a widget in Kohana3. I formulated it in the form of tutorial to make learning process more informal and practice-oriented (Kohanian gurus will certainly find nothing interesting below). Following text is the translation of my recent tutorial written in Russian, so it is based on my previous Ko3 tutorials (deploying Ko3, views in Ko3, simple HMVC in Ko3, and user input in Ko3, all in Russian). I hope that tutor is self-sufficient but ask me if anything is not clear. Also… I’m not a designer so only basic output decoration is presented, you can add more glamour if needed.

I love all kinds of feedback so if you find grammatical (my English is poor), semantic, stylish (maybe coding style) or even sense of humor mistake please let me know. I want to amend this tutorial. 

First of all, a little definition: I consider widget as a fragment of web page that is relatively independent on the other content of the page and (optionally) is possible to independently interact with user. Thus some typical cases of widget lifestyle can be like same widget on some pages or some exemplars of a widget on the same page. Some widget examples are calendars, polls, navigation bars, statistics etc.

Due to considerable independence on the page content a widget can be plugged outside of controller action method, for example in constructor or view. And simple widget can be made without any HMVC, but I do use this voodoo to make yet another example of its implementation. Our widget will show system date and time and a welcome message with the current page slug.

But let’s code it!

Let’s create parent controller for our future widget controllers. Now it is empty, but we’ll add some service functionality soon.

[code lang="php"]

class Controller_Widget extends Controller_Template {

   

}

[/code]

I’ll store widget controllers in application/controller/widget/ folder. So make the file date.php for  Controller_Widget_Date. Widget will be invoked by subrequest like that:

[code lang="php"]

Request::factory('widget_date/show/day-year')->execute();

[/code]

Consequently in the widget controller we should mention at least the template name (file will be created later) and action_show() method with one input parameter (output format):

[code lang="php"]

public $template = 'widgets/date';

   

public function action_show ($format = 'year-day-hour-min')

{

    $fields = explode('-',strval($format));

       

    $d = getdate();

    $date = "";

    foreach ($fields as $field)

    {

        switch ($field)

        {

            case "min":

                $date .= 'Minutes: '.$d['minutes'].'<br />';

            break;

            case "hour":

                $date .= 'Hours: '.$d['hours'].'<br />';

            break;

            case "day":

                $date .= 'Day: '.$d['month'].' '.$d['mday'].'<br />';

            break;

            case "year":

                $date .= 'Year: '.$d['year'].'<br />';

            break;  

        }   

    }

       

    $this->template->page = Request::instance()->action;

    $this->template->date = $date;   

}

[/code]

Note that the property Request::instance()->action contains name of main controller execute method.

The template  'widgets/date' looks like:

[code lang="php"]

<div style="background-color: #FFEEEF;">

<h4>«Date» widget<br />Welcome to the "<?php echo $page; ?>" page</h4>

<p>Current date is:<br /><?php echo $date; ?></p>

</div>

[/code]

Well, let’s write our creation in the main controller (in my tutorials it is the Controller_Welcome). Widgets should not depend on page content, so we can plug them outside of actions, for example in _construct() or before() methods. I love constructors but want to try new before() stuff:

[code lang="php"]

public $widgets = array();

   

public function before()      

{

    parent::before();

       

    $this->template->bind('widgets', $this->widgets);

       

    // attach "anywhere presented" widget

    $this->widgets[] = Request::factory('widget_date/show/year-day')->execute();   

}

[/code]

I created $widgets array to store widgets htmls. In before() method I invoked parent::before(), to init our template (can avoid this but you’ll need to write $this->template = View::factory($this->template); think it is less beautiful). Then bind our widget storage to main template and do subrequest to grab our widget html.

Now only two things remaining: include widgets in the main template

[code lang="php"]

<?php

    if (is_array($widgets) AND ! empty($widgets))

    {

           

    ?>

    <div id="widget_place" style="position: absolute; right: 10px; top: 20px;">

        <?php

            foreach ($widgets as $widget)

            {

                echo $widget;       

            }

        ?>

    </div>

    <?php

   

    }

?>

[/code]

… and add to application/bootstrap.php special widget route

[code lang="php"]

Route::set('widget', '<controller>(/<action>(/<format>))', array('controller'=>'widget_\w+'))

    ->defaults(array(

        'controller' => 'widget_dummy',

        'action'     => 'index',

    ));

[/code]

In the route controller is matched against a simple regexp, by default non-existent controller ‘widget_dummy’ and method ‘action_index’ were set (you can add them but all should work without).

Refresh any page of our test site — widget appears.

To add a widget on any page you can make corresponding subrequest in needed controller action

[code lang="php"]

$this->widgets[] = Request::factory('widget_date/show/hour-min')->execute();

[/code]

Yes, it is ugly and non developer-friendly. What if you need more flexibility in widget demonstration? Use config. Possible way to do this can be something like that.

Clone our present widget to widget ‘Cdate’. Simply rename widget controller and template (fix widget name in template), than plug thing in Controller_Welcome::before() — that’s all with cloning! Now start modification. First add configuration file cdate.php in application/config/cdate/. Configuration can be like that:

[code lang="php"]

return array

(

    'default' => array

    (

        'format' => 'day-hour-min-year',

        'allowed_pages' => array(),

    ),

    'form' => array

    (

        'format' => 'hour-min',

        'allowed_pages' => array('form'),

    ),

    'page' => array

    (

        'format' => 'year-hour-min',

        'allowed_pages' => array('page1','page2'),

    ),

   

);

[/code]

There are some variants of configuration, everyone containing widget format (that we’re set in quasi-uri earlier) and array of pages (page slugs) to show widget.

The parent controller Controller_Widget will contain the property $_config to store config data and the service method of config loading:

[code lang="php"]

protected $_config;

   

public function load_config($conf_name,$conf_entry='default')

{

    $this->_config = Kohana::config($conf_name.'.'.$conf_entry);

}

[/code]

Next step is modifying of our widget controller. Add config name in $config_name = 'widget/cdate', and in Controller_Widget_Cdate::action_show() load config, track the requested action and parse config format if needed:

[code lang="php"]

$this->load_config($this->config_name,$entry);

       

if ( ! in_array(Request::instance()->action, $this->_config['allowed_pages']))

{

    $this->auto_render = FALSE;

    return NULL;

}

$fields = explode('-',strval($this->_config['format']));

[/code]

       

Now we can add to main controller’s before() a widget request (last uri segment is the config entry ‘page’):

[code lang="php"]

$this->widgets[] = Request::factory('widget_cdate/show/page')->execute();

[/code]

All works: widget is shown only on page1 and page2, as prescribed in the config.

If needed you can invoke widget right from page template:

[code lang="php"]

<?php

    echo Request::factory('widget_date/show/year')->execute();

?>

[/code]

That’s all. You can post your feedback here (in captcha field input only lower, non striked-out symbols) or send it to Alexander.Kupreev at gmail.com. Thanks.

PS. I forgot to attach project files. Sorry. Download zipped application.

Also Kohana3 stuff in English

Deploying Кohana3 on the local XAMPP-powered Windows host

Комментариев: 6 RSS

Great Job !!! It would be great if you could find some time to translate other Kohana Tutorials too.

Александр Купреев2
2009-09-15 в 18:01:22

Thank you! These tutorials are very basic, but I'll try to translate them soon.

Thanks, its very usufull. When I need widget feature I usualy execute static method from Controller but your method is cleander and more developers frendly. smile

Александр Купреев4
2009-10-12 в 20:19:22

thanks for your opinion, Mandos! I'll try to share something even more interesting

Оставьте комментарий!

Используйте нормальные имена.

Имя и сайт используются только при регистрации

Если вы уже зарегистрированы как комментатор или хотите зарегистрироваться, укажите пароль и свой действующий email. При регистрации на указанный адрес придет письмо с кодом активации и ссылкой на ваш персональный аккаунт, где вы сможете изменить свои данные, включая адрес сайта, ник, описание, контакты и т.д., а также подписку на новые комментарии.

Авторизация MaxSiteAuth. Войти через loginza

(обязательно)