Appending to clips

[FYI: I posted my proposed mods to the Yii source in the first response to this post.]

For Javascript and CSS that pertains to only one view file, I want to put that Javascript and CSS in the view file, and I’d like the layout to render this in the head tag.

I’ve discovered controller clips via the CClipWidget. This allows conveying HTML to the layout, but it doesn’t seem to allow for accumulating HTML for the layout. I need to accumulate so that partials can each provide their own Javascript and CSS, in addition to allowing their including views to provide Javascript and CSS. That way my HTML head tag can include all the bits needed by each of the views in a generic way, without having to code for knowledge of all the partials.

I don’t see an existing mechanism for accomplishing this application-wide. I’m thinking that it could be done via an “appendClip()” method on the controller, complementing the existing “beginClip()”. For now I’ve concocted a hack that accomplishes this by first having the view stage the clip-to-append to a temporary ‘clipboard’ clip. It’s messier than I would like.

Here’s my interim “appendClipboard()” helper method:




	/**

	 * appendClipboard() appends the clip named 'clipboard' to the clip of

	 * the provided name, creating that clip if it doesn't already exist.

	 *

	 * @param object $controller View's controller

	 * @param string $targetClip Name of clip to which to append clipboard

	 */

	 

	public static function appendClipboard($controller, $targetClip)

	{

		$oldClip = '';

		if(isset($controller->clips[$targetClip]))

			$oldClip = $controller->clips[$targetClip];

		$controller->beginClip($targetClip);

		echo $oldClip;

		echo $controller->clips['clipboard'];

		$controller->endClip($targetClip);

	}



Here’s how I use this interim method:




views/layouts/main.php:


	<head>

		...

	<?php if(isset($this->clips['head_code']))

		echo $this->clips['head_code']; ?>

	</head>


views/mymodel/create.php:


	<?php $this->beginClip('clipboard'); ?>

		<!--Head code from create.php-->

	<?php $this->endClip('clipboard'); ?>

	<?php JoesHtml::appendClipboard($this, 'head_code'); ?>


views/mymodel/_form.php:


	<?php $this->beginClip('clipboard'); ?>

		<!--Head code from _form.php-->

	<?php $this->endClip('clipboard'); ?>

	<?php JoesHtml::appendClipboard($this, 'head_code'); ?>


Resulting rendering:


	<head>

		...

		<!--Head code from create.php-->

		<!--Head code from _form.php-->

	</head>



Ideally, Yii would allow me to simply call appendClip() in place of beginClip() to accomplish this, without requiring the above appendClipboard() hack. I’m guessing that this would require a special CAppendingClipWidget, constructed within appendClip().

I went ahead and implemented appendClip() on CBaseController. It was a cinch.

First, add the lines labelled JTL below to CClipWidget:




class CClipWidget extends CWidget

{

	public $renderClip=false;

	public $appending=false; /*JTL*/


	public function init()

	{

		ob_start();

		ob_implicit_flush(false);

		/*BEGIN JTL*/

		if($this->appending)

		{

			$clips = $this->getController()->getClips();

			if(isset($clips[$this->id]))

				echo $clips[$this->id];

		}

		/*END JTL*/

	}

	...

}



Next, add this appendClip() method to CBaseController:




	/**

	 * Begins recording for concatenation to the end of a clip, if the clip

	 * already exists. If the clip does not already exists, this method

	 * records a new clip and behaves identically to beginClip().

	 * @param string $id the clip ID.

	 * @param array $properties initial property values for {@link CClipWidget}.

	 * @see beginClip

	 */

	public function appendClip($id,$properties=array())

	{

		$properties['id']=$id;

		$properties['appending'] = true;

		$this->beginWidget('CClipWidget',$properties);

	}



That’s it! Seems to be working just fine.

This morning I have a much cleaner way of doing the interim solution:




	/**

	 * appendClip() begins recording for concatenation to the end of a clip,

	 * if the clip already exists. If the clip does not already exists, this

	 * method begins recording a new clip of the given name.

	 *

	 * @param object $controller View's controller

	 * @param string $targetClip Name of clip to which to append clipboard

	 */

	 

	public static function appendClip($controller, $targetClip)

	{

		$oldClip = '';

		if(isset($controller->clips[$targetClip]))

			$oldClip = $controller->clips[$targetClip];

		$controller->beginClip($targetClip);

		echo $oldClip;

	}	



Its use looks like this:




        <?php JoesHtml::appendClip($this, 'head_code'); ?>

                <!--Head code from _form.php-->

        <?php $this->endClip('head_code'); ?>



appendClip() belongs on the controller, but this works nicely.

Nice work. Thanks!