thindery
thindery

Reputation: 1194

Create new links between Magento Products

Product linking in Magento has these types by default:

  1. Related
  2. Cross-Sells
  3. Up-Sells
  4. Grouped Products

I am using the default options for their default functionality. However, I need to link related products together still another way. Is that possible? Is it possible for me to create a new linking method that works much like the Related linking option works?

I am selling customizeable print products like greeting cards. I only allow the Front view of cards to be be searchable and viewable on the store, we can call it the "master product". Once a customer clicks on a card product they like, i need to then display the different product options for the inside and the back of the card. These are individual magento products in my store. Some cards have different text options, photo options, etc..

So when I add a new "master product" in the backend, I need to then "link" the products that are associated with the inside, and the products that are associated with the back to that master product.

The way the "related" products tab works is great. If I can recreate that functionality, with my own linking attribute tab on the create product page, then I should be fine.

Upvotes: 2

Views: 6480

Answers (1)

Tim Hofman
Tim Hofman

Reputation: 1068

Okay, so you have two options. The quick and not so flexible SKU pattern way of matching products, or you could add a whole new product link relation to Magento. Obviously the last option costs more time but gives you the possibility to handle the relation from the admin and determine a display order within the batch.

Option 1: Matching on SKU (easy, quick, perhaps a bit dirty)

You could write a method that, let's say, takes the first 4 characters of your current product and then uses that to match the rest of the product database.

Underneath is a mock-up version of how that code might look:

$match = substr($product->getSku(), 0, 4);
$resource = Mage::getModel('core/resource');
$read = $resource->getConnection('core_read');

$select = $read->select()
    ->from(array('e'=>$resource->getTableName('catalog/product')), 'entity_id')
    ->where("e.sku LIKE '" . $match . "%'");
$ids = $read->fetchAll($select);

This will retrieve the ID's of the matching products. Then these id's can be loaded in a product collection, like:

$_productCollection = Mage::getModel('catalog/product')->getCollection()
    ->addAttributeToSelect('<your_needed_product_attributes>')
    ->addAttributeToFilter('entity_id',array('in'=> <your_array_of_productids>));

 Mage::getSingleton('catalog/product_status')->addSaleableFilterToCollection($_productCollection);
 Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($_productCollection);
 $_productCollection->addStoreFilter()->load();

Now you only have to loop through the collection and build your HTML.

Option 2: Adding a new product relation

Magento uses the table catalog_product_link to store all types of product relations. In catalog_product_link_type you add a new link type. This is needed to be able to insert new link type data in catalog_product_link. This new link type should be added via an upgrade script in your module.

From this point on we need to make adjustments and additions to the Magento admin. Since this is a place for answers and not full blown module solutions I'm going to only mark the places which need our attention to add the new link type. Though, with the following information I don't think you will have too much trouble getting your new relation up and running.

1) Adding a tab for the new type to the menu on the product edit admin page.

Like in Mage_Adminhtml_Block_Catalog_Product_Edit_Tabs we need to create a new tab for our link type.

        $this->addTab('combine', array(
            'label'     => Mage::helper('catalog')->__('Combine'),
            'url'       => $this->getUrl('*/*/combine', array('_current' => true)),
            'class'     => 'ajax',
        ));

*/*/combine will route to the combine action of the Mage_Adminhtml_Catalog_ProductController controller. You probably want to use a different route to your own module, but I will stick to this one for this example.

2) To handle the page request */*/combine/ we need make this action available in a controller. You will find your code references in Mage_Adminhtml_Catalog_ProductController for this.

/**
 * Get combine products grid and serializer block
 */
public function combineAction()
{
    $this->_initProduct();
    $this->loadLayout();
    $this->getLayout()->getBlock('catalog.product.edit.tab.combine')
        ->setProductsUpsell($this->getRequest()->getPost('products_combine', null));
    $this->renderLayout();
}

We also need a action for handling later grid actions within this tab.

/**
 * Get upsell products grid
 */
public function combineGridAction()
{
    $this->_initProduct();
    $this->loadLayout();
    $this->getLayout()->getBlock('catalog.product.edit.tab.combine')
        ->setProductsRelated($this->getRequest()->getPost('products_combine', null));
    $this->renderLayout();
}

Furthermore this controller also houses a ->_initProductSave() method in which product relations are fetched from the request and added to the 'to-be-saved' product object.

You will need to add the underneath snippet by either extending the controller or using an observer for this. Your call. A catalog_product_before_save observer is the best way to do this.

    if (isset($links['combine']) && !$product->getCombineReadonly()) {
        $product->setCombineLinkData(Mage::helper('adminhtml/js')->decodeGridSerializedInput($links['combine']));
    }

In step 5 this data will again be picked up for further saving the new relations into the earlier mentioned catalog_product_link table.

3) The layout we try to build in step 2 won't do a lot without the following XML handles.

<adminhtml_catalog_product_combine>
    <block type="core/text_list" name="root">
        <block type="<your_own_custom_block_for_product_link_type>" name="catalog.product.edit.tab.combine"/>
        <block type="adminhtml/widget_grid_serializer" name="upsell_grid_serializer">
            <reference name="upsell_grid_serializer">
                <action method="initSerializerBlock">
                    <grid_block_name>catalog.product.edit.tab.combine</grid_block_name>
                    <data_callback>getSelectedCombineProducts</data_callback>
                    <hidden_input_name>links[combine]</hidden_input_name>
                    <reload_param_name>products_combine</reload_param_name>
                </action>
                <action method="addColumnInputName">
                    <input_name>position</input_name>
                </action>
            </reference>
        </block>
    </block>
</adminhtml_catalog_product_combine>

<adminhtml_catalog_product_combinegrid>
    <block type="core/text_list" name="root">
        <block type="<your_own_custom_block_for_product_link_type>" name="catalog.product.edit.tab.combine"/>
    </block>
</adminhtml_catalog_product_combinegrid>

4) With all that in place we need to create the block for our 'combine' type.

This step is fairly easy. Take Mage_Adminhtml_Block_Catalog_Product_Edit_Tab_Upsell as a reference. We almost need an exact copy of this file. So place it in your module and rename all upsell to 'combine', or whatever name you are using for this relation. You need to place this block type at <your_own_custom_block_for_product_link_type> in step 3.

5) Extending Mage_Catalog_Model_Product_Link

Mage_Catalog_Model_Product_Link is the file that holds all data regarding relation types. For instance which types are available and some logic for saving and loading the relation types.

In your extend you need at least the following stuff:

  • A constant to define your relation type const LINK_TYPE_COMBINE = 6;
  • A useCombineLinks method like useUpsellLinks
  • A extend of the saveProductRelations method with the underneath addition. Though this is probably also doable via an proper event observer.

    $data = $product->getCombineLinkData();
    if (!is_null($data)) {
        $this->_getResource()->saveProductLinks($product, $data, self::LINK_TYPE_COMBINE);
    }
    

This snippet eventually will be triggered after a product is being saved.

6) Adding new relation logic to product model The last step to get the admin up and running is some logic that needs to be added to the product model (Mage_Catalog_Model_Product). You probably want to add these methods via an extend from your module on this model.

You will need to add 4 methods, you can take the following as a reference, copy and rename them to your relation name.

  • getUpSellProducts
  • getUpSellProductIds
  • getUpSellProductCollection
  • getUpSellLinkCollection

When you followed the above steps you should have a working admin for adding and handling this new product relation. Only thing left is having a block to use in the 'frontend' layout of your page. Again a good reference for this is Mage_Catalog_Block_Product_List_Upsell.

I hope this gives you some good information to start with. Your choice which way to go. As said the second option is more work but gives your more flexibility through the admin and is a more robust solution.

Upvotes: 12

Related Questions