Magento by default stores related, upsell and cross sell products in general scope. Is there an extension to store them at store scope? Does anyone have experience implementing this feature?
2 Answers
The saving of the relation is done in the Mage_Adminhtml_Catalog_ProductController
.
You can trace it via the saveAction
method to the _initProductSave
method where around line 640 it's set in the product object.
In the Mage_Catalog_Model_Product
class, method _afterSave
on line 540
the method Mage_Catalog_Model_Product_Link::saveProductRelations
is called that calls Mage_Catalog_Model_Resource_Product_Link::saveProductLinks
with the argument $product
, $data
and $typeId
that is actually persisted into the database which happens around line 99.
Now, with all that back tracing we can actually do something to solve your question.
To do this properly write your own module, a tutorial about that you can find here on Tutsplus.com.
You'll also need an install script. Check out this Inchoo tutorial about that.
So, first let's add a column to the catalog_product_link
table the data is actually stored in (thank you @MagePsycho for that diagram)
$installer = $this;
$connection = $installer->getConnection();
$installer->startSetup();
$installer->getConnection()
->addColumn($installer->getTable('catalog/product_link'),
'store_id',
array(
'type' => Varien_Db_Ddl_Table::TYPE_INT,
'nullable' => false,
'default' => 0,
'comment' => 'Store ID'
)
);
$installer->endSetup();
Now we'll rewrite the resource model actually saving the data to also include the store ID.
Add the following to your modules config.xml
, in the global
node.
<models>
<catalog_resource>
<rewrite>
<product_link>[Namespace]_[Module]_Model_Resource_Catalog_Product_Link</product_link>
</rewrite>
</catalog_resource>
</models>
with the following contents
class [Namespace]_[Module]_Model_Resource_Catalog_Product_Link extends Mage_Catalog_Model_Resource_Product_Link
{
/**
* Save Product Links process
*
* @param Mage_Catalog_Model_Product $product
* @param array $data
* @param int $typeId
* @return Mage_Catalog_Model_Resource_Product_Link
*/
public function saveProductLinks($product, $data, $typeId)
{
if (!is_array($data)) {
$data = array();
}
$attributes = $this->getAttributesByType($typeId);
$adapter = $this->_getWriteAdapter();
$bind = array(
':product_id' => (int)$product->getId(),
':link_type_id' => (int)$typeId
);
$select = $adapter->select()
->from($this->getMainTable(), array('linked_product_id', 'link_id'))
->where('product_id = :product_id')
->where('link_type_id = :link_type_id');
$links = $adapter->fetchPairs($select, $bind);
$deleteIds = array();
foreach($links as $linkedProductId => $linkId) {
if (!isset($data[$linkedProductId])) {
$deleteIds[] = (int)$linkId;
}
}
if (!empty($deleteIds)) {
$adapter->delete($this->getMainTable(), array(
'link_id IN (?)' => $deleteIds,
));
}
foreach ($data as $linkedProductId => $linkInfo) {
$linkId = null;
if (isset($links[$linkedProductId])) {
$linkId = $links[$linkedProductId];
unset($links[$linkedProductId]);
} else {
/**
* The actual change in the code
*/
$storeId = (int)$this->getRequest()->getParam('store');
$bind = array(
'product_id' => $product->getId(),
'linked_product_id' => $linkedProductId,
'link_type_id' => $typeId,
'store_id' => $storeId
);
$adapter->insert($this->getMainTable(), $bind);
$linkId = $adapter->lastInsertId($this->getMainTable());
}
foreach ($attributes as $attributeInfo) {
$attributeTable = $this->getAttributeTypeTable($attributeInfo['type']);
if ($attributeTable) {
if (isset($linkInfo[$attributeInfo['code']])) {
$value = $this->_prepareAttributeValue($attributeInfo['type'],
$linkInfo[$attributeInfo['code']]);
$bind = array(
'product_link_attribute_id' => $attributeInfo['id'],
'link_id' => $linkId,
'value' => $value
);
$adapter->insertOnDuplicate($attributeTable, $bind, array('value'));
} else {
$adapter->delete($attributeTable, array(
'link_id = ?' => $linkId,
'product_link_attribute_id = ?' => $attributeInfo['id']
));
}
}
}
}
return $this;
}
}
I'll be honest, it's not to 'clean' to retrieve post data in the resource model but this way you don't need to rewrite all above classes and methods to accept an extra $storeId
variable.
Now for retrieving the right collection in the grid when editing the product. In that case you'll need to rewrite the following blocks
Mage_Adminhtml_Block_Catalog_Product_Edit_Tab_Related
Mage_Adminhtml_Block_Catalog_Product_Edit_Tab_Upsell
Mage_Adminhtml_Block_Catalog_Product_Edit_Tab_Crosssell
and change the method _prepareCollection
to add a store ID filter to the retrieved collection around line 99. Which would look something like this
$collection = Mage::getModel('catalog/product_link')->use[...]Links()
->getProductCollection()
->setProduct($this->_getProduct())
->addAttributeToSelect('*')
->addAttributeToFilter('store_id', $this->getRequest()->getParam('store_id'));
DISCLAIMER: this is untested code. Which should work but might need some tweaking here and there.
As from the table structure for linked products (related, upsell & cross sell), it's clear that there is no store_id.
So first step will be adding a store_id
field to the table. Then you need to prepare the the linked product ids while displaying and saving the products as per store view.
May need lots of tweaking, just a rough idea to get started.