I am pretty new to Magento, and am creating a module to integrate third party reviews with a website I am developing.
It's all going well, but I've hit a snag, and I'm at a bit of a loose end with what exactly is going wrong.
I have to get reviews from the database corresponding to a certain product. After some googling, the query I came up with was this:
$reviews = Mage::getModel('ekomiwrapper/productReview')
->getCollection()
->addFieldToFilter('product_id', $product_id)
->setPageSize($reviews_count)->load();
When I visit a product page, however, if there are no reviews associated with that product, the query returns nothing, and I get the message saying there ar no reviews associated. But when at least one review is set for a product, it returns every review.
I logged the SQL query that was being executed with
Mage::log($reviews->getSelect()->__toString());
and the output was
SELECT `main_table`.* FROM `productreview` AS `main_table` WHERE (product_id = 'JDA 955760')
which to my knowledge selects only the reviews that match the product ID. Indeed, when I run this query manually, it does just that. I can only conclude that this is not the query being run.
If anyone can shine some light on what I am doing wrong here, or point me to a resource that explains the Magento ORM better, then that would really help.
Many thanks.
Update
I'm still not completely sure what the exact root of the problem was, but by replacing Mage::getModel('ekomiwrapper/productReview')->getCollection()
with Mage::getResourceModel('ekomiwrapper/productReview_collection')
.
I made a small sandbox:
require('app/Mage.php');
$app = Mage::app();
$sku = 'FOOBAR';
$reviews = Mage::getModel('ekomiwrapper/productReview')->getCollection();
foreach($reviews->addFieldToFilter('product_id', $sku)->setPageSize(1)->setCurPage(2) as $review) {
echo $review->getRating()."\n";
}
$reviews = Mage::getResourceModel('ekomiwrapper/productReview_collection');
foreach ($reviews->addFieldToFilter('product_id', $sku)->setPageSize(1)->setCurPage(2) as $review) {
echo $review->getRating()."\n";
}
The only thing different is in the way I'm getting a collection, and sure enough, the first loop returns all items in the database, and the second returns only one, as it is supposed to. The same problem I had originally. Weird, huh?
So why is this happening? I decided to take a deeper look at getCollection()
. At this point it's worth noting that after spending a long time searching, I'm still not sure what's wrong. I'm including what I learned just in case it helps someone in the future.
public function getCollection()
{
return $this->getResourceCollection();
}
/**
* Get collection instance
*
* @return object
*/
public function getResourceCollection()
{
if (empty($this->_resourceCollectionName)) {
Mage::throwException(Mage::helper('core')->__('Model collection resource name is not defined.'));
}
return Mage::getResourceModel($this->_resourceCollectionName, $this->_getResource());
}
But a var_dump
of my model shows that _resourceCollectionName
is defined, and defined to be ekomiwrapper/productReview_collection
. So that's not the problem. Perhaps it's in the getResource
call:
/**
* Get resource instance
*
* @return Mage_Core_Model_Mysql4_Abstract
*/
protected function _getResource()
{
if (empty($this->_resourceName)) {
Mage::throwException(Mage::helper('core')->__('Resource is not set.'));
}
return Mage::getResourceSingleton($this->_resourceName);
}
So that's not the problem either, because _resourceName
is defined correctly as well. Let's continue down the stack to see if we can find our problem.
So let's take a look at Mage::getResourceModel
. Maybe something funny is going on there:
/**
* Retrieve object of resource model
*
* @param string $modelClass
* @param array $arguments
* @return Object
*/
public static function getResourceModel($modelClass, $arguments = array())
{
return self::getConfig()->getResourceModelInstance($modelClass, $arguments);
}
And Mage_Core_Model_Config::getResourceModelInstance()
:
/**
* Get resource model object by alias
*
* @param string $modelClass
* @param array $constructArguments
* @return object
*/
public function getResourceModelInstance($modelClass='', $constructArguments=array())
{
$factoryName = $this->_getResourceModelFactoryClassName($modelClass);
if (!$factoryName) {
return false;
}
return $this->getModelInstance($factoryName, $constructArguments);
}
I'm going to ignore finding the class name, since I know that's working. But it looks like resource models are just models in the end, which comports with my understanding thus far.
Mage_Core_Model_Config::getModelInstance()
/**
* Get model class instance.
*
* Example:
* $config->getModelInstance('catalog/product')
*
* Will instantiate Mage_Catalog_Model_Mysql4_Product
*
* @param string $modelClass
* @param array|object $constructArguments
* @return Mage_Core_Model_Abstract|false
*/
public function getModelInstance($modelClass='', $constructArguments=array())
{
$className = $this->getModelClassName($modelClass);
if (class_exists($className)) {
Varien_Profiler::start('CORE::create_object_of::'.$className);
$obj = new $className($constructArguments);
Varien_Profiler::stop('CORE::create_object_of::'.$className);
return $obj;
} else {
return false;
}
}
This function is finally where the collection is created. If you can remember what it was we were doing originally. I can, but only just. Now we do the same thing for the second, working method to find a difference.
We start with Mage::getResourceModel
, which we've already seen.
When we instantiated the collection by Mage_Core_Model_Abstract::getResourceCollection()
, we passed a second argument, which was the resource model of the model class. I'm not sure why, but it gets passed to the collection's constructor. I don't use it, although perhaps I should. Anyway, after all that, I'm certain that my collection is a singleton and that it's the right type. Sure enough, I'm vindicated by a call to get_class()
.
Incidentally, the code that works, with a single call to Mage::getResourceModel()
creates an object of the same type, but not the same instance. I think that's what two objects not being equal in PHP means, anyway. So we get the resource model location then we pass it to a normal model factory. We're still in sync here. Both of the calls to this function return the right classname. So what happens next?
Another Update
After the last exercise proved fruitless, I decided to run $collection->load(true)
against my collection, to see the queries being generated (thanks to Vinai for this tip).
The second collection outputs its SQL just fine, with its limit and offset. The first one does nothing. It's running its query, though, because I can see the output plain as day.
So my colleague advises me to check if the first collection is already loaded. And it is. $reviews->isLoaded()
returns true.
So I now have a new question. Why is my collection loading as soon as it gets instantiated when I do it by Mage::getModel()::getCollection()
?
addFieldToFilter()
and theload
/setPageSize
? This doesn't make sense to me too.