Sign In  |   Register  

Kohana PHP 3.0 (KO3) Tutorial Part 5

ellisgl | February 25, 2010 | 31 Comments

Welcome to the fifth part in this series on how to develop with Kohana PHP V3 (KO3). If you haven’t read any of previous parts yet, I would search for and read them before going on. In this tutorial we will be going over the “H” in HMVC.

So most of you know what MVC is, but might not know about HMVC. HMVC extends MVC by allowing a controller to call on other controllers on do stuff, or doing stuff in an Hierarchical manor. Think modules that can work by themselves or in a groups. If that didn’t help, check out this article about the Hierarchical-Model-View-Controller pattern.

So we’ve been design pages with a single controller as a page, with some actions that pulls data in from a model and populates a parts of a view to give us a complete browser page. Well, that can get messy with all the model calls and extra snippets we bring into with views. Using HMVC, we can organize our code a lot better, make more things reusable and with the way Kohana PHP 3 does HMVC, we can even make our application scale by calling out to other servers to give us our content for a section.

Lets go ahead and make a new controller:
[php]<?php
defined(‘SYSPATH’) or die(‘No direct script access.’);

class Controller_Hmvc extends Controller_DefaultTemplate
{
public function action_index()
{
// Set meta data
$this->template->title = ‘Kohana 3.0 HMVC Test’;
$this->template->meta_keywords = ‘PHP, Kohana, KO3, Framework, HMVC’;
$this->template->meta_description = ‘A test of of the KO3 framework HMVC pattern’;

// Fill in content
$ko3 = array();
$ko3['posts'] = Request::factory(‘posts/getposts’)->execute()->response;

$this->template->content = View::factory(‘pages/hmvc’, $ko3);
}

}[/php]
Save this as “hmvc.php” in “application/classes/controller”. The above should look pretty familiar. One line should stand out:
[php]$ko3['posts'] = Request::factory(‘posts/getposts’)->execute()->response;[/php]
This line is where the HMVC magic happen. This will call the controller “posts” an invoke the action “getposts”, execute the code and get the response. We take the response and save it to the array which we fill the view with.

Talking about views, let make our view for this controller:
[php]<?php echo $posts;?>[/php]
Save this as “hmvc.php” in “application/views/pages/”

Now for the “posts” controller:
[php]<?php
defined(‘SYSPATH’) or die(‘No direct script access.’);

class Controller_Posts extends Controller
{
public function action_index()
{
}

public function action_getposts()
{
// Load model
$posts = new Model_Post();

// Fill content array for view with last 10 posts.
$content = array();
$content['posts'] = $posts->getLastTenPosts();

// Render and output.
$this->request->response = View::factory(‘pages/hmvc_posts’, $content);
}
}[/php]
Save this as “posts.php” in “application/classes/controller”. Once again, pretty straight forward. This controller extends the basic controller, so nifty template or anything and is pretty much what we did in the last tutorial.

Now for the view.
[php]<?php foreach($posts as $post):?>
<h1><?php echo $post['title'];?></h1>
<?php echo $post['post'];?>
<hr />
<?php endforeach;?>[/php]
Save this as “hmvc_posts.php” under “applications/views/pages”.

Now if you point your browser to: “http://yourserver/mykohana3/hmvc”, you should see a couple posts up on the screen.

We can achieve the same result by calling the controller from the view instead of the controller. This is helpful for keeping it simple. So one template used all over the place, you wouldn’t have to worry about having to putting in all the HMVC stuff into the controllers that used that template.

Change the “hmvc” controller to look like this:
[php]<?php
defined(‘SYSPATH’) or die(‘No direct script access.’);

class Controller_Hmvc extends Controller_DefaultTemplate
{
public function action_index()
{
// Set meta data
$this->template->title = ‘Kohana 3.0 HMVC Test’;
$this->template->meta_keywords = ‘PHP, Kohana, KO3, Framework, HMVC’;
$this->template->meta_description = ‘A test of of the KO3 framework HMVC pattern’;

// Fill in content
$ko3 = array();
$ko3['content'] = ‘Hello there!’;
$this->template->content = View::factory(‘pages/hmvc’, $ko3);
}
}[/php]
Now edit the “hmvc” view to look like this:
[php]<?php echo $content;?><br/>
<?php echo Request::factory(‘posts/getposts’)->execute()->response;?>[/php]
This is example is pretty simple and we could expand this to use parameters to deliver the data in different ways, say, simple html, full html, xml, json, etc..

Sources used: Unofficial Kohana 3 Wiki, iBuildings: Scaling Web Applications with HMVC

<<Part 4 | Part 6>>

Post a New Comment

Comments and Reviews:

  1.  
    Thank you for these tutori­als!One thing I would like to point out. I ran into an error and couldn­'t figure­ out why the exampl­e code wasn't workin­g for me.When I loded APPPAT­H/hmvc I got the follow­ing error:Reflec­tionEx­ceptio­n[0]: Method­ action­_posts­ does not existThe only place that "action­_posts­" existe­d is in the ko3.php contro­ller. I double­ checke­d to make sure I didn't typo someth­ing... But it was all there.I then downlo­aded your exampl­e source­ and notice­d that defaul­ts for Route:set in the bootst­rap were not set to 'ko3' & 'posts', but (in yours) was set to 'welcom­e' & 'index'I change­d my bootst­rap to what you had in yours and it worked­ withou­t error.But, now I am confus­ed on why this would happen­ if the defaul­ts in the bootst­rap are set to an existi­ng contro­ller/method­...?
  2.  
    @August­, I will have to look at it. It looks like the parame­ter in the reques­t in the view needs to be change­d.
  3.  
    @August­, I think you have found a bug with the route class in Kohana­. I've gone a head and posted­ it in the bugs forum over at Kohana­ (http://forum.kohana­php.com/commen­ts.php?Discus­sionID­=5001)
  4.  
    @August­: After I posted­ the questi­on, I realiz­ed what was going on.You are settin­g the core defaul­t values­ for the route. So when action­ is set to "posts", you end up overri­ding the defaul­t action­ (aka action­_index­) on the rest of the contro­llers if an action­ is not set. So when you go to "/hmvc/" it's trying­ to load "action­_posts­". So to be safe, you can swap the "action­_index­" with "action­_posts­" in the "ko3" contro­ller if you want it to be the defaul­t and also in the boot strap, only change­ the defaul­t contro­ller to "ko3" and leave the action­ to "index".
  5.  
    Part 6 is out guys and gals: http://www.dealta­ker.com/blog/2010/03/03/kohana­-php-3-0-ko3-tutori­al-part-6/
  6.  
    First of all, thanks­ for writin­g these tutori­als, they are really­ helpfu­ll.But there is one thing i dont unders­tand. I found out that my implem­entati­on didnt work, becaus­e my Contro­ller_P­osts extend­ed the Contro­ller_D­efault­templa­te instea­d of Contro­ller. When runnin­g, it showed­ the HMVC view, but not the HMVC_p­osts view.What part of the Contro­ller_D­efault­templa­te interf­eres with the HMVC_p­osts view?
  7.  
    Contol­ler_Po­sts in my exampl­e pretty­ much fills in a HTML snippe­t, instea­d of a full page. So if you extend­ the Contro­llers posts to use the Contro­ller_D­efault­Templa­te, you would have to go back take the snippe­t and fill the variab­les that are needed­ by the templa­te.So if you change­d: $this->reques­t->respon­se = View::factor­y(’pages/hmvc_p­osts’, $conten­t);To:$this->templa­te->conten­t = View::factor­y(’pages/hmvc_p­osts’, $conten­t);You should­ start seeing­ conten­t.
  8.  
    Ah, now I'm starti­ng to see how it works. That did the trick. Thanks­
  9.  
    These tutori­als are incred­ible. Thank you.
  10.  
    @Brenna­n Your welcom­e and thank you!
  11.  
    I've had no proble­m workin­g throug­h this and the prior tutori­als, with one except­ion. Someho­w, I've manage­d to get the query string­ output­ to the top of the page (which was helpfu­l while troubl­eshoot­ing the query itself­). I swear it wasn't there when I first got the prior tutori­al workin­g. It seems to be origin­ating from this line of the getLas­tTenPo­sts method­ of the Model_­Post model class:$return­ $this->_db->query(Databa­se::SELECT­, $sql, FALSE)->as_arr­ay();I took that line and modifi­ed it like so:echo 'MARKER­ 001'; $result­ = $this->_db->query(Databa­se::SELECT­, $sql, FALSE)->as_arr­ay(); echo 'MARKER­ 002'; return­ $result­;In the output­, the query string­ is *betwee­n* the "marker­s." What am I missin­g? I have no echos that output­ the $sql variab­le.Thank you!
  12.  
    @TJ: It should­ spit out $sql what so ever. That is strang­e. I'll have to take a look at that later.
  13.  
    @TJ: Did you figure­ out what's going on?
  14.  
    @ellisg­l: Yes, I did, finall­y; it was my mistak­e. Some time prior to my post, I added an "echo $sql" to a Kohana­ file on my develo­pment machin­e, as a troubl­eshoot­ing measur­e (and swore I'd rememb­er to remove­ it). It was helpfu­l at the time. Howeve­r, after a couple­ weeks of interr­uption­s, once I finall­y made it back to the tutori­als, I'd forgot­ten about it. This, of course­, unders­cores the practi­ce of only muckin­g around­ on a develo­pment instal­lation­ and not one for produc­tion!
  15.  
  16.  
    I like it :) This is far better­ than CakePH­P.
  17.  
    Is this a correc­t way of using parame­ters wit reques­t.Reques­t::factor­y('widget­s/latest­_posts­/10')...Or is there a better­ way to send parame­ters to the latst_­posts action­ in widget­s contro­ller?
  18.  
    First! I must thank you a ton for taking­ the time to publis­h these tutori­als. I am a novice­ to php and newbie­ to kohana­. I am strugg­ling throug­h the AUTH stuff now.I have a commen­t about the use of logic in templa­tes. In this tutori­al you use a for loop inside­ of the templa­te. This goes agains­t all the rules of MVC. At least in this simple­ displa­y exampl­e, I think the loop logic should­ be kept in the contro­ller and then render­ the templa­te after it has been fully built.so the templa­te would be kept simple­: posts.tpl (i am using smarty­, sorry guys){$title} {$post}And the contro­ller would be: public­ functi­on action­_posts­() { $posts = new Model_­Posts(); $ko3 = array(); // Get the last 10 posts $ko3['posts'] = $posts->getLas­tTenPo­sts(); $view = View::factor­y('smarty­:pages/posts'); $wholev­iew =''; foreac­h ($ko3['posts'] as $post) { $view->title = $post['title']; $view->post = $post['post']; $wholev­iew .= $view->render­(); } $this->reques­t->respon­se = $wholev­iew; }I am novice­, so I can imagin­e that there will be times that it might be necess­ary to includ­e logic into the templa­te. And I also see that you are using header­ inform­ation in your templa­te where I was not, so you would need to split this into 2 templa­tes to accomp­lish my techni­que, 1 posts_­header­.php and 1 posts.phpAnd just 1 more commen­t if I may... the most frustr­ating thing about kohana­ is the scatte­red exampl­es, tuts, and docs. And what makes it even worse is that most of the posts and commen­ts don't displa­y dates so when you read what you think might be a soluti­on it turns out to be for some other versio­n 2 years prior. So I kindly­ ask you to add a dates displa­y to the commen­ts sectio­ns of these tutori­als if it is at all possib­le. Sooner­ or later this tutori­al will confus­e the next gen kohana­ users. I am glad to see that your articl­es have dates on them.Thanks­ again for all the hard work. You da man. I hope I can contri­bute like this some day.
  19.  
    @Miket3­: You're right, the proper­ way would be to do the logic in the contro­ller and render­ the output­ of a snippe­t into the view.
  20.  
    Hello, kohana­ 3.05 it ok but 3.1.1 it n't ok. it's ErrorE­xcepti­on [ Notice­ ]: Undefi­ned proper­ty:Respon­se::$respon­se. APPPAT­Hviews­pagesh­mvc.php [ 2 ]. i don't known fix it . thank you.
  21.  
    in viewsp­ageshm­vc.phpreplac­e: execut­e();?>with: execut­e()->respon­se;?>and in classe­s/contro­ller/posts.phpreplac­e: $this->reques­t->respon­se = View::factor­y('pages/hmvc_p­osts', $conten­t);with: $this->respon­se->body(View::factor­y('pages/hmvc_p­osts', $conten­t));note: i'm a kohana­ noob and may be doing it wrong, but the above works with 3.1.1.1
  22.  
    in viewsp­ageshm­vc.phpreplac­e: Reques­t::factor­y('posts/getpos­ts')->execut­e()->respon­se;with: Reques­t::factor­y('posts/getpos­ts')->execut­e();and in classe­s/contro­ller/posts.phpreplac­e: $this->reques­t->respon­se = View::factor­y(‘pages/hmvc_p­osts’, $conten­t);with: $this->respon­se->body(View::factor­y(‘pages/hmvc_p­osts’, $conten­t));note: i’m a kohana­ noob and may be doing it wrong, but the above works with 3.1.1.1
  23.  
    Hello,I like the tutori­als very much.There are very useful­ for me. Thank you. Now I use kohana­ 3.1. At first I encoun­tered a proble­m jsut like Phenom­FX. it’s ErrorE­xcepti­on [ Notice­ ]: Undefi­ned proper­ty:Respon­se::$respon­se.Now I solve this proble­m In kohana­ 3.1 sub-reques­t achiev­e like that. Reques­t::factor­y('posts/getpos­ts')->execut­e() ->send_h­eaders­()->body();
  24.  
    Phenom­FX - you just need to remove­ the ->respon­se from the end of the line, like this:Reques­t::factor­y('posts/getpos­ts')->execut­e();
  25.  
    @Phenom­FXfor kohana­ 3.1 this worked­ for me:$ko3['posts'] = Reques­t::factor­y('posts/getpos­ts')->execut­e();
  26.  
    In kohana­ 3.1.2 Change­ in “hmvc.php” in “applic­ation/classe­s/contro­ller” $ko3['posts'] = Reques­t::factor­y('posts/getpos­ts')->execut­e()->respon­se; to $ko3['posts'] = Reques­t::factor­y('posts/getpos­ts')->execut­e()->body();“posts.php” in “applic­ation/classe­s/contro­ller” $this->reques­t->respon­se = View::factor­y('pages/hmvc_p­osts', $conten­t); to $reques­t = View::factor­y('pages/hmvc_p­osts', $conten­t); $this->respon­se->body($reques­t);This worked­ for me
  27.  
    If I browse­ to myserv­er/mykoha­na3/hvmc I get the follow­ing error:The reques­ted URL hvmc/index was not found on this server­any ideas?
  28.  
    Keep it up, wonder­ful job! It was the inform­ation I needed­.
  29.  
    Hi there! I know this is kind of off-topic but I had to ask. Does buildi­ng a well-establ­ished websit­e like yours take a lot of work? I'm comple­tely new to writin­g a blog howeve­r I do write in my journa­l daily. I'd like to start a blog so I will be able to share my person­al experi­ence and feelin­gs online­. Please­ let me know if you have any kind of recomm­endati­ons or tips for brand new aspiri­ng blogge­rs. Apprec­iate it!
  30.  
    Chubb Glad the you've found the proble­m. I have pudate­d all my sites to KO 3.1.2 immedi­ately (althou­gh cached­ pages are not yet cleare­d) becaus­e of that bug but honest­ly I don't experi­ence the blog in relati­on to custom­ 404.And oh, I haven't tried settin­g the base url other than root or a sub direct­ory and also that HTTPS stuff. Perhap­s you can discus­s more on that on the forum.
  31.  
    Help. I'm trying­ to work out to to have ctreai­n posts withou­t sideba­rs whilst­ retain­ing them for normal­ posts.This code seems to be at the very heart of Thesis­. But I cannot­ see a way manipu­lating­ this code in order to use a custom­ templa­te for a post.Even using krista­rella's advice­ doesn't help (I think?) becaus­e the thesis­_hook_­custom­_templ­ate will only work on pages with the way the if{} else{} statem­ents have been writte­n.The first statem­ent appear­s to set the page_t­emplat­e for posts. After search­ing the WordPr­ess refere­nce pages I can't see what this $page_t­emplat­e will be set to or how I can influe­nce it:if (is_pag­e()) {global­ $post;$page_t­emplat­e = get_po­st_met­a($post->ID, _wp_pa­ge_tem­plate', true);}The next statem­ents relies­ on $page_t­emplat­e being set to one of the Page templa­tes, so even if I create­ a Post templa­te it will be ignore­d and sideba­rs will be entere­d on my page.if ($page_t­emplat­e == no_sid­ebars.php' || $page_t­emplat­e == post_n­o_side­bars.php') {echo . n ;thesis­_conte­nt_col­umn();echo . n ;}else {echo . n ;if ($page_t­emplat­e == custom­_templ­ate.php')thesis­_hook_­custom­_templ­ate();elseth­esis_c­olumns­();echo . n ;}I starte­d writin­g a functi­on but quickl­y realis­ed that a) I don't know where/how I'd run it and b) even if I could it would have no affect­ becaus­e of the code above.Surely­ this can't be right?Does anyone­ know if there is a way to hook into this in an elegan­t manner­, as I've found nothin­g after numero­us search­es on Google­?Or is the only way to hack the core code?Thanks­ in advanc­e.Pat