Multiple views in MVC-like PHP Frameworks

Hi,

I still sometimes hesitate using php frameworks for simple websites. I just can’t imagine how to implement multiple dynamic and reusable views within one request/response. Let me illustrate that by the following example.

A corporate website has six subpages: Home, Products, Services, News, Profile and Contact. Its layout has two columns – the main content on the left, a sidebar with some additional information on the right. The latest news posts are listet in the sidebar on every subpage except „News“ itself and „Contact“, while on these two pages the sidebar features random products. Products and news are editable via CMS.

So, I define some routes, build two models for products and news, create a controller for both which select a template and create the page – et voila. But at which point should I implement these news and product lists that are reused in the subpages sidebar? Do they have their own controller?

Sorry for my bad english. If you don’t get the point, please let me know. I’ll try to explain it.

Thanks in advance for your help.

Regards
Thomas

I’m also interested in this as I’m now trying out Silex micro-framework to see what coding practices may be worthwhile. But in my opinion the cleanest and most proper way would be to make the views request the modules (news posts) wherever they need them, probably having access to a model object so that the views can get the needed modules out from the model. I don’t think the controllers need to know which modules the views want to display.

Using some pseudo-ish code, you’d probably start with a “layout” template that defines the overall page structure but with placeholders for the main and side content.

<!DOCTYPE html>
<html>
    <head>
        <title>My Webpage</title>
    </head>
    <body>
        <main>
            {% block main %}{% endblock %}
        </main>
        <aside>
            {% block aside %}{% endblock %}
        </aside>
    </body>
</html>

You could then make two sub-“layout” templates that define just the sidebar content.

Latest news sub-layout:

{% extends "layout.html.twig" %}
{% block aside %}
    <h3>Latest news</h3>

    {# assumes there is a controller that renders just a snippet list of news #}
    {{ render(controller('latestNews')) }}
{% endblock %}

Random products sub-layout:

{% extends "layout.html.twig" %}
{% block aside %}
    <h3>Products</h3>

    {# assumes there is a controller that renders just a snippet list of products #}
    {{ render(controller('randomProducts')) }}
{% endblock %}

Then each page-specific template can choose which layout they want to use.

Home page template:

{% extends "latest_news_layout.html.twig" %}
{% block main %}
    <h1>My Webpage</h1>
    ...
{% endblock %}

News page template:

{% extends "random_products_layout.html.twig" %}
{% block main %}
    <h1>News Today</h1>
    ...
{% endblock %}

Is this just a bad choice of words? Because, I thought any rendering was the job of the view. :wink: So, now we have a view calling a controller, which is calling for data from a model, joining it with a template to be embedded back into the view. Isn’t that some whacky MVC? :smiley:

Here is Symfony’s docs explaining the usage of a template embedded controller.

Scott

Thanks so much! I never came up with the idea of seeking the solution in the View part of a documentation :unamused:

If I get it right, at Laravel there is a workaround for that to keep the templates “clean”. I can hardly believe this is the common way to realize it. Like Scott said, that seems somehow irregular.

Wouldn’t it be a more consistent way to let the Router call Views directly – instead of calling Controllers to let them return Views?

So far (in my own yet-another-selfmade-grown-out-of-time-undocumented-framework) I used to define Routes for these sidebar Controllers, which contribute variables to a global View $app->response->body:

<?php

# /app/routes.php

$app->router->add('get (.+)', function() use ($app) {
  $app->response->body->template = 'pages/'.$app->request->path(0).'.html';
});

# ...

$app->router->add('get (index|products|services|profile)', 'list_latest_news@App\Controllers\Sidebar_News');

$app->router->add('get (news|contact)', 'list_random_products@App\Controllers\Sidebar_Products');

?>

<?php

# /app/controllers/sidebar_news.php

namespace App\Controllers;

class Sidebar_News {
  privat $app;
  
  public function __construct(\App $app) {
    $this->app = $app;
  }
  
  public function list_latest_news() {
    $posts = # get posts somehow
    
    $this->app->response->body->add('sidebar_news_posts', $posts);
  }
}

?>

What about this way to do it? Am I ignoring conceptional mistakes or disadvantages?

Is the view supposed to call a controller in MVC? I imagine that would happen in a desktop application as part of user input but in the case of rendering a sub-template there is no user input.

That is one of the things I was trying to accomplish with my web MVc mini-project. To me, between the request and the template, the view should be able to know what is requested in a GET request and render the output by itself. To accomplish this, it does mean splitting up routing logic throughout the system, which isn’t optimal either. It was just something I did to learn and I still feel an MVC framework shouldn’t send GET requests to controllers. And definitely not have views call controllers for data. That is just ridiculous.

That is how Symfony resolves the problem of rendering “extra” data.

Scott

Are you referring about the same thing? It looks like Symfony calls controllers for data, do you mean this is ridiculous? (not that I have anything against you calling a Symfony way ridiculous!)

If we have a separate controller for rendering a module of a template then this controller can only be used for module templates since it doesn’t contain html of a complete web page. Then we have some controllers for complete pages and some controllers for partial pages (modules). This looks a bit messy to me. Why would we need a controller for generating part of a template? Isn’t that what the view should be responsible for?

Nothing says it can, nothing says it can’t. :wink:

But think of it instead as issuing a sub-request. For example, rather than rendering a controller, imagine instead that we make a literal HTTP sub-request.

{% block aside %}
    <h3>Products</h3>

    {# assumes there is a page that renders just a snippet list of products #}
    {{ httpClient.get('http://your-own-site.com/products/random-list') }}
{% endblock %}

Rendering another controller is conceptually the same thing, except you never have to leave the currently running application.

If you use ajax, then there’s a decent chance you have a lot of this already. :wink: Ajax responses can often return just a partial page that is meant to be embedded inside a complete page.

The issue is that it’s not just rendering a template. In the case of a products list or a news list, it means querying the database, caching results, and probably other controller work.

If we didn’t render a controller, then since the products or news list will be in the aside of every page, that means every page would have to make the same query. And even worse, what if the footer or header from the layout template also needed database queries? Should every page make the same header/footer queries?

Allowing templates to issue sub-requests, it turns out, is actually a tremendously cleaner solution.

What you’re talking about is basically HMVC, is it not?

Not basic it is

That is is and a very powerful one.

Okay, this makes sense and I’m not saying it’s a bad approach. I’m just wondering how much it adheres to MVC but I understand we can never achieve full MVC on the web.

I understand this reasoning but it’s not really true if you allow the view to fetch data from the model. In that case there is no problem with fetching data from the database or redundant database calls. The view modules are kept entirely in the view layer and are handled with templates and no extra controllers are necessary for this. Seems like the win is at least in fewer controllers (the model and the template would have to be present in both cases). Any pros & cons as opposed to calling controllers from the templates like in Symfony?

Yes, I am calling this method ridiculous.

If you give a “sub-template” the knowledge about the “request” it needs to make to get the data it needs to render, why is the loop through a controller even necessary? Why can’t the view just pull the data from the model, which it knows it needs? It has all the other auxiliary data needed from the original request to get or present the data properly too. It just seems like a roundabout way that can be done simpler.

Scott

Correction, Symfony allows and promotes templates to embed sub-request. That is VERY different from obtaining a reference to a controller and calling methods directly in a template. HMVC (Hierarchical MVC) is really no different from ESI (edge side includes) in basic concept. The model isn’t ridiculous at all provided a none persistent domain in contrast to something like a desktop app. Actually it makes much sense with separation of concerns and DRY at the fore-front for HMVC architecture.

Doing database queries from our HTML is one of the original plain PHP problems we’ve been trying to get away from all these years.

Pro of rendering controllers: We get all the benefits of a full-blow request. For example, we can cache the result of a sub-request using ordinary HTTP cache headers. Also we get built-in authorization checks, such as if a public page tries to issue a sub-request to a password protected area.

I would have thought the avoidance of querying from the HTML would be for change of state, where the PHP in a template is trying to manipulate the model. That shouldn’t happen. Reading from the model, is what the View basically should be doing. This rule of MVC is bent in frameworks and thus, give controllers the responsibility of gathering the data for the View too. A responsibility a controller really doesn’t need to do. Maybe everyone was afraid they might get tempted to do too much in the View and said, for web pages, we’ll always have to go through the controller.

In other words, MVC in PHP frameworks looks something like this.

Instead of something like this.

I’ll leave the question open, which one is the closest to proper MVC?

Scott

We avoid querying from HTML so our templates will be reusable. In this thread, for example, we wanted a random list of products. What if we also wanted a top selling list of products? Or a per-user list of products? If the database query is baked into the template, then you can’t reuse the template as a general purpose list of products.

Proper MVC? As it was originally defined for desktop software? Then neither of these are close.

I think the closest thing we have to a formal description of MVC for the web is this paragraph from Fowler.

You’re getting hung up on semantics when the most important thing is practical application. In practical application the modified version of “MVC” for the web makes much sense. Patterns only exist to facilitate business requirements so you shouldn’t be getting so caught up on terminology. Especially terminology that has long been established norm for the web as set forth by rails and spring. That being said you’re right web based MVC is not traditional “MVC”. However, I question what about that really matters when it comes to resolving real-world business problems. As a matter of fact I would go as far to say that using an established norm for no other reason than semantics is an anti-pattern to be avoided.

2 Likes

Agreed

Although I am not a pragmatist, neither am I strictly an idealist

IMHO balance between the two is the better choice

Get caught up in either extreme and inefficiency or stagnation is the most probable outcome

I don’t think I am questioning the semantics, but rather more the complication I think I am seeing. I believe I can do this, since I am relatively new to software architecture. I may be wrong and at one point also just give in to the status quo and say, the controller is the coordinator between the view and model for everything in a web application. But for now, I’d still like to rock the boat a little on this subject. Call it me doing a Tony M. :smiley:

Scott