How Do You Do Fragment Caching While "keeping The View Stupid"?

Hi,

It’s been a long time since I wanted to ask this question…

As far as I understand, and if this hasn’t become obsolete, it is a general rule of good practice in the MVC paradigm to “keep the view stupid”, that is, let the controller take decisions and manipulate data (by calling model logic, as in “fat model, thin controller”, another rule of good practice) rather than the view. Right?

So, a typical super-simplified way of showing a page with some data would be:

Controller code:


  public function actionViewSomething(...) {

    $someCriteria=.....

    $items=SomeModel::model()->findAll($someCriteria);

    $this->render("someView", array('items'=>$items));

  }

View code:


  foreach ($items as $item) {

    //output pretty html with the properties of $item

  }

So far so good.

Now, [size="5"]enter fragment caching.[/size]

The way fragment caching is conceived in Yii, you do it mainly in the view, right?

So the code should look like this:

Controller code:


  same as before

View code:


  if ($this->beginCache(/*some dependencies*/)) {

    foreach ($items as $item) {

      //output pretty html with the properties of $item

    }

    $this->endCache()

  }

Now, the problem here is that you get only half (or less) of the benefit of fragment caching.

When the view is being cached, you are still doing useless database queries to gather the data, which you then don’t use.

Now you could do query caching in the controller, but that seems stupid to me, because you would have to duplicate the caching dependency logic, which would be error prone.

So, the only reasonable solution I’ve been able to come up with is the following:

Controller code:


  public function actionViewSomething(...) {

    $someCriteria=.....

    $this->render("someView", array('criteria'=>$comeCriteria));

  }

View code:


  if ($this->beginCache(/*some dependencies*/)) {

    $items=SomeModel::model()->findAll($criteria);

    foreach ($items as $item) {

      //output pretty html with the properties of $item

    }

    $this->endCache()

  }

That is, instead of having the controller gather the data, and pass it to the view, I let the controller decide the criteria, and pass them to the view. The view then uses the criteria to get the data, unless the fragment turns out to be available from cache, in which case the data doesn’t need to be retrieved.

This doesn’t sound too bad to me, but it is certainly strongly against the “keep the view stupid” phylosophy.

In the oversimplified example above it’s just a matter of moving a find() call from the controller to the view, but in real-life scenario this means moving a lot of logic from the controller to the view which doesn’t seem fair. But it’s the only way (that I can see) to exploit the efficiency advantage of fragment caching.

So the question is:

  • is this a weakness of the MVC paradigm itself? (in that it’s all about elegance but becomes weak when it comes to efficiency)?

  • or is it a weakness in the way caching is conceived in Yii (in that it is inherently incompatible with the principles of MVC good practice)?

  • or am I missing the right way of doing stuff, which would allow to use all the good of fragment caching while maintaining the view stupid (i.e. keeping the data retrieval logic in the controller)?

And if the latter, then what is that right way I’m missing?

Or perhaps somebody can point me to some resources where this is discussed in a general context (not necessarily in the case of Yii)?

Thanks in advance

m.

I was always surprised by the lack of response to this topic.

Now I’m curious: Does Yii 2.0 provide some new approach to this problem?

This seems to me a fundamental limitation that lies in the MVC design pattern, and I think yii is providing the decent solutions and you are doing the best in the given situation.

This is what the developers think of the MVC pattern and/or design patterns in general:

How does Yii Compare with Other Frameworks?

Doing caching in mind, the actual db accessing should be delayed as lazily as possible.

The basic workflow of MVC pattern is like the following:

[list=1]

[*]Controller receives a request

[*]Controller makes a model ready

[*]Controller passes the model to a view

[*]View renders the model

[/list]

Where should we do db accessing? In phase #2? Probably. But we sometimes want to do it in phase #4 as you did for fragment caching. Is it against MVC? Maybe. But is it a bad practice? I think it is not as long as the db accessing is decently hidden from the view.

Think about the data widgets like CGridView and CListView which we use in the view. We pass a model instance from a controller to a view, and a CGridView (or CListView) will use it to render a table (or a list). But the model instance passed to the view contains no actual data retrieved from database. It’s only a set of search parameters. The widget will call the “search” method of the model to get the data provider which is basically nothing more than a CDbCriteria that controls the actual db accessing. And the widget uses the data provider to get the actual data from db in order to render the items. All these things happen in phase #4. Do you think it is against MVC? I don’t think so. Or, to be precise, I don’t mind.

And as for Yii 2.0, there’s no fundamental change in the caching functionality as far as I know.