2

I was just recently given the task to create a simple Product configurator for our Magento Template in 5 days, where you can choose some attributes and it calculates the price for you, fades over a new image and changes the add to cart button to the new product.

Before this I had no experience in PHP or JQuery and just some basics in Magento (never done a custom module before.) My only programming Background is OOP Games in Action Script 3.

So far my code works somehow. I have some variables that I can change on click of some radio buttons and the variables are updated via the ajax method. As URL my block index method is called which does just load and render my layout. After I add the returned HTML (which is my whole block) to the parent of my outermost div in my block. It works but I can't seem to find a way to animate the changes and it seems kind of slow if the ajax rebuilds the whole block each time the user just changes one option.

Is there a more elegant way to just reload the changed parts, smoothly animate the changes and make my block remember the inputs that were made?

Here are the source files for download: http://www.roflxd.de/doorconfig.zip

If you need to see the site itself, please message me :)

Thanks in advance!

My Block phtml:

    <?php 

                $type = 'Simple';
                $color = 'FFFFFF';
                $size = '2500x1800';

                if (isset($_POST['color'])) {
                    $color = "#" . $_POST['color'];
                }

                if (isset($_POST['type'])) {
                    $type = $_POST['type'];
                }

                if (isset($_POST['size'])) {
                    $size = $_POST['size'];
                }

                $currentStoreUrl = Mage::getBaseUrl();

                $currentProduct = $this->getProduct($type,$color,$size);

                $currentId = $currentProduct->getId();

                $currentUrl = $currentProduct->getProductUrl();         

                $currentPrice = $this->getPrice($currentId);

                $currentImgUrl = $this->getDoorBaseImgUrl($type, $size);

?>

<div id="door_wrapper" class="">
    <div id="door_left_wrapper" class="mj-grid48">

        <form id="testform">

            <div id="door_colors">

                <label id="FFFFFF">White<input type="radio" name="toggle" value="FFFFFF"></label>
                <label id="000000">Black<input type="radio" name="toggle" value="000000"></label>
                <label id="736D6C">Grey<input type="radio" name="toggle" value="736D6C"></label>

            </div>

            <div id="door_model" >

                <?php print_r($_POST); ?>

                <?php echo $type;?>
                <?php echo $color;?>
                <?php echo $size;?>

                <br>

                <?php echo $currentImgUrl;?>
            </div>

            <div id="door_size">

                <select name="doorsizes">
                </select>

            </div>

            <?php if ($currentProduct->isSaleable()): ?>
                <button type="button">
                    <a href="<?php echo $currentStoreUrl . "checkout/cart/add?product=" . $currentId . "&qty=1";?>">
                        Test
                    </a>
                </button>
            <?php else: ?>
                <button disabled>Out of Stock</button>
            <?php endif;?>

        </form>

    </div>

    <div id="door_right_wrapper" class="mj-grid48">

        <div id="door_img">
            <img src="<?php echo $currentImgUrl;?>">
        </div>

        <div id="result"></div>
    </div>

</div>

<script type="text/javascript">

    var $col = "000000";
    var $type = "Advanced";
    var $size = "3050x2150";

    function ajaxUpdate()
    {         
        $j.ajax({
            url: "/doorconfig/ajax/index",
            type: "POST",
            data: {color : $col, type : $type, size : $size },
            context: $j('#door_wrapper').parent(),
            success: function(data) 
                     {
                        $j(this).html(data).$(this).fadeIn(slow);
                     }
        });
    };

    $j(document).ready(function() 
                       {    
                          $j("input[name=toggle]:radio").change(function ()
                                                                {
                                                                    ajaxUpdate();
                                                                })
                       });   

</script>

My Block php:

    <?php
class Geeklab_DoorConfig_Block_Doorconfig extends Mage_Core_Block_Template
{

    public function getProduct($type,$color,$size)
    {

        //Get Product Collection
        $collection = Mage::getModel('catalog/product')->getCollection();

        //Select needed Attributes
        $collection->addAttributeToSelect('doorconfig_enable'); 
        $collection->addAttributeToSelect('doorconfig_color');
        $collection->addAttributeToSelect('doorconfig_size');
        $collection->addAttributeToSelect('doorconfig_type');

        //Filter for Selected Product
        $collection->addFieldToFilter('doorconfig_enable',
                array(
                        'eq' => Mage::getResourceModel('catalog/product')
                                    ->getAttribute('doorconfig_enable')
                                    ->getSource()
                                    ->getOptionId('Yes')
                     )
        );

        $collection->addFieldToFilter('doorconfig_color',
                array(
                        'eq' => Mage::getResourceModel('catalog/product')
                                    ->getAttribute('doorconfig_color')
                                    ->getSource()
                                    ->getOptionId($color)
                     )
        );

        $collection->addFieldToFilter('doorconfig_size',
                array(
                        'eq' => Mage::getResourceModel('catalog/product')
                                    ->getAttribute('doorconfig_size')
                                    ->getSource()
                                    ->getOptionId($size)
                     )
        );

        $collection->addFieldToFilter('doorconfig_type',
                array(
                        'eq' => Mage::getResourceModel('catalog/product')
                                    ->getAttribute('doorconfig_type')
                                    ->getSource()
                                    ->getOptionId($type)
                     )
        );

        $product = $collection->getFirstItem();

        return $product;
    }

    public function getPrice($id)
    {
        $product = Mage::getModel('catalog/product')->load($id);
        $_taxHelper  = new Mage_Tax_Helper_Data;
        $finalprice = $_taxHelper->getPrice($product, $product->getFinalPrice(), true);
        $finalprice .= $this->getCurrency();
        return $finalprice;
    }

    public function getCurrency()
    {
        return Mage::app()->getLocale()->currency(Mage::app()->getStore()->getCurrentCurrencyCode())->getSymbol();
    }

    public function getDoorImageDir()
    {
        return Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA) . 'wysiwyg/geeklab/doorconfig/';
    }

    public function getDoorBaseImgUrl($type, $size)
    {
        return $this->getDoorImageDir() . strtolower($size) . '_' . str_replace("\040", "\137", strtolower($type)) . '.png';
    }

    public function getDoorColorImgUrl($color, $size)
    {
        return $this->getDoorImageDir() . strtolower($size) . '_' . strtolower($color) . '.png';
    }
}

?>

And my AjaxController.php

    <?php

class Geeklab_DoorConfig_AjaxController extends Mage_Core_Controller_Front_Action
{
   public function indexAction ()
   {
        $this->loadLayout();
        $this->renderLayout();
   }

}
?>
ericstumper
  • 81
  • 1
  • 5
  • 11
  • I am figuring out the theory piece by piece. The current behaviour makes perfect sense, as I call my controller which renders the layout on any ajax event,the html is reloaded each time. So I need something like a second controller action or model which then answers my ajax calls and gives me the new product values. After the DOM is changed via jQuery and continues to listen for changes. The phtml would just get the initial data from Magento and initialize the select and color fields for the configurator . But still the question: what do I need, model, new controller action or new controller? – ericstumper Jun 05 '13 at 02:23
  • Is it good practice to collect the data in another controller action? Do I have access to the functions from my Block.php there? So many questions, please give me a hint to solve this in a correct way :) – ericstumper Jun 05 '13 at 02:23
  • I have had to do the exact sort of thing in Mage. A team member wrote the module and I hooked up the ajax. I used jQuery to post to an action method he wrote in the controller. [Alan Storm's](http://alanstorm.com/category/magento) site is great place to pick up some good info on writing modules. Hope this helps point you in the right direction. – Greg Suskind Jun 05 '13 at 05:56
  • So you also realized it with 2 action methods, one that build the layout and one that sends back the new product data on ajax requests? Which means I should be able to use my block methods there ^^ Did you use JSON as return format or something else? I have read a lot of Alan Storms articles already, made it a lot easier to get the basics :) – ericstumper Jun 05 '13 at 09:28
  • Reading this [QUESTION](http://stackoverflow.com/questions/4006183/magento-passing-data-between-a-controller-and-a-block) I think it is possible to do the variable settings in my new controller action, but then reading Alan Storms answer, this doesn't seem to be good practice. So I get the idea that I need a second controller Action that then instantiates a model for the data operation. Didn't find any guides on that, that didn't use a resource model for database access :/ I just want simple data to be read from Magento and sent back as JSON to my block. – ericstumper Jun 05 '13 at 11:38

1 Answers1

5

So I came up with a solution that works just awesome. I added another controller action and a model to do the Magento Interactions during my ajax calls. So let me show you how it's done, I hope somebody can profit from this sooner or later :)

My new Action:

public function updateAction ()
   {

      //Instantiate Product Model
      $productModel = Mage::getModel('doorconfig/product');

      //Get Updated Values from the Model
      $currentProduct = $productModel->getProduct($_POST);
      $currentProductId = $currentProduct->getId();
      $currentProductUrl = $currentProduct->getProductUrl();
      $currentPrice = $productModel->getPrice($currentProductId);
      $currentType = $this->getRequest()->getPost('doorconfig_type');
      $currentSize = $this->getRequest()->getPost('doorconfig_size');
      $currentProductBaseImgUrl = $productModel->getDoorBaseImgUrl($currentType,$currentSize);

      //Populate Resultarray
      $result = array("currentProductId"=>$currentProductId,"currentPrice"=>$currentPrice,"currentProductUrl"=>$currentProductUrl,"currentProductBaseImgUrl"=>$currentProductBaseImgUrl);

      //Encode Result in JSON
      $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));

      return $result;
   }

My model just got most of the business logic from my block, so nothing special to point out about that.

And finally the updated Ajax section which now triggers my new controller action, receives the result as a JSON encode and changes the values in the DOM:

<script type="text/javascript">

   var $price = "";
   var $baseImgUrl = "";
   var $productUrl = "";
   var $productId = "";
   var $f = $j("#attributeform");
    var $formData;
    var $currentStoreUrl = "<?php echo $currentStoreUrl ?>";

   function ajaxUpdate()
   {

      $j.ajax({
         url: "/doorconfig/index/update",
         type: "POST",
         data: $formData,
         dataType: "json",
         success: function(data) 
                {
                  $productId = data.currentProductId;
                  $price = data.currentPrice;
                  $baseImgUrl = data.currentProductBaseImgUrl;
                  $productUrl = data.currentProductUrl;
                     $j("#result").text($price);

                     $j("#addtocart").attr('href',  $currentStoreUrl + "checkout/cart/add?product=" + $productId + "&qty=1");
                     $j("#productimg").attr('src', $baseImgUrl);

                     console.log(data);

                   },
            error: function(error)
                  {
                     console.log("Error:");
                     console.log(error);
                     alert("ERROR");
                  }
        });
    };

   $j(document).ready(function() 
   {

      $j("#result").text('<?php echo $defaultProductPrice; ?>');
      $j("#addtocart").attr('href', '<?php  echo $currentStoreUrl . "checkout/cart/add?product=" . $defaultProductId . "&qty=1" ?>');
      $j("#moreinfo").attr('href', '<?php echo $defaultProductUrl; ?>');
      $j("#productimg").attr('src', '<?php echo $defaultProductImgUrl; ?>');
      $j("#attributeform")[0].reset();

      $j("form[name=attributeform]").change(function () 
      {

         $formData = $f.serialize();
         ajaxUpdate();
      })
   });   

</script>

If you need any further explanation or wanna improve something please comment :)

ericstumper
  • 81
  • 1
  • 5
  • 11
  • could you show me what your xml files look like for this. Thanks :) –  Aug 29 '13 at 09:33