CakePHPのHtmlHelper->scriptBlock()やscriptStart()は$scripts_for_layoutで出力されない

CakePHP 1.2の事は分かりませんが、1.3系ではHtmlHelperでJavaScriptをViewテンプレートに書いていきます。scriptタグを書いても良いのかもしれませんが、HtmlHelperを使って書いたらスクリプトをバッファに貯めておいて にまとめて出力してくれます。まぁ折角なので使っていたら、layoutファイルに書いても $scripts_for_layout で出力されなくて困りました。

結論

layoutファイルに書いた scriptStart() scriptBlock() では array('inline' => true) じゃないとスクリプトが出力されない。
逆を言えば $options = array('inline' => false) を指定しても $scripts_for_layout で出力されない。
なので layoutファイルでは echo $html->scriptBlock('',array('inline'=>true)) や echo $html->scriptStart(array('inline'=>true)) を使う。*1

あと、とりあえず echo はしておいたほうが無難。

echo $html->scriptStart();
echo $html->scriptEnd();
echo $html->scriptBlock();

$html->scriptStart(array('inline' => false)) や $html->scriptBlock('',array('inline' => false)) で使ってるなら、$scripts_for_layout で出力されるので echo は要りません。ただ、array('inline' => true) に変更した場合は、script要素が return されてくるので、echo しないと何も出力されません。CakePHPのHelper は基本的に出力するには echo するスタイルで統一されてるので、これらもとりあえず echo しておいたほうがよさそう。じゃないと、array('inline' => true) に切り替えた時に何も出なくて無駄にはまりそう。

解説

簡単に言えば、layoutのレンダリング時に $scripts_for_layout に値をセットしてからレンダリングするのに、layoutそのものに書いた scriptBlock() はレンダリング時に処理されるからです。

<?php
function renderLayout($content_for_layout, $layout = null) {
	$layoutFileName = $this->_getLayoutFileName($layout);
	if (empty($layoutFileName)) {
		return $this->output;
	}

	$dataForLayout = array_merge($this->viewVars, array(
		'content_for_layout' => $content_for_layout,
		'scripts_for_layout' => implode("\n\t", $this->__scripts),
	));

	if (!isset($dataForLayout['title_for_layout'])) {
		$dataForLayout['title_for_layout'] = Inflector::humanize($this->viewPath);
	}

	if (empty($this->loaded) && !empty($this->helpers)) {
		$loadHelpers = true;
	} else {
		$loadHelpers = false;
		$dataForLayout = array_merge($dataForLayout, $this->loaded);
	}

	$this->_triggerHelpers('beforeLayout');
	$this->output = $this->_render($layoutFileName, $dataForLayout, $loadHelpers, true);

	if ($this->output === false) {
		$this->output = $this->_render($layoutFileName, $data_for_layout);
		trigger_error(sprintf(__("Error in layout %s, got: <blockquote>%s</blockquote>", true), $layoutFileName, $this->output), E_USER_ERROR);
		return false;
	}

	$this->_triggerHelpers('afterLayout');

	return $this->output;
}
http://api13.cakephp.org/view_source/view/#line-459
<?php
	$dataForLayout = array_merge($this->viewVars, array(
		'content_for_layout' => $content_for_layout,
		'scripts_for_layout' => implode("\n\t", $this->__scripts),
	));
http://api13.cakephp.org/view_source/view/#l-465

View::renderLayout() のここで $scripts_for_layout に出力するデータがセットされてます。

<?php
$this->output = $this->_render($layoutFileName, $dataForLayout, $loadHelpers, true);
http://api13.cakephp.org/view_source/view/#l-482

そして17行下のここで、layoutがレンダリングされてます。layoutに書いた $html->scriptBlock('',array('inline'=>false)) や $html->scriptStart(array('inline'=>false)) は、ここで初めて実行されますが、その時 $scripts_for_layout の内容は確定済みというわけです。

環境

Mac Mac OS X 10.5.8(Leopard
MAMP 1.7.2
CakePHP 1.3.6
php 5.2.6

*1:デフォルト値がarray('inline'=>true)なので省略可能