views:

25

answers:

2

I'm using the term "partial" to refer to a small section of presentational code which is repeated on many views. For example, a sidebar. In vanilla PHP, where the business and presentation logic is mixed, including a sidebar is no trouble:

if($someCondition) {
    include('sidebar.php');
}

However, in an MVC design pattern, the presentational logic must be kept in the view whilst the business logic must be kept in the controller. If I wish to include a partial unconditionally, then this is unproblematic since I can just have include('sidebar.php') in my view. However, I can no longer do so conditionally because that if logic is banned from my view.

I have attempted a number of solutions but they all have problems. I am currently using Solution 2:

Solution 1

Create an include function in my view class which could conditionally include content from my controller. So in my controller I could have the following logic:

if($someCondition) {
    $this->view->include('sidebar.php');
}   
$this->view->show('index.php');

Problems: sidebar.php will need to be included into index.php at a specific point requiring the include method on the view object to do some sort of parsing.

Solution 2

Move control of the partials out of the view and put them into the controller:

if($someCondition) {
    $this->view->show('header.php', 'sidebar.php', 'index.php', 'footer.php');
}
else {
    $this->view->show('header.php', 'index.php', 'footer.php');
}

Problems: Moves a large portion of the presentational logic into the realm of the controller. It seems to be more natural to me for the view to decide whether or not to include the header. Indeed, every PHP MVC tutorial I can find, has partials under the control of the view and not the controller.

Solution 3

Duplicate the view and alter the clone so that it includes the sidebar. Then I could conditionally load one or the other in the controller:

if($someCondition) {
    $this->view->show('indexWithSidebar.php');
}
else {
    $this->view->show('index.php');
}

Problems: Duplication of code. Consider what would happen if I had 2 sidebars which I needed to be conditionally loaded. Then I would need index.php, indexWithSidebar1.php, indexWithSidebar2.php, indexWithSidebar1And2.php. This only gets worse with every condition. Remember that the entire point of taking the sidebar out as a partial was to avoid replicating it anyway and this approach seems to defeat the point.

Are any of these solutions the "right" solution and if so, how can I overcome their problems? Is there a better approach out there?

A: 

Have your controller evaluate the condition and pass the result to your view. Then, the view can decide whether to include the partial.

For example, the controller can check whether a variable, $foo, isn't null. It passes the result of the comparison to the view via the model's property, $model->isFooed. In this case, the view can display the sidebar based on the value of $model->isFooed.

+1  A: 

However, in an MVC design pattern, the presentational logic must be kept in the view whilst the business logic must be kept in the controller.

IMHO: From an architecture standpoint, I push my business logic further back, out of the controller. We use services to handle all the business logic and repositories for data retrieval. The services call the repositories and then pass back our data model with all the business logic decided for us. Any logic outside that is really UI logic (show this, hide that), as our returned data could be (should be able to be) used in any kind of application, whether it's a mobile app, windows app, or web app.

You could use an extension helper method for your control, and in the model for the partial you can return EmptyResult() if you don't wish to render the sidebar. Or, more succintly:

<% Html.RenderAction<MyController>(x => x.Sidebar({params})); %>

And then in the controller:

public ViewResult Sidebar({params})
        {
            SidebarModel model = new SidebarModel();

            //...get/build model

            if ({someCondition})
            {
                return View("MySidebarPartialView", model);
            }


            return new EmptyResult();

        }
Josh