MVC Refactor, Take 2

Cool! Thanks. :blush:

If the same template should be used, but we are working with a different object, it would mean a new template file, but with a simple include to the orginal clock template. The model “format” would be the same, we’d only need a new way to get the data.

The object defines a new model class though.

We can (and should) collect our URL parameters too. For instance, if we said. /clock/date-only. Then we could actually modify the view for the proper formatting.

Or, if we had, /clock/ntp, we could actually call up a source for data for the model with this and use the same template.

In the end, the parameters would be given to both the view or the model to determine further logic. This is the only “iffy” part about it. But still, it is a client coder’s part to handle. She just needs to know what parameter is needed for which manipulation. Model or View (or both).

Yes, I knew you would say that and I can’t argue against it much either. I just feel like this makes more sense from a client coder’s perspective. Her templates directly influence the model. The model influences the templates. Everything else is reduced to the basics of HTTP.

Scott

I’m not sure this is much better, it just feels like a workaround rather than a fix, having a file which exists just to include another file can be a little annoying. Want to edit the output? Open the file… realise it doesn’t actually do anything and have to go and find the file it references. Want to rename the original file? You have to go through and change the includes in the other files. A router configuration has this same problem but at least it’s all in one place.

And how about using NTPModel with date.html.php? You’d need a model called NTPDateModel that extended the original one and added nothing new, and a ntpdate.html.php which included date.html.php presumably?

Personally I think there’s a happy medium between an approach like you described and a configurable router :slight_smile:

How about?

/clock/ntp/date-only

You can theoretically fit a lot of configuration (parameters) into a URL (~2KB) and even more through the HTTP write methods. :wink: What is cool about this too is (or maybe a disadvantage, I am not sure yet), the actual users can also manipulate the outcome, as long as the necessary parameters are known to them.

I’d go so far as to make a configuration setup for URLs per object (and what you can do with them), so there can be a standard formatting for any URLs used by the system (which helps with dissection into parameters too). So, instead of having the usual routing mapping, you have URL/object mappings.

for instance, this would be our clock object mapping

/{object}/{data_source}/{date_format}

So, if you wanted the standard clock with just the date formatted, you’d have something like:

/clock/default/date-only

This mapping can also help for URL creation too, so the client dev makes less mistakes. The base information for building the URL is always part of the model, which is already injected into the view. It also helps with the name of the parameter variables (the keys) in the client code.

Oh, and a malformed URL simply ends up with the default.

Scott

I use this structure currently:

/{model}/{template}/{controller_action}/{controller_arg1}/{controller_arg2}

Then it does a ‘best match’. Can’t find {model}? Everything gets shifted over one spot so then it uses a default model {model} for {template}, {template} for {controller_action}, etc. Can’t find {template} It uses a default and the value from {template} becomes {controller_action}. Controller action doesn’t exist? Shift everything over, call a default action with the args {controller_action}, etc.

Another idea I have with this is to have an unknown object open up a wizard to automatically build the model and view. It starts off with asking for properties for the model. When you are finished you build the view. No need for any controller. If you need “special logic”, you extend the pre-built model.

Scott

Yes. That is certainly one way of doing a URL.

But, is the URL always that format? That seems limiting to me. What if the client coder/ user wants the id parameter to show up after the object and then have the name or title of the object after the id (some people are strange like that)? Any URL mapping is most certainly user land and thus, should not be limited at all.

And would a template ever be used for an object or model it was never really meant for? Surely, you could automate templating too and should, where it makes sense. But, the final object/model the web page is about will always be singular and the template must be made for it, so I feel we can certainly use composition to match them up. View formatting and model manipulation is a matter of implementation through any auxiliary parameters, which are set up by the client developer.

Scott

This is convention-over-configuration so where other formats are needed then configuration can be added.

I’ve got a folder structure where each model (Well, model API) has its own folder with models and templates, once a model has been selected, only templates in the directory relative to that model can be loaded. This has a nice added benefit of portability… want to copy a shopping basket from one site to another, just copy the /basket folder and it contains only the templates/models for the basket.

And this is the potential I am very much looking for. Up till now, application logic has not really been very portable, due to the coupling to the framework. I read this article trying to find solutions for my ideas and thought to myself, there has to be a better way. Although, there will always be some dependencies to a framework, I feel it is easier to inject and manipulate them in a model. And, it seems certainly much easier to standardize the model, than a bunch of controller actions.

Scott

I agree. The biggest advantage of the approach I suggested above is standardisation. The framework provides some interfaces and the developer programs around them, with a page-controller Web MVC set up, there’s no inversion of control, the application developer pulls in the required libraries/models/templates via service locators rather than having them provided via IoC, breaking Tell, Don’t Ask and LoD… resulting in less flexible code.

1 Like

I think we lost @Jeff_Mott? Jeff. What do you think of this direction?

I am on a short business trip. When I get back on Saturday, I’d like to try out the URL object mapping and formatting bit.

Scott

Still here. :slight_smile: Just been busy the past few days.

2 Likes

:smile:

Ahh, I was looking at it from the perspective of the router, where both approaches seemed to do essentially the same thing (instantiate a class then call a method that renders a template):

    $view = new SomeView('newThing.html.php');
    $response = $view->render();
    $controller = new SomeController();
    $response = $controller->someAction();

But it sounds like you’re actually talking about the overhead of writing an extra class, such as a controller class.

In Web MVC, if we truly had several wholly static pages (no user input, no database content), then we would make a generic controller. We might call it SimpleRenderController or something like that, and it would take the template to render as an argument from the router. Essentially we’d do it the way you’re doing it, but in Web MVC it’s not a rule to always do it that way. We would do it that way in cases where it helps (such as static pages).

The date and time examples are essentially also static pages (no user input, no database content), so in Web MVC, we’d probably use a generic controller for that too. So instead, let’s try some dynamic content (that is, database driven), which is by far the more common scenario in web applications. Let’s say there’s a template “thingList.html.php”, which we’ll reuse for “recently created list of things”, “recently viewed list of things”, and “search results list of things”.

First, a piece of code that both our approaches would use: a repository.

class ThingRepository
{
    public function getRecentlyCreated() { /* ... SQL */ }
    public function getRecentlyViewed() { /* ... SQL */ }
    public function search() { /* ... SQL */ }
}

And now we differentiate. In Web MVC, we would have three actions.

class ThingController
{
    // ...

    public function recentlyCreatedAction()
    {
        $things = $this->repository->getRecentlyCreated();
        return $this->templating->render('thingList.html.php', ['things' => $things]);
    }

    public function recentlyViewedAction()
    {
        $things = $this->repository->getRecentlyViewed();
        return $this->templating->render('thingList.html.php', ['things' => $things]);
    }

    public function searchAction()
    {
        $things = $this->repository->search();
        return $this->templating->render('thingList.html.php', ['things' => $things]);
    }
}

Contrasted with ToMVC, where we’d have three model classes.

class RecentlyCreatedModel
{
    // ...

    public $things;

    public function getThings()
    {
        $this->things = $this->repository->getRecentlyCreated();
    }
}

class RecentlyViewedModel
{
    // ...

    public $things;

    public function getThings()
    {
        $this->things = $this->repository->getRecentlyViewed();
    }
}

class SearchModel
{
    // ...

    public $things;

    public function getThings()
    {
        $this->things = $this->repository->search();
    }
}

If down the road the thingList template were to change (maybe it needs an extra argument), then I’d have to edit all three controllers and you’d have to edit all three model classes.

So here’s what I think I’ve concluded. For static pages, your approach is useful. Though, Web MVC can (and in real life does) use generic controllers to cater to this specific case. But for dynamic pages, which are by far the more common, the benefit of your approach seems severely diminished.

Depends a little on the specific circumstances. If the templates are related in some way (such as in our format example), then the controller can usually select the appropriate template through some string interpolation. But if we need to choose between several unrelated templates (seems unusual), then the code you showed is probably how we’d do it. In this scenario, your alternative approach would probably save a line of code or two (the one-line actions that invoke the common controller code). And we’d both need to add a route for each template.

Yes, that’s true for both of us. If I want my controllers to be able to use any of twig/blade/plain, or if you want your view classes to be able to use any of twig/blade/plain, and if those template engines don’t have compatible interfaces, then in both our approaches, we’d have to write adapter classes.

Web MVC doesn’t force us to use (or not use) either service locator or dependency injection. If you want to be able to substitute the model, then you can still take it as a constructor argument and let a dependency container construct the controller.

The problem with this approach, of course, is if the template needs some information from a model (See my clock example using clock.html.php and date.html.php). There’s no way you can do this using a generic controller because in web mvc the controller passes the data (in this case the date/time) to the model. You have to write a controller to achieve this. There are lots of cases where there is a read but no write: Displaying the latest 10 news stories, displaying a list of “new arrivals” products, displaying “on sale” products. All of these read from the database but require no interaction from the user.

You’re making a distinction between dynamic and static that isn’t really correct. There are actually 3 different cases:

  1. Static pages which are just rendering a template (e.g. Thank you for your enquiry we will get back to you shortly). You can achieve this in web mvc and tomvc with a generic controller

  2. Pages which display some information from the model/database in a predefined format e.g. latest news, latest products, special offers (which might use the same template as latest products!). You cannot achieve this in webmvc with a generic controller, with tomvc you can

  3. Pages which display some information from the model/database and allow the user to manipulate how it’s displayed (forms, search results, pagination, loading a specific record into a template e.g. viewing a product by ID)

Of course, but the point you were making is that by adding a ‘view’ I didn’t have one less class in the system:

Once you account for your template wrapper, ToMVC does have that one fewer class. If I want to support other template systems I add: TwigView, BladeView etc.

In which case, where is the decision made about which model to inject? If it’s in the router then my point from earlier still stands: If the router selecting the model and the controller, what is the problem with having it also select the view? If as you say it’s in the DIC, have the DIC also construct a view.

The problem with putting it in a DIC is that the configuration is spread among two places and potentially complex. Consider the clock example with two models that supply the same data from multiple sources. You need to now route to the DIC and fetch two differently configured instances of the controller on different routes, one for the NTP version and one for the standard version. This makes the router far more complex, something you were trying to avoid previously.

There is a simple conceptual issue with this and with a “true” MVC architecture. Normally, the controller in MVC has no interaction with the view. The controller is there purely for the manipulation of the model. The view then always calls the model to get the modified state.

The issue we have with web technology and MVC is, the actual “view” is in the web browser. On top of that, the “commands” needed for the controller are also coming from the web browser. There is a “conversion” to HTTP (or a web socket) required between the server and the browser. This interface simply needs to convert the HTTP request, run it through the MVC to get a response. Then the converter must send the response back. This is exactly what my example does.

Um, there is user input in my mini-MVC example. And it wouldn’t be hard at all to add a database layer to my example either. In fact, I’d say it is easier and more logical to add the database layer in this type of web MVC, now that all the data MUST be manipulated in the model (like it should be).

Scott

There’s another big problem with Web MVC as well: It ignores the fact there is such a thing as application state. It understands domain state but makes no attempt to account for the state of the application. As such, the application state is stored in the controller action in a very transient way.

Application state is things like:

  • What is the ID of the record being viewed/edited
  • Was the form submitted or not?
  • Is the submitted data valid?
  • Which page of results are we on?
  • What is the filter for the current set of results? e.g. search term, search order, category filter

The problem with storing this in temporary variables in the controller is that the controller has to give this information to anything that needs it and there’s no way of supplying the application state from anywhere else. Using a proper MVC approach where the state is in the model, the model can read the state from anywhere.

To be clear, here is how Web MVC would probably handle your clock/date scenario.

class GenericClockController
{
    public function doAction($clockObj, $template)
    {
        return $this->templating->render($template, ['time' => $clockObj]);
    }
}
if ($_SERVER['REQUEST_URI'] === '/clock') {
    $response = new GenericClockController()->doAction(new Clock(), 'time.html.php');
} else if ($_SERVER['REQUEST_URI'] === '/date') {
    $response = new GenericClockController()->doAction(new Clock(), 'date.html.php');
} else if ($_SERVER['REQUEST_URI'] === '/clock-ntp') {
    $response = new GenericClockController()->doAction(new NtpClock(), 'time.html.php');
} else if ($_SERVER['REQUEST_URI'] === '/date-ntp') {
    $response = new GenericClockController()->doAction(new NtpClock(), 'date.html.php');
}

Like I said in my last post, we’d do it essentially the way you’re doing it. I do agree this is the better approach for this specific scenario. To be clear, this scenario is static content with multiple variations. The reason you don’t see this kind of implementation very often in Web MVC applications is because this scenario doesn’t actually occur very often. Static pages are already few and far between (maybe a terms of service page, an about us page, things like that), and multiple variations of those pages almost never happens in real life (excluding i18n, which is typically handled in a completely different way). So, again, to be clear, Web MVC can and does shift to your kind of solution when it’s helpful to do so, but it usually isn’t.

Didn’t I cover exactly this with the “recently created”, “recently viewed”, and “search” list of "thing"s??

Again, this case describes the “list of things” that I gave example code for in my last post. In Web MVC, we’d make three controllers, and in ToMVC, you’d make three model classes. Both the controllers and your model classes would be coupled to the template.

The Template class was obviously generically named, but what it is is a plain PHP template rendering engine. It could have just as easily been, for example, a Twig template rendering engine. But what you did in your examples was remove the rendering engine and instead bake the template rendering logic straight into your view class. In real life, or even if we just expanded our examples a little further, you’d wouldn’t have been able to get away with that. Instead, your view classes should use a template rendering engine.

The dependency container config would define a variety of controller services, and the router would pick a particular service.

Because you’d be limiting your view selection options for no good reason. For example, here’s sample code from Fowler’s book:


He chooses between a couple templates based on what he gets back from the database. But if the router tells you which template you’re supposed to use, then you don’t get this kind of freedom.

I was talking about Tom’s date/time examples.

Well, I did make my example after his. There is a select in Tom’s example, where the user can select the time zone.

Scott

But that’s display logic based on a result. Put the if statement in the template. Chances are, some of the information about a Classic Album and an Album is the same (or we wouldn’t be using the same repository) so we can just use the if to dictate which version to show. The same is true of “Not Found”. This is clearly display logic “If no album is found, display an error” so should go in the view (the template).

By doing this, we can make a generic controller. I’ll use the same album example. The framework could supply a generic controller and interface:

class SingleEntityController {

	public function __construct(EntityModel $model) {
		$this->model = $model;
	} 

	public function get($id) {
		$this->model->load($id);
	}
}


interface EntityModel {
	public function load($id);
}

The developer would then provide the model and template:

class AlbumModel implements EntityModel {

	public function __construct(AlbumRepository $repository) {
		$this->repository = $repository;
	}

	public function load($id) {
		$this->entity = $this->repository->findById($id);
	}

	//For the template to call..
	public function getEntity() {
		return $this->entity;
	}
}

Ok it’s half a dozen of one and six of another, but wait! That can be simplified can’t it, I’d have to write a model for Albums, Blogs, Cars, Products, etc. Consider this:

class RepositoryModel implements EntityModel {

	public function __construct(Repository $repository) {
		$this->repository = $repository;
	}

	public function load($id) {
		$this->entity = $this->repository->findById($id);
	}

	//For the template to call..
	public function getEntity() {
		return $this->entity;
	}
}

Now I can write a route that looks like this:

$model = new RepositoryModel(new AlbumRepository());
$controller = new SingleEntityController($model);
$view = new View('album.html.php', $model);

Or like this:

$model = new RepositoryModel(new BlogRepository());
$controller = new SingleEntityController($model);
$view = new View('blog.html.php', $model);

or

$model = new RepositoryModel(new ProductRepository());
$controller = new SingleEntityController($model);
$view = new View('product.html.php', $model);

In the example you provided, because there are domain concepts in AlbumController (is it an album or a classic album, does it exist or not?) as well as hardcoded template names, this kind of flexibility just isn’t possible and requires a new controller with similar logic for each and every type of domain entity.

edit: The difference between the two approaches is entirely about IoC.

All of this information(except for “is valid”) is stored in the request object. Why would anyone think it’s stored in the controller?

1 Like