Fragment Caching and Model View Controller

I’ve created a simple fragment cache scenario:

Contoller:




	public function actionLatestrv()

	{

                $mydata=MyData::model()->findAll();

		$this->render('index', array('mydata'=>$mydata));

	}



View:




<?php if($this->beginCache($id, array('duration'=>3600))) {


        foreach ($mydata as $item) {

            echo $item->data;

        }


$this->endCache(); } ?>



I’d like to have the $mydata query skipped if the page generation was in the cache duration time.

The dummy solution’d be moving the query line to the view:

Contoller:




	public function actionLatestrv()

	{

		$this->render('index');

	}



View:




<?php if($this->beginCache($id, array('duration'=>3600))) {


        $mydata=MyData::model()->findAll();

        foreach ($mydata as $item) {

            echo $item->data;

        }


$this->endCache(); } ?>



So the query is also skipped due to the caching.

But it’d break the Model View Controller principles. What would be the right solution for this case?

Can I have any check about the view caching in the controller part in order to manage the data provision?

Yes, that would be what I would try.

OK, but how? How could I get any information in the controller about whether any part of the view would be cached or not?

Actually, this is the same question I’m solving now - how can I determine, from a controller, if a fragment with specific ID is present in the cache? I need to know that especially in the controller, to decide, whether to call few methods, which do some heavy SQL, to generate fresh data, or not to call them and just rely on the cache?

if (BeginFragment )… in the view is great, but even if it returns false (content is in the cache, so it outputs the cached content instead of the if{…} body), methods such as loadData… in the controller are still called, so it bothers the SQL server anyway.

I haven’t tested the following but maybe it works and could help you.

Controller:




public function actionLatestrv()

{

  $cache = Yii::app()->cache; // your cache component defined in the config

  if ($cache->getValue('mydata-key') { // value is in cache

    $mydata = $cache->getValue('mydata-key');

  } else { // value is not in cache

    $mydata = MyData::model()->findAll();

    $cache->setValue('mydata-key',$mydata,3600);

  }

  $this->render('index', array('mydata'=>$mydata));

}



Edit:

After posting the above code I found the COutputCache::checkContentCache() method which is maybe a better way to check if the data is already in cache. But I’m not sure how to use this method cause it accept no (key) parameter…

All controller methods and properties (including virtual properties) are available easily in view scripts, so you can add a virtual property to controller:




public function getMyData()

{

	// Run SQL queries to retrieve data

	return $result;

}



and access it in the view:




<?php if ($this->beginCache($id, array('duration' => 3600))) : ?>


<?php foreach ($this->myData as $item) : ?>

// display item data

<?php endforeach ?>


<?php $this->endCache() ?>

<?php endif ?>



This way unnecessary SQL queries can be avoided and view code can be kept "clean".

Hi all,

looks like you guys forgotten that Yii is using lazy loading, i.e. real SQL queries are being executed at the moment of first data usage (in


foreach ($mydata as $item) {

in our case).

So there is no need to change anything in you original code to avoid unnecessary queries.

That’s only true when you use a dataprovider. If any of find*() methods of CActiveRecord is called directly, queries will run immediately.

Ah, you are definitely right. Sorry for misinformation. Also in similar cases I’ll use your solution, as most clean and elegant.