renderDynamic seems not working

I added renderDynamic() in the label text of the CMenu widget and found it is nor working.




   $this->widget('zii.widgets.CMenu',array(

    	'htmlOptions'=>array('style'=>'list-style-type:none;'),

    	'submenuHtmlOptions'=>array('style'=>'list-style-type:none;'),

    	'encodeLabel'=>false,

    	'items'=>array(

   		:

            	array('label'=>'Status',

                    	'url'=>_u('katsudou'),

                    	'itemOptions'=>array('class'=>'l_menu1'),

                    	'items'=>array(

                            	array('label'=>'Marketing'.$this->renderDynamic('dynamicTeamMemberCount','Marketing'),

                                    	'url'=>array('/dep', 'bu'=>'Marketing'),

                                    	'itemOptions'=>array('class'=>'l_menu2')

                            	),






function dynamicTeamMemberCount($bu)

{

    	$criteria=new CDbCriteria;

    	$criteria->compare('bu',$bu,false);

    	$model = new Team;

    	return "(".$model->count($criteria).")";

}



The dynamic contents were displayed before the right place. I would like to have a separated evaluation/display of the dynamic contents as these do not seem to be separated by the current implementation.

What I got is as follows. I salvaged it from the APC cache.


<###dynamic-0###><###dynamic-1###><###dynamic-2###><###dynamic-3###><ul style="list-style-type:none;" id="yw2">

<li class="l_menu1"><a href="http://www.xxx.com/topmsg/ceo/Jp/20120307.html">Top message<br /></a></li>

 <li class="l_menu1"><a href="/demos/jgkk/index.php?r=site/page&amp;view=strategy">Strategy</a></li>

 <li class="l_menu1"><a href="/demos/jgkk/index.php?r=organization">Organization</a></li>


*** actual places are below ***

 

As you can see above, four "<###dynamic-n###>" were displayed before the actual places. I assume that renderDynamic() evaluates the expression and it is displayed immediately. We may need another method that the display will be delayed till the actual display. Does it make sense?

Hi mocapapa,

Yes, it makes sense to me.

The place holder string is immediately echoed in renderDynamic, as you see in the source:

http://www.yiiframework.com/doc/api/1.1/CController/#renderDynamic-detail

We need a function returning the place holder string instead of echoing it.

I think you can write a wrapper function to capture the echoed output and return it as a string.

And why you don’t use dynamicTeamMemberCount() directly without renderDynamic?




...

'items'=>array(

       array('label'=>'Marketing'.$this->dynamicTeamMemberCount('Marketing'),

   ...                                    




Thank you for the reply. I am going to write an alternative method other than renderDynamic.

I understand that I should use CController::renderDynamic in order to have a dynamic result. If I do not use it, the result must be cached.

You can add caching to the dynamicTeamMemberCount function:





function dynamicTeamMemberCount($bu)

{

   

   $cacheId =  'teamMemberCount_'.$bu;


   $value=Yii::app()->cache->get($cacheId);


   if($value===false)

   {

    // regenerate $value because it is not found in cache

     $criteria=new CDbCriteria;

     $criteria->compare('bu',$bu,false);

     $model = new Team;

     $value = "(".$model->count($criteria).")";


     //save it in cache for later use:

     Yii::app()->cache->set($cacheId,$value);

   }    


   return $value;

}




Thanks for the idea. But it seems to be the opposite direction.

What I wanted is to cache the whole page in a APC cache, with some exceptions such as "login user name" and "team member count". Therefore I put COutputCache in the filters method in the controller as below.

http://www.yiiframew…en/caching.page

Then, I used renderDynamic for the exceptions. It means I do not want to cache the exceptions. The user name works fine, as the evaluation time and the display time is the same, though showing the team member count does not work fine as the evaluation time and the display time should differ.

I also think echoing in the renderDynamic is not appropriate for this use case as softark pointed out.

Hi softark,

Based on your idea, I added a method called ‘renderDynamicDelay’, though the name may not be appropriate, in the CController and found it worked. Thanks.




    	public function renderDynamicDelay($callback)

    	{

            	$n=count($this->_dynamicOutput);

            	$params=func_get_args();

            	array_shift($params);

            	$this->renderDynamicInternal($callback,$params);

            	return "<###dynamic-$n###>";

    	}



Qiang, could you consider adding this for this use-case?

Ah, you tweaked the CController.php in the core?

I thought that you would be unable to access "_dynamicOutput" property from your own controller, because it is declared private.

I was thinking of a wrapper like the following:




// in your Controller.php

public function renderDynamicDelay($callback)

{

    $params = func_get_args();

    array_shift($params);

    ob_start();

    $this->renderDynamic($callback, $params);

    $ret = ob_get_contents();

    ob_end_clean();

    return $ret;

}



Not tested.

Thank you sortark, it works fine as well!