Reputation: 6457
Magento 1.4.1.1
Back in history, app/code/core/Mage/GoogleAnalytics/Block/Ga.php was copied over to app/code/local/Mage/GoogleAnalytics/Block/Ga.php and changes were made so the protected function _toHtml() {}
function would produce a proper Google Analytics asynchronous javascript entry on the page. The original had issues that didn't post tracking information to Google properly for this website. This rewrite was fully functional with the desired result.
In order to clean things up, it was decided to modularize this change to a local namespace module. The following files were created...
apps/etc/modules/Chief_GoogleAnalytics.xml
<?xml version="1.0"?>
<config>
<modules>
<Chief_GoogleAnalytics>
<active>true</active>
<codePool>local</codePool>
</Chief_GoogleAnalytics>
</modules>
</config>
apps/code/local/Chief/GoogleAnalytics/etc/config.xml
<?xml version="1.0"?>
<config>
<modules>
<Chief_GoogleAnalytics>
<version>0.1.0</version>
</Chief_GoogleAnalytics>
</modules>
<global>
<blocks>
<googleanalytics>
<rewrite>
<ga>Chief_GoogleAnalytics_Block_Ga</ga>
</rewrite>
</googleanalytics>
</blocks>
</global>
</config>
app/code/local/Chief/GoogleAnalytics/Block/Ga.php
/**
* GoogleAnalitics Page Block
*
* @category Chief
* @package Chief_GoogleAnalytics
* @author Magento Core Team <[email protected]>
*/
class Chief_GoogleAnalytics_Block_Ga extends Mage_GoogleAnalytics_Block_Ga
{
/**
* Prepare and return block's html output
*
* @return string
*/
protected function _toHtml()
{
if (!Mage::getStoreConfigFlag('google/analytics/active')) {
return '';
}
$this->addText('
<!-- BEGIN GOOGLE ANALYTICS CODE -->
<script type="text/javascript">
//<![CDATA[
var _gaq = _gaq || [];
_gaq.push(["_setAccount", "' . $this->getAccount() . '"]);
_gaq.push(["_trackPageview", "'.$this->getPageName().'"]);
(function() {
var ga = document.createElement(\'script\'); ga.type = \'text/javascript\'; ga.async = true;
ga.src = (\'https:\' == document.location.protocol ? \'https://ssl\' : \'http://www\') + \'.google-analytics.com/ga.js\';
var s = document.getElementsByTagName(\'script\')[0]; s.parentNode.insertBefore(ga, s);
})();
//]]>
</script>
<!-- END GOOGLE ANALYTICS CODE -->
');
$this->addText($this->getQuoteOrdersHtml());
if ($this->getGoogleCheckout()) {
$protocol = Mage::app()->getStore()->isCurrentlySecure() ? 'https' : 'http';
$this->addText('<script src="'.$protocol.'://checkout.google.com/files/digital/ga_post.js" type="text/javascript"></script>');
}
return parent::_toHtml();
}
}
Instead of the single corrected script entry as was normal when it was coming from app/code/local/Mage/GoogleAnalytics/Block/Ga.php, I now get a duplication. The Chief_GoogleAnalytics Block overwrite comes first, followed by the old stock junk code from app/code/core/Mage/GoogleAnalytics/Block/Ga.php. There is a single layout XML file for Google Analytics that defines it to show in after_body_start, so that's working. Here's the duplication where the new Block overwrite and old Block are showing. What gives?
<!-- BEGIN GOOGLE ANALYTICS CODE -->
<script type="text/javascript">
//<![CDATA[
var _gaq = _gaq || [];
_gaq.push(["_setAccount", "UA-xxxxxx-x"]);
_gaq.push(["_trackPageview", "/"]);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
//]]>
</script>
<!-- END GOOGLE ANALYTICS CODE -->
<!-- BEGIN GOOGLE ANALYTICS CODE -->
<script type="text/javascript">
//<![CDATA[
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
})();
var _gaq = _gaq || [];
_gaq.push(["_setAccount", "UA-xxxxxx-x"]);
_gaq.push(["_trackPageview", "/"]);
//]]>
</script>
<!-- END GOOGLE ANALYTICS CODE -->
page.phtml entries that load the block.
<?php echo $this->getChildHtml('after_body_start') ?>
googleanalytics.xml layout
<layout version="0.1.0">
<default>
<!-- Mage_GoogleAnalytics -->
<reference name="after_body_start">
<block type="googleanalytics/ga" name="google_analytics" as="google_analytics" />
</reference>
</default>
</layout>
Added forensics by use of Mage::Log()
with $this->getData()
in strategic locations
Since we're passing an array object around here, it's a little more complicated than working with a string.
Start of _toHtml() function
2012-06-24T21:02:04+00:00 DEBUG (7): Array
(
[type] => googleanalytics/ga
[module_name] => Chief_GoogleAnalytics
)
Immediately cause it to load parent::_toHtml();
with $original_output =parent::_toHtml
2012-06-24T21:02:04+00:00 DEBUG (7): Array
(
[type] => googleanalytics/ga
[module_name] => Chief_GoogleAnalytics
[account] => UA-xxxxxx-x
[page_name] => /
[text] =>
<!-- BEGIN GOOGLE ANALYTICS CODE -->
<script type="text/javascript">
//<![CDATA[
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
})();
var _gaq = _gaq || [];
_gaq.push(["_setAccount", "UA-xxxxxx-x"]);
_gaq.push(["_trackPageview", "/"]);
//]]>
</script>
<!-- END GOOGLE ANALYTICS CODE -->
)
To prevent this from turning into a ;tldr, logging $this->getData();
where the return was at the end, spits out both javascripts, but in reverse of the original.
$this->getData();
without ever referencing parent::_toHtml
passes the following which is what we want for a returned object. One script with proper identifiers...
2012-06-24T21:06:24+00:00 DEBUG (7): Array
(
[type] => googleanalytics/ga
[module_name] => Chief_GoogleAnalytics
[account] => UA-xxxxxx-x
[page_name] => /aircraft
[text] =>
<!-- BEGIN GOOGLE ANALYTICS CODE -->
<script type="text/javascript">
//<![CDATA[
var _gaq = _gaq || [];
_gaq.push(["_setAccount", "UA-xxxxxx-x"]);
_gaq.push(["_trackPageview", "/aircraft"]);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
//]]>
</script>
<!-- END GOOGLE ANALYTICS CODE -->
)
Upvotes: 2
Views: 1434
Reputation: 166086
First, a seemingly-but-not-really-pedantic naming convention. When you put files in local
that's not a rewrite, it's a code pool override. When you create a custom module and use the rewrite
node, that's a class rewrite. These are two different actions, and each one behaves differently. These differences are why you're seeing the behavior you're seeing.
When you use a class override, you're saying
Hey Magento, instead of including your class file for a
Mage_GoogleAnalytics_Block_Ga
, include mine instead
This completely replaces the original class in the system.
When you use a class rewrite, you're saying
Hey Magento, when you instantiate a
googleanalytics/ga
(Mage_GoogleAnalytics_Block_Ga
) block object, instantiate the object from my class instead.
Then you, by having your class extend the original Mage_GoogleAnalytics_Block_Ga
class have an object that behaves the same as the original object, and you can add your methods. However, when you add your methods their parent method is the original block, which is different from an override.
Step one would be removing the local code pool file
app/code/local/Mage/GoogleAnalytics/Block/Ga.php
It sounds like you've already done this. Next, there's the method in your rewritten class. When you call the parent::_toHtml();
method
protected function _toHtml()
{
//... your stuff here ...
return parent::_toHtml();
}
you're telling Magento "Hey, do whatever the original object would have done". With blocks and the _toHtml
method, that means producing the same output. That's why you're getting the "old stock junk code". The contract for the _toHtml
method is "whatever string this returns, will be included as the block output". Your new code is also being included because you're using the addText
method, which your parent method is aware of.
So, if you want to change the behavior of _toHtml
in a block rewrite, you need to do something like this
protected function _toHtml()
{
$original_output = parent::_toHtml();
$string = '';
//... your code here which ultimately generates your output
//and places it in $string
return $string;
}
By calling the parent method first, you ensure that anything the block does from a state point of view still happens (which a block shouldn't do, but, well, we know how that goes). Then, you return your string instead.
Upvotes: 6
Reputation: 23205
DDx: does commenting out the <block type="googleanalytics/ga" ... />
code from googleanalytics.xml
cause all output to disappear?
Assuming the local codePool override file has been removed, the issue certainly is return parent::_toHtml()
. The CE 1.4.1.1 version of the parent (core) method uses addText()
which is derived from Mage_Core_Block_Text->_toHtml()
. addText()
will append content to _data['text']
, and then the method will return this in course of rendering. So, your override is setting some string content to _data['text']
, and then when you call the parent method, it adds its content to _data['text']
and then calls Mage_Core_Block_Text->_toHtml()
, which simply returns the value of _data['text']
.
Generally it's a good idea to call the overridden method, but this is a case where it's not going to work. Given that this method was changed in 1.4.2.0 to just return the string from a local variable, you should be fine to ignore the parent. If this doesn't work out for some bizarre reason it would be possible to use layout XML to replace the core block instance with another.
Upvotes: 2