The Magento buyRequest Object – A Reference

Whoever looked at the Magento source code or database in the context of orders, probably encountered the parameter $buyRequest in many methods or the option info_buyRequest in quote items and order items. Its purpose is not immediately obvious since it contains seemingly redundant information. Also it does not have its own model class, it’s just a Varien_Object and a serialized array when stored to the database.

Example:

mysql> select code,value from sales_flat_quote_item_option where option_id=2359;
+-----------------+------------------------------------------------------------------------------------------------------------+
| code            | value                                                                                                      |
+-----------------+------------------------------------------------------------------------------------------------------------+
| info_buyRequest | a:4:{s:4:"uenc";s:140:"[...],,";s:7:"product";s:4:"5000";s:15:"related_product";s:0:"";s:3:"qty";s:1:"1";} |
+-----------------+------------------------------------------------------------------------------------------------------------+

In short: The $buyRequest object represents the customer request coming from a click on “Add to cart”, all related data can be derived from this object. It’s basically a generator for quote items. The minimal necessary data is thus the product id (product) and quantity (qty).

Where is the $buyRequest object needed?

For example it is created when you add a product to the wishlist, so that it can easily be transfered to the cart with the same configuration. When reconfiguring a product, i.e. editing it from the cart, the buyRequest is passed to the product view to preselect all options.

Also for recurring profiles and reordering the buyRequest objects get “reused” to generate a new order.

Use Cases

Some examples, when it is useful to deal with the buyRequest:

Continue reading “The Magento buyRequest Object – A Reference”

Magento Layout: getChildHtml() with Empty Result

The problem: Mysteriously, getChildHtml() yields an empty result in the template, although the child blocks were created.

The solution: Blocks in the Magento layout should always have a name. Without name-attribute they are shown, but child elements cannot be assigned and stay “orphaned”.

The responsible method is Mage_Core_Model_Layout::_generateBlock(). As you can see, the parent block only gets assigned if it has a name:

            $parentName = $parent->getBlockName();
            if (!empty($parentName)) {
                $parentBlock = $this->getBlock($parentName);
            }

Note that $parent is an XML node here, not a block object. So it does not help that blocs without name automatically get a name like ANONYMOUS_1. The assignment happens via  name-attribute of the node, so that it works for <reference> as well as for <block> parents.

Magento Testing: Fill Forms with one click

Who doesn’t know this situation: When testing features like the guest checkout manually, you have to fill all form fields tedously again and again. With Chrome auto complete or “test”, Ctrl + C, Ctrl + V it’s halfway fast but still a bit annoying. And what if the test data is supposed to make sense and should not be the same every time?

Inspired by this article on css-tricks.com I developed a little Magento module, that enables filling Magento forms with dummy data with one mouse click. Currently it is implemented for billing address and shipping address.

Github-Repository: SSE_FormFiller

And this is how it looks:

Screenshot: Forms in Checkout

Configuration

Of course you want to see this button only on your development system, that’s why there are two ways to hide it: Either disable the module completely per configuration or show the button only in developer mode:

SSE_FormFiller Configuration

The nice thing about the second variation is that the JavaScript still gets loaded, so that you can substitute the button with a bookmarklet. Here the snippets:

Billing address form

JavaScript:

formFiller.fill(billingForm.form)

Bookmarklet (right click, add as bookmark!)

Shipping address form

JavaScript:

formFiller.fill(shippingForm.form)

Bookmarklet (right click, add as bookmark!)

Technology

A bit of information on the implementation:

  • The dummy data comes from Faker.js.
  • The button gets added to choosen blocks with an observer for core_block_abstract_to_html_after
  • In the JavaScript the form ID determines which fields should be filled.

The module is designed to be extended for other forms as well. Suggestions and of course pull requests on Github are most welcome! Continue reading “Magento Testing: Fill Forms with one click”

Form Problems after Magento Update to CE 1.8.1 or EE 1.13.1 (Login, Cart)

If the login forms in the frontend do not work anymore after an update to Magento CE 1.8.1 or EE 1.13.1, the used theme has to be updated. These forms now need a form key, if it is missing, validation in the controller fails. The same applies for the “Update Cart” button in cart.phtml. How to fix it, see this link:


http://blueclawecommerce.co.uk/blog/fix-customer-cannot-login-to-magento-1-8-1/

Of course it would be optimal, if the them overrides as little core templates as possible and does its changes with layout updates in [theme]/local.xml and CSS wherever possible.

4 Tools To Make Magento Development Easier

I compiled a list of the most important Magento specific development tools in my toolbox. Additions are most welcome!

Greetings to the Magento Stammtisch Aachen guys, this is for you 😉

Magneto (sic!) Debug

http://www.magentocommerce.com/magento-connect/magneto-debug-8676.html

A free extension that integrates a developer toolbar in the Magento Layout where you find various information about the current page:

  • Request route
  • Loaded modules
  • Configuration summary
  • SQL profiler
  • Used layout handles
  • Rendered blocks/templates
  • Instantiated block classes (rendered or not)
  • One-click cache clearing

CommerceBug

http://store.pulsestorm.net/products/commerce-bug

Another integrated developer toolbar for $49.95 that offers mostly the same features but with two important additions:

  • Complete merged layout XML with all handles
  • Ability to log the information for all requests

EcomDev PHPUnit

http://www.ecomdev.org/shop/code-testing/php-unit-test-suite.html

PHPUnit extension for unit tests and integration tests. Includes database fixtures, configuration tests and more. See PDF documentation for details.

MageTool

https://github.com/alistairstead/MageTool

Command line tool for shop management and extension development. Some features:

  • Automatically create extensions and class files
  • Manage configuration
  • Manage cache, compiler and indexer

Print All SQL Queries in Magento

Activate the Zend SQL Profiler with the following node in your local.xml:

    <resources>
     <default_setup>
      <connection>
       <profiler>1</profiler>

Then you can access the profiler somewhere in your code and retrieve a lot of informations about all executed queries:

$profiler = Mage::getSingleton('core/resource')
    ->getConnection('core_write')->getProfiler();

To simply output all queries:

print_r($profiler->getQueryProfiles());

Question and answer on StackOverflow

Magento: Rewrite Shipping Method

There is a way to rewrite carrier classes of shipping methods but it is not obvious and required me to browse the shipping module source:

If you look at Mage_Shipping_Model_Config, you will discover that the code used as parameter for Mage::getModel() is taken from the store configuration. This code is NOT the standard code like 'shipping/carrier_tablerate', so it does not help overriding as usual.

Now you have to find out first what this code is. For example I wanted to override the matrixrate carrier, so I tested it like that:

$carrierConfig = Mage::getStoreConfig('carriers/matrixrate')
var_dump($carrierConfig['model']);

Yes, you can put this code anywhere on the page temporary but it is useful to have a separate file for such things that can be run from the command line (starting with Mage::app() to initialize Magento)

In my case the code was matrixrate_shipping/carrier_matrixrate so I had to change my config.xml like that:

<global>
    <models>
        <matrixrate_shipping>
            <rewrite>
                <carrier_matrixrate>my_class_name</carrier_matrixrate>
            </rewrite>
        </matrixrate_shipping>
    </models>

instead of

<global>
    <models>
        <matrixrate>
            <rewrite>
                <carrier_matrixrate>my_class_name</carrier_matrixrate>
            </rewrite>
        </matrixrate>
    </models>

Question and answer on StackOverflow

Magento: Make New Customer Attributes Visible in Backend Forms

If you add a new attribute to the customer entity in a a setup script, using addAttribute(), gets a surprise starting from Magento 1.5. The setup runs and database entries in customer_eav_attribute are created correctly but in the backend there is no new form element.

Which works well with other entities like categories and until Magento 1.4 also with customers, needs an additional setup step now, to add the attribute explicitly to the admin form (and/or other forms, like the registration form).

There is a new table for it, customer_form_attribute, which defines for each attribute, in which forms it can be used:

mysql> select * from customer_form_attribute;
+----------------------------+--------------+
| form_code                  | attribute_id |
+----------------------------+--------------+
| adminhtml_customer         |            1 |
| adminhtml_customer         |            3 |
| adminhtml_customer         |            4 |
| checkout_register          |            4 |
| customer_account_create    |            4 |
| customer_account_edit      |            4 |
| adminhtml_customer         |            5 |
| checkout_register          |            5 |
| customer_account_create    |            5 |
| customer_account_edit      |            5 |
| adminhtml_customer         |            6 |
| checkout_register          |            6 |
| customer_account_create    |            6 |
| customer_account_edit      |            6 |
| adminhtml_customer         |            7 |
| checkout_register          |            7 |
| customer_account_create    |            7 |
| customer_account_edit      |            7 |
[...]
+----------------------------+--------------+
88 rows in set (0.00 sec)

So much to for a better understanding, of course you don’t need to (and should not!) modify the database manually, it is sufficient to add the following lines after the addAttribute() call in your setup script:

    Mage::getSingleton('eav/config')
        ->getAttribute('customer', $attributeCode)
        ->setData('used_in_forms', array('adminhtml_customer'))
        ->save();

with $attributeCode being the unique code of the new attribute and 'adminhtml_customer' the code for the customer management form in the backend. To keep setup scripts more clear if you add multiple attributes, it helps to extend the entity setup class, like this:

class MyNamespace_ExtendableCustomer_Model_Entity_Setup
    extends Mage_Customer_Model_Entity_Setup
{
    public function addAttributeToForm($attributeCode, $formCode)
    {
    	Mage::getSingleton('eav/config')
            ->getAttribute('customer', $attributeCode)
            ->setData('used_in_forms', (array) $formCode)
            ->save();
    }
}

Now when you use this class for your setup scripts that add customer attributes, it’s as simple as:

$this->addAttribute('customer', 'new_attribute', array( /* ... */);
$this->addAttributeToForm('new_attribute', 'adminhtml_customer');

Of course you can add the attribute to other forms in the same way, for the codes see the list above.

Thank you to the Magento forums for the crucial hint.

Update

Pay attention, on save the field eav_entity_attribute.sort_order gets set, regardless being specified before!

See my comment on StackOverflow.

Furthermore sort_order only applies if user_defined => 0 was specified in the attribute setup because Magento sorts by user_defined first and then by sort_order.