TL;DR:
A report_event.subject_id == 0
usually is the result of either an ignored user agent, or an explicitely excluded route triggering an event which normally would have been logged by Magento.
See app/code/core/Mage/Log/etc/config.xml
:
<config>
<global>
<!-- : -->
<ignoredModules>
<entities>
<install/>
<adminhtml/>
<admin/>
</entities>
</ignoredModules>
<ignore_user_agents>
<google1>Googlebot/1.0 (googlebot@googlebot.com http://googlebot.com/)</google1>
<google2>Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)</google2>
<google3>Googlebot/2.1 (+http://www.googlebot.com/bot.html)</google3>
</ignore_user_agents>
<!-- : -->
</global>
</config>
Long answer
To explain why this happens, I need to go into greater detail.
When any Magento controller action is about to be dispatched, a controller_action_predispatch
event will be triggered, right before the dispatching of the action actually happens.
Having another look at app/code/core/Mage/Log/etc/config.xml
:
<config>
<frontend>
<events>
<controller_action_predispatch>
<observers>
<log>
<class>log/visitor</class>
<method>initByRequest</method>
</log>
</observers>
</controller_action_predispatch>
<!-- : -->
</events>
</frontend>
</config>
shows, that the Mage_Log
module defines an observer for the controller_action_predispatch
event, represented by the method Mage_Log_Model_Visitor::initByRequest()
:
public function initByRequest($observer)
{
if ($this->_skipRequestLogging || $this->isModuleIgnored($observer)) {
return $this;
}
$this->setData($this->_getSession()->getVisitorData());
$this->initServerData();
if (!$this->getId()) {
$this->setFirstVisitAt(now());
$this->setIsNewVisitor(true);
$this->save();
}
return $this;
}
The property $this->_skipRequestLogging
will be true
, when the user agent sending the current request matches one of the <ignored_user_agents />
(GoogleBot).
The method $this->isModuleIgnored()
returns only true
, if the request route matches one of the <ignoredModules />
(that is install/
, adminhtml/
or admin/
; in EE also api/
).
The point is, if one of these both returns true
, then the observer immediately exits, that is, the visitor will not be created/saved at all.
Now, to further explain using your catalog_product_view
case as example, have a look at another config file, app/code/core/Mage/Reports/etc/config.xml
this time:
<config>
<frontend>
<events>
<catalog_controller_product_view>
<observers>
<reports>
<class>reports/event_observer</class>
<method>catalogProductView</method>
</reports>
</observers>
</catalog_controller_product_view>
<!-- : -->
</events>
</frontend>
</config>
which defines an observer for catalog_controller_product_view
events, represented by the
Mage_Reports_Model_Event_Observer::catalogProductView()
method:
public function catalogProductView(Varien_Event_Observer $observer)
{
$productId = $observer->getEvent()->getProduct()->getId();
Mage::getModel('reports/product_index_viewed')
->setProductId($productId)
->save()
->calculate();
return $this->_event(Mage_Reports_Model_Event::EVENT_PRODUCT_VIEW, $productId);
}
The last line of this observer calls the _event()
method:
protected function _event($eventTypeId, $objectId, $subjectId = null, $subtype = 0)
{
if (is_null($subjectId)) {
if (Mage::getSingleton('customer/session')->isLoggedIn()) {
$customer = Mage::getSingleton('customer/session')->getCustomer();
$subjectId = $customer->getId();
}
else {
$subjectId = Mage::getSingleton('log/visitor')->getId();
$subtype = 1;
}
}
$eventModel = Mage::getModel('reports/event');
$storeId = Mage::app()->getStore()->getId();
$eventModel
->setEventTypeId($eventTypeId)
->setObjectId($objectId)
->setSubjectId($subjectId)
->setSubtype($subtype)
->setStoreId($storeId);
$eventModel->save();
return $this;
}
Have a look at the else
block, especially this line:
$subjectId = Mage::getSingleton('log/visitor')->getId();
When a visitor doesn't gets created/saved at all, because of an ignored user agent, or an excluded route, then $subjectId
will be null
.
Since the table column subject_id
is defined as NOT NULL (see app/code/core/Mage/Reports/sql/reports_setup
SQL scripts), 0
will finally be saved.
Thats because of the Data Type Default Values of MySQL.