After many hours of research and thinking I have found an understanding, then coming to question it again, I am struggling to find something concrete which I can grasp and apply to my code in a practical sense going forward.

I think I am referring to presentation logic and a domain model. I have read numerous articles such as Anemic Domain Model, but I still don't fully understand what the correct solution is. I will start with one actual scenario which is puzzling me.


Let's say I have a web form which requires data to be shown from 10 different models. From each model I need to get a specific set of data which is only useful to this particular view. For example, it might be a bunch of drop-down menus with certain options in them.

1. Let the Controller do it

Pseudo Code:

/**
 * Controller action
 * 
 * Process a request for the specific view.
 */
public function myView()
{
    $options1 = $model1->query(
        // Get fields A, B, C from the database
        // Order the data by a certain field
        // Exclude data which does not meet conditions
        // Process the data some more
    );

    $options2 = $model2->query(
        // Get fields X, Y, Z from the database
        // Order the data by a certain field
        // Exclude data which does not meet conditions
        // Process the data some more
    );

    // ...repeat this sort of thing 8 more times

    $this->sendToView($options1, $options2, ...);
}

So the controller asks different model entities for the data the view needs, in exactly the format the view needs.

In a sense, this seems nice to me because the model layer has no idea about the view, and the controller takes responsibility for bridging the gap between them. All the logic about what should be presented in the view is handled by the only method in the application which is directly coupled to that view.

However, this could easily be 200 lines in my controller, and this is just for one specific view. Soon all the controllers will be huge sprawling messes of dozens of calls to the models for specific data for each view. You could split the different calls up into other methods on the controller (e.g. private) to make it a little more modular/manageable but what's the point?

Conclusion: Keep the controller thin, and delegate this stuff elsewhere, I guess.


2. Let the Model layer do it

Pseudo Code:

/**
 * Controller action
 * 
 * Process a request for the specific view.
 */
public function myView()
{
    $options1 = $model1->getOptionsForAbc();
    $options2 = $model2->getOptionsForXyz();
    // ...repeat for 8 more models
}

/**
 * Model 1
 */
public function getOptionsForAbc()
{
    $options = $this->query(
        // Get fields A, B, C from an associated table
        // Order the data by a certain field
        // Exclude data which does not meet conditions
    );
    return $options;
}

// Each model has similar methods which retrieve specific sets of data

So now the controller is nice and thin, and is not concerned with any logic. It just asks the model for some stuff it needs, and the model knows how to get it.

I use the term "model" loosely here. I fully understand that the model is a layer comprised of many things.

The problem with this appears to be that models will end up having lots of functionality which is probably only useful for one particular view. It will not be used elsewhere because only that one view requires that particular subset of data. This seems incorrect to me.


3. Implement a Service/Business Logic layer

I believe all the business logic should really be in the model. For normal business logic, such as calculations, validation and other things which happen to data, I understand this.

However, what should happen with this particular example of a view requiring specific data? Should I be creating a service class which represents the form, which is capable of delivering all the data the view requires, in the correct format? That doesn't seem right.

Final Question: So where should I be storing this kind of data-related presentation logic which is only for the view?

  • 4
    Sounds like you need ViewModel objects. Have a looksie at those. – Robert Harvey Jun 22 '16 at 18:51
  • @RobertHarvey I read about view models and it doesn't seem to apply to an MVC framework, rather MVVM only. – BadHorsie Jul 20 '16 at 21:34
  • 1
    Then don't call them ViewModels. Call them "Those objects that contain my UI data and validation logic, so that the UI surface itself doesn't have to." Just because ViewModel is two of the initials in MVVM doesn't invalidate the concept for MVC. – Robert Harvey Jul 20 '16 at 21:53

Make the view do formatting. That's what it's there for. The controller's job is to feed it arbitrary data. MVC is a pattern, not a concrete thing. There is nothing wrong with views being "smart".

Validation belongs in the model. Controllers shove user data directly at the model and it's the model's job to reject invalid stuff (e.g. throw an exception).

If you find yourself copypasting formatting code, make helper classes with reusable static methods, or a view class hierarchy with inherited methods, or use traits.

You could treat the business layer as a server-side MVC application and the whole presentation layer could (but need not) be another MVC application on the client-side. The client-side application could asynchronously make requests to controllers that in turn relay the requests to models and forward the results back to the presentation layer. This enables the client

  1. to make parallel asynchronous requests
  2. to obtain partial data
  3. to manage similar use-cases without much effort

Personally, apart from scenarios such as transactions, I prefer to aggregate at the client side. Feels like every component is lighter and simpler, and as if the concerns have been separated and delegated appropriately.

The nice thing about MVC is that there is a gazillion different implementations out there. I'm sure if you looked hard enough you can find one that matches whatever approach you end up following. To me, it was the realization that web applications follow a Request/Response pattern that freed me from the tyranny of caring about MVC. http://symfony.com/doc/current/introduction/http_fundamentals.html

Does your controller really care about form control choice options? Of course not. So there is really no reason for the controller to grab the options only to pass them onto the view. Let the view do it. Can there be some business logic in deciding which options the view needs? Sure. So you really don't want to embed the nitty gritty logic in your views.

So something like:

    // Inside of the view
    $this->gameOfficialChoices = array_merge(
        [null => 'Select Game Official'],
        $this->assignorFinder->findGameOfficialChoices($game)
    );

In the above code, we need to know which game officials are eligible to officiate a given game. There is actually quite a bit of business logic going on here so we move the actual lookup into the AssignorFinder service.

This sort of approach seems to work okay for me. Is it MVC? Don't know. Don't care.

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.