Magento's CRUD models have three basic class types.
A "model" is what you'll most commonly use. This represents data of a particular type in a database agnostic way (product, category, CMS Page, Review, etc.)
A "resource model" is a class that, behind the scenes, does the actual fetching of data from Magento. Every model has a resource model that is used to load a single instance of a model from the database.
A "collection" is a class that loads an array like structure of multiple models based on a set of rules. Think of it like a SQL WHERE clause
.
Confusing things somewhat is, behind the scenes, Magento also considers a collection to be a resource model. So there's collection resource models and individual resource models.
Generally speaking, when you want to load a specific item, you use a model. When you want to load a number of items, you use a collection resource model.
//loads one
$product = Mage::getModel('catalog/product')->load($id);
//loads many products where price is greater than 100
$products = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('*') //load all fields, see EAV below
->addAttributeToFilter('price', array('gt'=>'100'));
foreach($products as $product)
{
var_dump($product->getSku());
}
One thing to keep in mind about loading via a collection resource model is each individual model's afterLoad
method won't be called automatically, which means some data may not be loaded.
The individua resource model classes are usually not accessed directly. However, occasionally a specific model will have a resource model with a useful method on it. These you'll discover over time using the system.
Finally, there's one more distinction to be made. Magento's regular CRUD models have a special syntax for loading via something that's not the numeric ID. For example, here's how to load a CMS page model by title
$page = Mage::getModel('cms/page')->load('Home page','title');
var_dump($page->getData());
However, a few Magento models are EAV models. That stands for "entity attribute value", and is a fancy way of saying these models don't have a fixed set of fields. This includes product models, and category models. This feature is what lets you pick different attributes for different types of products. Each field on an EAV model is known as an attribute. The above syntax won't work with these models, instead you need to use the loadByAttribute
method.
$product = Mage::getModel('catalog/product')->loadByAttribute('sku', 'n2610');
Regarding your specific problem, if you're getting an error with the specific text "method undefined", then something is very wrong with your system. Even if the getSku
method doesn't exist, Magento has magic setter and getter methods which ensure a get
call on any model will always return something, even if it's null.
What's more likely is you're getting an error like the following
Call to a member function getSku() on a non-object
This error is happening because the loadByAttribute
method returns false
if it can't load the object.
$product = Mage::getModel('catalog/product')->loadByAttribute('sku', 'this-is-not-a-sku');
var_dump($product); //prints false
Try something like this in your code.
$product = Mage::getModel('catalog/product')->loadByAttribute('sku', 'sku-value');
if($product)
{
var_dump($product->getName());
var_dump($product->getSku());
var_dump($product->getData());
}
else
{
var_dump("Could not load product");
}
In additional to the conditional, notice the getData()
method. This will return an array of all the object's data as an array.
Hopefully this is enough to get you moving. Good luck!
Posting this as an answer, since comments just don't have the space.
It is not an answer for your actual question, but an answer none the less.
In my opinion you are approaching the problem the wrong way.
My question still stands. Why not use core functionality? What does you review module give that core do not have?
The sync? Easy enough to sync into magento's core review system, and I give some examples at the end of my ramblings.
One golden rule, when doing modules, is to enhance, not replace. If there is core functionality that closely matches your planned solution, extend it, don't replace it. Core is stable, use it :) You will end up writing a lot less code, and have a more stable module in the end. Don't re-invent the wheel. It is round, and it works well :)
Magento is a complex application, and adding 3rd party solutions (for existing functionality) needlessly adds to that complexity. This is true for any complex application, not just magento.
You state that you cannot use magento's core review functionality, as you need to sync with an external system, but it looks to me (from the code snippet given) that you are pulling in all the reviews, into the magento database, into a custom table. How you do this is not known, but I assume it is done via a cron/periodic sync. So in the end, you have just duplicated what core already has, since you are not displaying the reviews from the external system directly onto the page. You are saving it in magento db first. (which is the right way, your implementation is not quite right ;) )
Looking at your code, I can already see an inherit issue. You are using product SKU's (textual data) as a means to find the data that you need to display. You are also incorrectly calling this the $product_id
(which by the name of the variable, suggests a numeric value) In magento the product_id
(or entity_id
really) is the key value of the magento product, and you should not make matters confusing by calling a SKU field the product_id
field.
Your table, should in fact not even have the SKU as part of it's structure, but a foreign key value, referencing back to the main product_entity table in magento, ideally set with 'ON DELETE CASCADE'
Using the SKU in a textual match will cause you heaps of issues down the line. What do you do if your client decides to rename all their product sku's? (this can happen, and I have seen it happen multiple times)
But enough of that, and lets get back to the issue in not using core review system, and your sync issue.
How you get the data from the external system (API, direct db connect etc) is beyond the scope of this example, and should be the only code/module you should really need to write. (the connector to/from that system)
In my example I went with a cron based solution, thus the external review system reviews will be pulled in every x minutes)
The code is of the top of my head, thus can be incomplete, or have mistakes, it serves purely as an example of how to do it using core review system.
I also gather the only link between that system and your products is the SKU (hence why you used it as your custom review table lookup key field)
class MyModule_ExternalReviewSystem_Model_Cron {
public function importReviews($schedule){
$externalReviews = CODE TO CONNECT TO EXTERNAL SYSTEM,
AND PULL IN REVIEWS AS AN ARRAY/COLLECTION/DB
ROW ITERATOR HERE.
foreach ($externalReviews as $key => $review) {
$productId = $product->getIdBySku($externalReviews['sku']);
if ($productId) {
$data = array('nickname'=>$externalReviews['name'],
'detail'=>$externalReviews['review'],
'title'=>$externalReviews['subject']);
$review = Mage::getModel('review/review')->setData($data);
$review->setEntityId($review->getEntityIdByCode(Mage_Review_Model_Review::ENTITY_PRODUCT_CODE))
->setEntityPkValue($productId)
->setStatusId(Mage_Review_Model_Review::STATUS_APPROVED)
->setCustomerId(null)
->setStoreId(Mage::app()->getStore()->getId())
->setStores(array(Mage::app()->getStore()->getId()))
->save();
} else {
mage::log("Could not import {$externalReviews['sku']} - not found in local db")
}
}
}
}
Done, now your external system review is inside magento's review system, and magento core will take care of all the rest.
To sync back from core review system is really easier. All you need to do is create an observer which listens to the review model save event, and sync that data back on a save. Thus you would listen to the event review_save_after
, which gets passed the entire review object, from which you can get all the product details, the review data etc.
Using core functionality results in less code, less complexity, less errors, and way way less work for yourself. Not to mention time and money saved for your client :)
Hope this helps.
Best Answer
The reporting collections work a bit differently that the
Mage_Core_Model_Abstract
/CRUD collections. The CRUD collections will have a SQL query assigned immediately at instantiation. The reporting collection, however, initializes its SQL statement during its load operation. You can see this in theload
method down in the abstract base class for all reporting collections.There's no select until
_initSelect
is called.The general approach you'll want to take here is
Create a new resource model class which extends the
Mage_Reports_Model_Resource_Report_Collection_Abstract
classDefine a
_applyCustomFilter
method in your classand then use your new resource model. Your
_applyCustomFilter
method would look something like this.