Solving Magento

Solutions for Magento E-Commerce Platform

by Oleg Ishenko

Creating a Custom Product Type in Magento

The standard product types in Magento cover a wide range of possible merchandise: physical and virtual, configurable and downloadable, grouped and bundled. Yet, the e-commerce world is so diverse that often these types are not enough. There is always a chance that your product has some unique features that standard types do not cover. Fortunately, you can always extend an existing type, and I’ve shown you in my previous tutorials how to do that. Although extensions allow a high level of customization, sometimes business rules require a totally new product type. In this tutorial I am going to present such a case: a product type that requires a distinct functionality not available in core Magento. This may sound grand, but, in fact, this functionality is quite simple – it is an affiliate product.

Affiliate products are quite common. The idea behind them is that your shop is affiliated with some other retailer that allows you to display its products in your catalog. These affiliate products are not sold in your shop, they can’t be added to cart. Instead, customers are redirected to the partner shop where the actual transaction happens. The redirect link allows the partner shop to identify you as a referrer which entitles you to a commission from each sale. There are hundreds of thousands (or more like millions) websites that work like that: from humble blogs with a handful of Amazon links to e-commerce giants that “outsource” parts of their catalog to partners.

So, what are the requirements to the new product type?

  • affiliate products must not be able to be added to cart – I do not sell them and can’t handle such orders.
  • affiliate products must have an attribute in which I can store the affiliate link;
  • there must be a way, if necessary, to authenticate customers before redirecting them (i.e. people must register as customers to access affiliate products);
  • there must be a custom UI for affiliate products that replaces the standard add-to-cart form with a button that sends customers to my partners;

With this plan in mind, I create a new extension that will contain the affiliate product type functionality. As always, I use Solvingmagento as package. The extension name is AffiliateProduct:

<?xml version="1.0"?>
<config>
    <modules>
        <Solvingmagento_AffiliateProduct>
            <active>true</active>
            <codePool>local</codePool>
        </Solvingmagento_AffiliateProduct>
    </modules>
</config>

Listing 1. Module registration file, /app/etc/modules/Solvingmagento_AffiliateProduct.xml

Next, I set up a directory structure. As you can see, I will need Blocks, Helpers, Models, controllers, and installation scripts:

app/
    code/
        local/
            Solvingmagento/
                AffiliateProduct/
                    Block/
                    controllers/
                    etc/
                    Model/
                    Helper/
                    sql/
Figure 1. Solvingmagento_AffiliateProduct directory structure.

Declaring the Affiliate product type

Every product type must be declared in Magento configuration. I put this declaration into the module’s config.xml file, and at this stage its contents look like this:

<config>
    <modules>
        <Solvingmagento_AffiliateProduct>
            <version>0.1.0</version>
        </Solvingmagento_AffiliateProduct>
    </modules>
    <global>
        <catalog>
            <product>
                <type>
                    <affiliate translate="label" module="solvingmagento_affiliateproduct">
                        <label>Affiliate Product</label>
                        <model>solvingmagento_affiliateproduct/product_type</model>
                        <is_qty>0</is_qty>
                        <composite>0</composite>
                        <can_use_qty_decimals>0</can_use_qty_decimals>
                    </affiliate>
                </type>
            </product>
        </catalog>
    </global>
</config>

Listing 2. Declaring a new product type, etc/config.xml

Product type declaration nodes are nested within the global/catalog/product/type node. My type’s name is affiliate and it will be presented by label Affiliate Product. The product type model class is referred to by path solvingmagento_affiliateproduct/product_type (I will have to configure a models node to make it work). The new type is not composite and can’t use quantity decimals, in fact, quantity information isn’t relevant. The translation of the label is performed by a helper solvingmagento_affiliateproduct, so my module will need a helper class, which I create at Helper/Data.php. At this moment the class has no functions because for translations to work it is enough to inherit from Mage_Core_Helper_Abstract. Later in the tutorial I will need the helper to do one more task for which I will add a function.

I also add the necessary configuration nodes for my models and blocks:

<config>
    <!-- code omitted for brevity -->
    <global>
        <!-- code omitted for brevity -->
                    <blocks>
            <solvingmagento_affiliateproduct>
                <class>Solvingmagento_AffiliateProduct_Block</class>
            </solvingmagento_affiliateproduct>
        </blocks>
        <helpers>
            <solvingmagento_affiliateproduct>
                <class>Solvingmagento_AffiliateProduct_Helper</class>
            </solvingmagento_affiliateproduct>
        </helpers>
                    <models>
            <solvingmagento_affiliateproduct>
                <class>Solvingmagento_AffiliateProduct_Model</class>
            </solvingmagento_affiliateproduct>
        </models>
    </global>
    <!-- code omitted for brevity -->
</config>

Listing 3. Models, blocks, and helper configuration, etc/config.xml

With the model configuration ready, I can now create my product type model class. Since affiliate products are virtual in nature, it makes sense to extend Magento’s Virtual product class. The only significant customization I need is a protected function _prepareProduct. As you might have read from my post Product Type Logic Implementation in Magento, this function is called whenever a product is added to a shopping cart or to a wish list. There are two processing modes: strict for shopping cart and lite for wish list. I want customers to be able to add affiliate products to their wish lists but not to cart. To do that, I return an error message in the strict mode, but allow the system to process a wish list addition request when in the “lite” mode.

class Solvingmagento_AffiliateProduct_Model_Product_Type
        extends Mage_Catalog_Model_Product_Type_Virtual
{
    const TYPE_AFFILIATE          = 'affiliate';
    const XML_PATH_AUTHENTICATION = 'catalog/affiliate/authentication';

    protected function _prepareProduct(Varien_Object $buyRequest, $product, $processMode)
    {
        if ($this->_isStrictProcessMode($processMode)) {
            return Mage::helper('solvingmagento_affiliateproduct')->__(
                'Affiliate product %s cannot be added to cart. ' .
                ' On the product detail page click the "Go to parent site"'.
                ' button to access the product.',
                $product->getName()
            );
        }
        return parent::_prepareProduct($buyRequest, $product, $processMode);
    }
}

Listing 4. Affiliate product type model, Model/Product/Type.php

Adding a new attribute to affiliate products

Affiliate products need a new attribute for their partner links. To create this attribute I use an installation script sql/solvingmagento_affiliateproduct_setup/install-0.1.0.php. Adding a product attribute to the EAV system requires a use of a special setup class: Mage_Catalog_Model_Resource_Setup. The necessary entries in the config.xml are these:

<config>
    <!-- code omitted for brevity -->
        <global>
        <!-- code omitted for brevity -->
                <resources>
            <solvingmagento_affiliateproduct_setup>
                <setup>
                    <module>Solvingmagento_AffiliateProduct</module>
                    <class>Mage_Catalog_Model_Resource_Setup</class>
                </setup>
            </solvingmagento_affiliateproduct_setup>
        </resources>
        <!-- code omitted for brevity -->
    </global>
    <!-- code omitted for brevity -->
</config>

Listing 5. Configuring a setup resource for the extension, etc/config.xml

I add the following code to the installation file:

/** @var $installer Mage_Catalog_Model_Resource_Setup */
$installer = $this;
$installer->startSetup();

$installer->addAttribute(
    Mage_Catalog_Model_Product::ENTITY,
    'affiliate_link',
    array(
        'type'                    => 'text',
        'backend'                 => '',
        'frontend'                => '',
        'label'                   => 'Affiliate Link',
        'input'                   => 'text',
        'class'                   => '',
        'source'                  => '',
        'global'                  => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE,
        'visible'                 => true,
        'required'                => true,
        'user_defined'            => false,
        'default'                 => '',
        'searchable'              => false,
        'filterable'              => false,
        'comparable'              => false,
        'visible_on_front'        => false,
        'unique'                  => false,
        'apply_to'                => 'affiliate',
        'is_configurable'         => false,
        'used_in_product_listing' => false
    )
);

Listing 6. Creating a new EAV attribute, sql/solvingmagento_affiliateproduct_setup/install-0.1.0.php

The $installer variable is an instance of the setup class, whose function addAttribute I call to create a text type attribute affiliate_link. This attribute doesn’t need custom backend and frontend models, its scope is store view, it applies to affiliate products only and is required for them. If you are interested in the meaning of the attribute’s other properties – check out my description of Magento’s EAV system.

I want this attribute to appear in its own tab in the back-end product edit form. This means, I need to add it to an attribute group which I call “Affiliate Information”. I also need to add this attribute to an attribute set. I don’t want to complicate things too much by creating a special attribute set for affiliate products, so I use the default attribute set. For all this, I extend the installation script with these lines:

$attributeId = $installer->getAttributeId(
    'catalog_product',
    'affiliate_link'
);

    $defaultSetId = $installer->getAttributeSetId('catalog_product', 'default');

$installer->addAttributeGroup(
    'catalog_product',
    $defaultSetId,
    'Affiliate Information'
);

//find out the id of the new group
$groupId = $installer->getAttributeGroup(
    'catalog_product',
    $defaultSetId,
    'Affiliate Information',
    'attribute_group_id'
);

//assign the attribute to the group and set
if ($attributeId > 0) {
    $installer->addAttributeToSet(
        'catalog_product',
        $defaultSetId,
        $groupId,
        $attributeId
    );
}

$installer->endSetup();

Listing 7. Adding attribute affiliate_link to the default attribute set and to an attribute group, sql/solvingmagento_affiliateproduct_setup/install-0.1.0.php

In this code I get the ID of my new attribute affiliate_link and the ID of the default attribute set. I use the set ID to add a new attribute group Affiliate Information to the default set. At the end, I add the attribute to both the attribute set and attribute group.

Now I can run Magento to install the attributes and try to create an Affiliate product in the back-end. I log into the admin area, go to the catalog management and click the “Add Product” button. The “Product Type” drop-down already lists my new product type, so I choose “Affiliate Product”, leave the “Default” attribute set selected and click “Continue”. The product edit form contains a new tab – “Affiliate information” – and there is my attribute input field. Great! But as I fill out other attribute values I open tab “Price” which is suddenly empty:

no_price_attributes

Figure 2. No price attributes.

The reason why there are no price attributes listed is that they don’t apply to the new product type yet. But Affiliate products need prices, so I add an upgrade file to my setup directory: sql/solvingmagento_affiliateproduct_setup/upgrade-0.1.0-0.1.1.php. This script will run after I set a new, higher version 0.1.1 in my module’s configuration. The code I need to execute is this:

$installer = $this;

$installer->startSetup();

$attributes = array(
    'price',
    'special_price',
    'special_from_date',
    'special_to_date',
    'minimal_price',
    'tax_class_id'
);

foreach ($attributes as $attributeCode) {
    $applyTo = explode(
        ',',
        $installer->getAttribute(
            Mage_Catalog_Model_Product::ENTITY,
            $attributeCode,
            'apply_to'
        )
    );

    if (!in_array('affiliate', $applyTo)) {
        $applyTo[] = 'affiliate';
        $installer->updateAttribute(
            Mage_Catalog_Model_Product::ENTITY,
            $attributeCode,
            'apply_to',
            join(',', $applyTo)
        );
    }
}

$installer->endSetup();

Listing 8. Applying price attributes to the Affiliate product type, sql/solvingmagento_affiliateproduct_setup/upgrade-0.1.0-0.1.1.php

In this upgrade I list price attributes in an array and iterate through it. For each of the attributes I get product types it is already applied to and extend this list with affiliate product type. Next time I run Magento the “Price” tab looks different:

with_price_attributes

Figure 3. Price attributes added.

I go ahead and create an affiliate product: I add a name, an SKU, a price, and descriptions. I set a tax class, configure visibility, and assign the product to a website and a category. I set “Manage Stock” to No because affiliate products don’t need it. Finally, I add an affiliate link (any link for test purposes) and save the product.

Redirects and customer authentication

I want to be able to tell the system whether to ask customers to log in before sending them to partner URLs or not. This authentication requirement will be controlled from the shop’s configuration. To add a system configuration entry I create a system.xml file in my etc/ folder and fill it with this code:

<config>
    <sections>
        <catalog>
            <groups>
                <affiliate translate="label">
                    <label>Affiliate Product Options</label>
                    <frontend_type>text</frontend_type>
                    <sort_order>800</sort_order>
                    <show_in_default>1</show_in_default>
                    <show_in_website>1</show_in_website>
                    <show_in_store>1</show_in_store>
                    <fields>
                        <authentication translate="label">
                            <label>Authentication required</label>
                            <frontend_type>select</frontend_type>
                            <source_model>adminhtml/system_config_source_yesno</source_model>
                            <sort_order>300</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>0</show_in_store>
                            <comment>Customer must log in to access partner links</comment>
                        </authentication>
                    </fields>
                </affiliate>
            </groups>
        </catalog>
    </sections>
</config>

Listing 9. Creating a configuration option, etc/system.xml

This XML adds a new group Affiliate Product Options to the Catalog configuration section. This group contains a single field – Authentication required, which is a drop-down with two values: yes and no:

authentication_configuration

Figure 4. Authentication configuration.

I want this option to be set to yes by default. For this I have to add a default node to my config.xml file:

<config>
    <!-- code omitted for brevity -->
    <default>
        <catalog>
            <affiliate>
                <authentication>1</authentication>
            </affiliate>
        </catalog>
    </default>
</config>

Listing 10. Setting a default value for the catalog/affiliate/authentication option, etc/config.xml

Customer authentication and redirecting to partner URLs is done by a controller. To pass redirect requests to the controller the system needs a router which I set up in the configuration file:

<config>
    <!-- code omitted for brevity -->
    <frontend>
        <routers>
            <affiliate>
                <use>standard</use>
                <args>
                    <module>Solvingmagento_AffiliateProduct</module>
                    <frontName>affiliate</frontName>
                </args>
            </affiliate>
        </routers>
        <secure_url>
            <affiliate_redirect>/affiliate/redirect/</affiliate_redirect>
        </secure_url>
    </frontend>
    <!-- code omitted for brevity -->
</config>

Listing 11. Router configuration, etc/config.xml

I use the standard (not admin or CMS) router and instruct the system to look for the controller in my module Solvingmagento_AffiliateProduct. The front name affiliate will be used in redirect URLs that will look like this: http://www.myshop.dev/affiliate/redirect/product/id/100. The redirect will be translated into RedirectController, product into productAction, and id will pass the affiliate product ID value to the controller action. I also want redirect URLs to be SSL secured – after all, they represent a customer transaction.

The Solvingmagento_AffiliateProduct_RedirectController controller extends the Mage_Core_Controller_Front_Action class and contains four methods. The main method, redirectAction is called when a redirect request is dispatched. But before that, the system triggers the preDispatch() function, which I am going to use to authenticate customers. If authentication is required and a customer is not logged in, I will redirect him to the registration form. If some other error happens I will try to send the customer back to the page he came from. For this I will use a protected method goBack() that tries to determine the referrer URL. The last method, a protected _construct function is used to initialise a helper instance that I need to translate error messages.

The preDispatch method of the RedirectController looks like this:

public function preDispatch()
{
    parent::preDispatch();

    if (!$this->getRequest()->isDispatched()) {
        return;
    }

    $authenticationRequired = (bool) Mage::getStoreConfig(
        Solvingmagento_AffiliateProduct_Model_Product_Type::XML_PATH_AUTHENTICATION
    );

    if ($authenticationRequired) {
        $customer = Mage::getSingleton('customer/session')->getCustomer();
        if ($customer && $customer->getId()) {
            $validationResult = $customer->validate();
            if ((true !== $validationResult) && is_array($validationResult)) {
                foreach ($validationResult as $error) {
                    Mage::getSingleton('core/session')->addError($error);
                }
                $this->goBack();
                $this->setFlag('', self::FLAG_NO_DISPATCH, true);

                return $this;
            }
            return $this;
        } else {
            Mage::getSingleton('customer/session')->addError(
                $this->helper->__('You must log in to access the partner product')
            );
            $this->_redirect('customer/account/login');
            $this->setFlag('', self::FLAG_NO_DISPATCH, true);
            return $this;
        }
    }
}

Listing 12. Checking authentication requirements before redirecting to a partner link, controllers/RedirectController.php

In this method I read the authentication setting from the current store’s configuration, and if it is a Yes, I check the session for a customer object. If this object is not found then the customer is not logged in and I send him to the registration form. If a customer is logged in, I validate the customer object. Customer validation is optional, but I want to make sure that I have all customer data that is required: first and last names, an email address, a password, etc. This validation is done by function Mage_Customer_Model_Customer::validate(). If something goes wrong, I call the goBack() method and set the “no-dispatch” flag to true. In case of an error, this flag will tell the system to stop further request dispatching and redirect the customer to either the registration form or to the page he came from. The redirect controller’s goBack() method helps me to find out where to send the customer back to:

protected function goBack()
{
    $returnUrl = $this->_getRefererUrl();

    if ($returnUrl) {
        $this->getResponse()->setRedirect($returnUrl);
    } else {
        $this->_redirect('checkout/cart');
    }

    return $this;
}

Listing 13. Redirecting the customer to a referring URL, controllers/RedirectController.php

The _getReferrerUrl() method is inherited from Mage_Core_Controller_Varien_Action class and extracts referrer information from the HTTP request. If I don’t get any referrer URL, I send customer to the shopping cart page.

After passing the checks in the preDispatch() method, the redirect request is processed by the productAction() method:

public function productAction()
{
    $productId  = (int) $this->getRequest()->getParam('id');

    $product = Mage::getModel('catalog/product')->load($productId);

    if (($product instanceof Mage_Catalog_Model_Product)
        && ($product->getTypeId() === Solvingmagento_AffiliateProduct_Model_Product_Type::TYPE_AFFILIATE)
    ) {
        if (!Zend_Uri::check($product->getAffiliateLink())) {
            Mage::getSingleton('core/session')->addError(
                $this->helper->__('The partner product is not accessible.')
            );

            $this->goBack();
            return;
        }

        $this->getResponse()->setRedirect($product->getAffiliateLink());
        return;

    } else {
        Mage::getSingleton('core/session')->addError(
            $this->helper->__('Affiliate product not found')
        );

        $this->goBack();
        return;
    }
}

Listing 14. Redirect controller’s productAction method, controllers/RedirectController.php

The request is supposed to contain a parameter id that is a reference to an affiliate product. If no product with this ID is found, or if a product is not of the affiliate type, I send the customer back to the page he came from where an error message is shown. The same happens if the product’s affiliate link attribute doesn’t contain a valid URL. But if everything is fine, the response is set to redirect to the target URL.

Front-end customizations

The last thing left to do is user interface. An affiliate product detail page has no add-to-cart form which is replaced by a “Go to partner site” button. This modification requires a layout update file, which I declare in the module’s configuration:

<config>
    <!-- code omitted for brevity -->
    <frontend>
        <layout>
            <updates>
                <affiliate module="Solvingmagento_AffiliateProduct">
                    <file>solvingmagento/affiliate.xml</file>
                </affiliate>
            </updates>
        </layout>
                    <!-- code omitted for brevity -->
    </frontend>
    <!-- code omitted for brevity -->
</config>

Listing 15. Declaring a layout update file, etc/config.xml

The layout update file modifies blocks under handle PRODUCT_TYPE_affiliate that is rendered in the affiliate product detail page. First, I add a type-specific block solvingmagento_affiliateproduct/catalog_product_view_type as child to the product.info.affiliate block. Second, I set a different template file to the add-to-cart block:

<layout version="0.1.0">
    <PRODUCT_TYPE_affiliate translate="label" module="solvingmagento_affiliateproduct">
        <label>Catalog Product View (Affiliate)</label>
        <reference name="product.info">
            <block type="solvingmagento_affiliateproduct/catalog_product_view_type" name="product.info.affiliate" as="product_type_data" template="solvingmagento/affiliate/product/type.phtml">
            </block>
        </reference>
        <reference name="product.info.addtocart">
            <action method="setTemplate"><template>solvingmagento/affiliate/product/view/addtocart.phtml</template></action>
        </reference>
    </PRODUCT_TYPE_affiliate>
</layout>

Listing 16. Layout updates for the product detail page, /app/design/frontend/base/default/layout/solvingmagento/affiliate.xml

I’ve created a class file for the affiliate type block at Block/Catalog/Product/Type/View.php. I don’t need any custom functionality there, it is enough to inherit from the Mage_Catalog_Block_Product_View_Type_Virtual class. The template file for this block is also nothing special – it outputs two children blocks. One child is supposed to render “extra” data for the product type, but at the moment affiliate type doesn’t have any. The second child renders product price:

<?php
$_product = $this->getProduct();
echo $this->getChildHtml('product_type_data_extra');
echo $this->getPriceHtml($_product);
?>

Listing 17. Affiliate type block template, /app/design/base/default/template/solvingmagento/affiliate/product/type.phtml

The new add-to-cart template is a bit more interesting: there is no input element for quantity, and the “onclick” event of the button opens a URL generated by a helper function getRedirectUrl():

<?php $_product = $this->getProduct(); ?>
<?php $buttonTitle = $this->__('Go to partner site'); ?>
<?php if($_product->isSaleable()): ?>
    <div class="add-to-cart">
        <button type="button" class="button btn-cart" onclick="javascript:window.location.href='<?php
            echo Mage::helper('solvingmagento_affiliateproduct')->getRedirectUrl($_product);?>'; return false;">
            <span><span><?php echo $buttonTitle ?></span></span>
        </button>
        <?php echo $this->getChildHtml('', true, true) ?>
    </div>
<?php endif; ?>

Listing 18. Affiliate product’s version of the “add-to-cart” form, /app/design/base/default/template/solvingmagento/affiliate/product/view/addtocart.phtml

So far the module’s helper class has been used for translations only and had no functions of its own. Now it gets a new task: rendering redirect URLs. These URLs do not show actual partner links to customers, instead, they point to the module’s redirect controller that, if necessary, will authenticate customers before sending them to target websites.

public function getRedirectUrl(Mage_Catalog_Model_Product $product)
{
    return Mage::getUrl('affiliate/redirect/product', array('id' => $product->getId()));
}

Listing 19. Helper function to generate redirect URLs, /Helper/Data.php

If I open the detail page of my sample affiliate product now, it will look like this:

affiliate_product_page

Figure 4. Affiliate product in the front-end.

I am not logged in and clicking the “Go to partner site” button will send me to the log-in page where an error message “You must log in to access the partner product” will be shown. After I log in and click the button again, I am successfully redirected to the URL I set up for the product earlier.

But I am not done yet. The add-to-cart form is still present in the category view, in the wish list, and in the product comparison pop-up. To replace it with redirect URLs I have to do some helper rewrites. The Mage_Checkout_Helper_Cart::getAddUrl($product, $additional = array()) method is responsible for generating add-to-cart URLs for list views (above all, category view). I want it to produce a different kind of URLs for affiliate products. I copy and paste the getAddUrl function into my new class, Solvingmagento_AffiliateProduct_Helper_Cart, and add a condition to the beginning of the method:

class Solvingmagento_AffiliateProduct_Helper_Cart extends Mage_Checkout_Helper_Cart
{
    public function getAddUrl($product, $additional = array())
    {
        if ($product->getTypeId() === Solvingmagento_AffiliateProduct_Model_Product_Type::TYPE_AFFILIATE) {
            return Mage::helper('solvingmagento_affiliateproduct')->getRedirectUrl($product);
        }
        /**
         * code omitted for brevity
         */
    }
}

Listing 20. Extending the Mage_Checkout module’s Cart helper function to generate redirect URLs for affiliate products, /Helper/Cart.php

I must do exactly the same modification to the helper that generates add-to-cart URLs in the product comparison page:

class Solvingmagento_AffiliateProduct_Helper_Compare extends Mage_Catalog_Helper_Product_Compare
{
    public function getAddToCartUrl($product)
    {
        if ($product->getTypeId() === Solvingmagento_AffiliateProduct_Model_Product_Type::TYPE_AFFILIATE) {
            return Mage::helper('solvingmagento_affiliateproduct')->getRedirectUrl($product);
        }
        /**
         * code omitted for brevity
         */
    }
}

Listing 20. Extending the Mage_Catalog module’s Compare helper function to generate redirect URLs for affiliate products, /Helper/Compare.php

The helper rewrites are declared in the module’s configuration file:

<config>
    <!-- code omitted for brevity -->
    <global>
        <!-- code omitted for brevity -->
        <helpers>
            <!-- code omitted for brevity -->
            <checkout>
                <rewrite>
                    <cart>Solvingmagento_AffiliateProduct_Helper_Cart</cart>
                </rewrite>
            </checkout>
            <catalog>
                <rewrite>
                    <product_compare>Solvingmagento_AffiliateProduct_Helper_Compare</product_compare>
                </rewrite>
            </catalog>
        </helpers>
        <!-- code omitted for brevity -->
    </global>
</config>

Listing 21. Helper rewrites, etc/config.xml

There are two more areas where affiliate products are displayed and where the add-to-cart form must be replaced. It is the wish list sidebar and the wish list overview in the customer area. Both are taken care of with layout updates:

<layout version="0.1.0">
    <default>
        <reference name="wishlist_sidebar">
            <action method="setTemplate"><template>solvingmagento/affiliate/wishlist/sidebar.phtml</template></action>
        </reference>
    </default>
    <wishlist_index_index>
        <reference name="customer.wishlist.item.cart">
            <action method="setTemplate"><template>solvingmagento/affiliate/wishlist/item/column/cart.phtml</template></action>
        </reference>
    </wishlist_index_index>
    <!-- code omitted for brevity -->
</layout>

Listing 22. Wish list related layout updates, /app/design/frontend/base/default/layout/solvingmagento/affiliate.xml

The add-to-cart form in the respective templates is extended with a check for affiliate product type:

<?php if ($product->getTypeId() === Solvingmagento_AffiliateProduct_Model_Product_Type::TYPE_AFFILIATE) : ?>
    <a href="<?php Mage::helper('solvingmagento_affiliateproduct')->getRedirectUrl($product);?>" class="link-cart"><?php
    echo Mage::helper('solvingmagento_affiliateproduct')->__('Go to partner site') ?></a>
<?php else: ?>
    <a href="<?php echo $this->getItemAddToCartUrl($_item) ?>" class="link-cart"><?php echo $this->__('Add to Cart') ?></a>
<?php endif;?>

Listing 23. Two different checkout links: for affiliate products and for all others, /app/design/frontend/base/default/template/solvingmagento/affiliate/wishlist/sidebar.phtml

Conclusion

This tutorial is the final part of the multi-post discussion of product types in Magento. I have shown you the product type system implemented in Magento and described in detail the standard type that are available in the core application. Product types are flexible models that can be modified and extended to match your business requirements. In this last part I’ve also shown you a way to create a new product type which you can use as a guide when developing your own custom products. The extension I’ve been using in the tutorial is available for download at Github: Solvingmagento_AffiliateProduct.

Readers who read this post also read these:

  • Magento Grouped Product Type Tutorial
    This post is a follow up to the discussion on the Grouped product type in Magento. In this tutorial I will develop a simple Magento module, which will change some front-end display features of grou...
  • Magento Grouped Product Redirect Tutorial
    Recently, one of my blog readers has raised a question in the comments to my post Magento Grouped Poduct Type: Is there a way to direct a customer to the grouped product when he clicks a link to th...
  • Product Type Logic Implementation in Magento
    Depending on what merchandise your shop specializes in, you may need different product types. The basic, simple product type logic is straightforward: a product has a price, it has product informat...
  • Magento Configurable Product Type Tutorial
    This tutorial will demonstrate some functionality specific to the Configurable product type. This post complements the overview of this product type, which you can find in my previous posts: Magent...

15 thoughts on “Creating a Custom Product Type in Magento

  1. This is an excellent tutorial. I’ve been looking for this information for several months. It’s baffling that there aren’t more people out there trying to add this kind of affiliate link to their products in Magento, or even several extensions for sale. Thank you for the great tutorial and your generosity for adding the extension on Github.

  2. Hi sir,

    I have used your affiliate extention. It works well. But have in shop, other products that I sell, which don’t go to the shopping cart when the customer wants to purchase. I am selling my own products not only affiliate products. How can I customize the file extention that my products go to the shopping cart (one page checkout).

    Thanks
    Thanks

  3. Hi sir,

    many thanks for coding this extension. Can we add multiple seller for single product in affiliate section & showing prices & feature for each seller Like junglee.com doing these days.

    Your response will be highly appreciated

    Thanks

  4. Thank you very much for sharing this tutorial with the community, nicely written and easy to understand! This is a perfect reference. Keep up the awesome work!!

  5. Hi everyone!
    I don’t know anything about magento, but i have this problem I want to ask why 2 people use windows 8.1 with Firefox 32 when login magento (admin). Both open a product and go to “images” but with my computer shows image of product and not other computer (delete cache and cookie).
    Hope your answers about how to solve and how to show this error (no image) on my computer.
    Sympathic! if i posted this no true location (because i don’t look for this magento so i post here).
    Thanks!
    (I edit it again)

  6. Pingback: Magento Produkttypen (product types) - Artikelarten erklärt

  7. I would like to create a custom product type which extends Configurable Product Type. I have extended 2 classes: Mage_Catalog_Model_Product_Type_Configurable and Mage_Catalog_Model_Product_Type_Configurable_Price. What are the other files I must extends to make it work?

  8. Thanks Oleg, great extension you got there. Exactly what I was seeking. Keep on posting stuffs like that it’s really helpful.

    Using this code here you’re able to modify the buttons displaying such as in catalog grid-view or list-view:

    getTypeId() == ‘affiliate’ ): ?>

    <button type="button" class="button btn-cart-aff" onclick="javascript:window.location.href='getRedirectUrl($_product);?>’; return false;”>
    __(‘Buy Now’) ?>

Leave a Reply to Doan Tien Tai Cancel reply

Your email address will not be published. Required fields are marked *

Theme: Esquire by Matthew Buchanan.