Magento: Adding custom attributes to orders, Part II

In hindsight I probably should have tested the method I blogged about recently, just to make sure it works.

Because it doesn’t.

In hindsight I probably should have tested the method I blogged about recently, just to make sure it works.

Because it doesn’t.

Oh, the EAV attribute is created all right. But it’s useless, because as I learned since then, orders do not read from the EAV tables since Magento 1.4 (released end of 2010, maybe). They use a flat table ({sales_flat_order}) along with a bunch of other flat tables (order items, quotes, invoices…).

What to do? There are a couple of options. I could add a field to that flat table. Easy to do with an install script, something like:

$installer->run("ALTER TABLE ADD my_custom_field VARCHAR(500);");

Problem is, according to one poster on Stack Overflow, that’s probably not upgrade safe. Plan B would be to create a separate table, keyed on Order ID. Easy enough to do, and my first plan was to write the to the custom as the info is copied from the quote to the order, in Mage_Sales_Model_Convert_Quote::toOrder().

But that doesn’t look possible, because the Order ID isn’t set yet. Seems crazy, I know, but I tried to read

$order->getIncrementId() //I know that one's set
$order->getId() //Nope
$order->getOrderId() //Not that either
$order->getEntityId() //Because the order table's primary key field is entity_id

So I could use increment_id (not exactly sure what that field is good for, except for displaying to customers)… but then I decided to just key the custom table on quote_id. No need to worry about writing to the order, I just extend the order model like so:

class Npd_Myextension_Model_Sales_Order extends Mage_Sales_Model_Order
{
    public function getCustomField() {
    	$quote = Mage::getSingleton('sales/quote');
	$quote->load($this->getQuoteId());
   	return $quote->getCustomField();
    }
}

Which works, because orders always keep a reference to their source quote. Likewise, invoices keep a reference to their source order.

So there you go, and I’m making progress. Progress against a massive headwind, mind you. Again, I whine: why the hell is Magento making things so hard for developers?

PS: about the order ID, am I just kind of dim, or is it actually not set at that point? If it is, what getter function should my code be using? The fact that I’m even asking “what getter function should I be using?” shows either Magento or myself needs serious help…

Magento: Adding custom attributes to orders

I’m currently working on a little Magento side project. The learning curve is still steep, and still frustrating. A big part of it involves creating custom fields on orders.

I’m currently working on a little Magento side project. The learning curve is still steep, and still frustrating. A key part of it involves creating custom fields on orders.

Better Magento developers than me have already gone this route, so I didn’t need to reinvent the wheel. Alan Storm’s excellent post on setup resources was a huge help. I’ll just note a couple of things: first, the actual setup class name could in fact be anything, it doesn’t have to follow the Company_Module_Model_Mysql4_Setup pattern. All you need is to place it in the location in your directory that corresponds to the class name (as with all Magento classes).

Second, it bears repeating: the installer script will only run if the version stored in the core_resource table is different from the version being installed. To run the installer multiple times, simply delete the appropriate record from core_resource, then clear the cache (if it’s enabled) and refresh any page. Having only written one other extension with an installer script in my career, I’d forgotten that particular bit.

Third, and this also bears repeating: damn, but Magento is not friendly to developers. Usual Magento bitching aside (convoluted config.xml, overcomplex extension class/directory structure), why is it making developers do extra work, like writing:

$installer = $this;
$installer->startSetup();
...
$installer->endSetup();

…in every installer script? It’s just a little thing, you say? Maybe, but it’s one of many unnecessary little things, one more little annoyance for experienced developers, one more thing novice devs need to remember, making the learning curve just a little bit steeper.

So that’s general instructions for the installer. Now, how do I specifically add this custom field? I got my answer from the first post on this page. The logic goes like this:

$setup = new Mage_Eav_Model_Entity_Setup('core_setup');
$setup->addAttribute('order', 'newfield', array(
    'position'             => 1,
    'type'              => 'text',
    'label'                => 'Your New Field',
    'global'            => 1,
    'visible'           => 1,
    'required'          => 0,
    'user_defined'      => 1,
    'searchable'        => 0,
    'filterable'        => 0,
    'comparable'        => 0,
    'visible_on_front'  => 1,
    'visible_in_advanced_search' => 0,
    'unique'            => 0,
    'is_configurable'   => 0,
    'position'          => 1,
));            

Pretty self-explanatory, right? Note, though, one thing that tripped me up before I found the above link: the first argument of addAttribute() is a string, not an integer. I’d expected to have to enter the entity_type_id corresponding to orders (5, in my system), but no, it looks like it needs the entity_type_code (‘order’). Weird, and against my common sense.

So there you go, now you know how add custom fields to orders.

PS: here’s some more Magento bitching. This whole EAV scheme, what exactly is it good for? Because frankly, so far it’s given me nothing but headaches. It makes the db bloated and slow, makes looking up data extremely time-consuming, and does not make my life as a developer easier. Like, at all. Other e-commerce platforms (*coughUbercartcough*) use simpler and saner systems that are just as flexible, if not more so.

PPS: While playing around with this extension, I discovered some weird / amusing behaviour related to installing & uninstalling extensions. Since this has nothing to do with custom attributes or installer scripts per se, I’m saving it for another post.

More Magento Annoyances

This week I had to deal with yet more stupid Magento behaviour. First, I had to overload a core controller class. Naively, I tried doing it the easy way at first—ie: simply duplicating the file and file path in the /app/code/local directory and making my changes there. (Which, BTW, always struck me as a deeply stupid way to go about things, but that’s another story)—but that didn’t work.

This week I had to deal with yet more stupid Magento behaviour. First, I had to overload a core controller class. Naively, I tried doing it the easy way at first—ie: simply duplicating the file and file path in the /app/code/local directory and making my changes there. (Which, BTW, always struck me as a deeply stupid way to go about things, but that’s another story)—but that didn’t work. A further bit of research told me that hey, controllers can’t be overridden that way. What you have to do, apparently, is create your own extension, with a controller class extending the core class, and perform other configuration magic.

Read the whole tutorial in the official Wiki here. Speaking of, I’m a little appalled at how incomplete and in need of a cleanup this article is, for what you’d think is a pretty important operation. Not only that, but the editors can’t even seem to agree on the details. For example, regarding Step 2, should the rewritten URL in the config file contain a leading slash or not? For the record, I did need those slashes, but maybe that depends on the version. That, plus lines like

(comments I’m not a 100% sure about are ending with “(?)”):

and

You need to get extra precise with the rewrite regular expression cause this causes a very hard time. In this part.

make the article look really unprofessional. Guys, it’s your freaking official freaking Wiki, you need to (a) be 110% sure about what you’re putting up, and (b) write in correct English. Sheesh.

Anyway. So I followed the instructions and it worked. Yay? But wait! In a crazy twist, my override controller (ie: in the extension) was actually extending my local controller (ie: the duplicate of the core class, that I’d forgotten to delete). So… a local override could have worked after all? Or is it a bug? I have no idea. I decided to just delete the local override and call it a day.

(There was another issue that at first I blamed on a Magento default template, but on mature reflection may have been caused by our team.)

The Joy of Drupal, The Agony of Magento

I’ve been working for a small web development shop for the past few months. It’s interesting work, all open-source, all PHP-based. My first project was with Magento, and it was frakking painful.

I’ve been working for a small web development shop for the past few months. It’s interesting work, all open-source, all PHP-based. My first project was with Magento, and it was frakking painful.

I’m not just talking about the inevitable learning curve of tackling a new code base, and a (to me) whole new kind of CMS. The pain comes from an incredibly unintuitive code architecture that I keep having to wrap my brain around over and over.

From working with WordPress I was used to files for a given theme or plugin sitting together in one directory; this makes sense because they are all part of one module. Not so in Magento. Code, templates and other config elements are spread all across the system. From a design point of view I can sort of see why you’d want your business logic separate from your page templates, and both separate from your javascript, CSS, images and whatnot. You’d think it seems tidier, right? But in practice it makes extensions much harder to code than they should. To enhance or override core functionality, I have to know the exact names of these core classes and where they sit in the code directories, with no tidy hooks or API around which to build my functions.

And that’s not even counting the unholy tag soups in configuration XML files, with weird rules and redundancies that I haven’t been able to grok so far, so I just copy from existing samples and hope it eventually works. For example, according to A book to cook with Magento, config parameters defaults are set in the file config.xml, but if you want to view and modify them in the Web interface, that form is defined in another file, system.xml. My jaw literally dropped when I read up on this. How does it make any sense?

Mind-boggling or not, I’m grateful to blogs like A book to cook because there a serious dearth of online Magento documentation, official or otherwise. As often as not, googling particular issues led me to forum threads asking the same question I was researching… and nothing else, except maybe others chiming in to say they’ve got the same problem, or the same guy posting again a few months later to ask “What, nobody can help me?” Magento just doesn’t seem to have the same kind of active online community as, say, WordPress.

So what you end up with is a system that is way harder than it should be to enhance in any substantial way. Magento is open-source, sure, but if the source is a scary and confusing jungle of code, that doesn’t really help. No wonder people are willing to shell out megabucks for what you’d think are pretty minor extensions, instead of working it out themselves. Case in point:

The Backorder Button extension gives you the ability to display to your customers which of your products are currently on backorder.

If one of your products are on backorder, the Backorder Button extension will change the text’s of your “Add to Cart” buttons to “Backorder” (both in the product list, and on the product details pages), and the “Availability: In Stock” text’s (displayed on the product details pages) to “Availability: Available on Backorder.”. You can of course rewrite / customize the replacement text’s from the System Configuration.

$29 for this? Really? It took my coworker a couple of hours to figure out how to do it. But Magento’s deceptive: what looks simple and obvious isn’t always so. For instance, I had to abandon one extension that I thought at first would take me a few days, but later looked like many weeks of tedious hacking. It’s still on my to-do list, though: it’s useful functionality that should be present in an e-commerce system, but as far as I can tell nobody’s done in Magento yet. I tell you, if I ever make it work I’m going to be a fucking millionaire.

And then there’s Drupal

Now, I only have a few weeks’ actual experience with Drupal. I’d started playing with it a few months ago in preparation for a large volunteer web design project—that will end up using another CMS, but that’s another story. Then last month at work I finally got to do some actual Drupal development.

And it’s true, there’s a steep learning curve, which I’m only starting on. But unlike Magento, the learning process really isn’t frustrating. There are plenty of resources to help me with issues, and at the end of the day I just feel productive: Drupal development is way more straightforward—no mucking around with core classes, no having to create a dozen files in five different directories just for a simple “Hello World” module. Drupal is complex, but not overly complicated. It feels elegant. It makes sense, and I like that. Drupal is, frankly, a joy to work with.