Adventures in WooCommerce: rounding

Here’s another fun thing I found recently. It’s definitely a bug, and I do have a solution, but I don’t know if it’s a good one. I’ve been told that these rounding errors are common in e-commerce systems, so there’s got to be a more general solution.

Here’s the setup: a product worth $29.95; a coupon taking 10% off the cart amount; number of decimal places set to 2.

Add one product and the coupon, and here’s what you end up with: a $26.96 cart total, with a $3.00 discount amount. The database has the correct amounts for the line items ($26.955 and $2.995 respectively) but because of how rounding works, you don’t get to see that. PHP by default rounds to the closest value, and .5 will always round up. Even if I set it to always round up or down, the values would still be off by $0.01.

Here’s my solution: adding a function to action ‘woocommerce_after_calculate_totals’. If there are percentage-type discounts, check if the subtotal and total discount discount amount are off. If they are, adjust one of the percentage discounts (if there are more than one). It works, but I’m not happy with it. It seems there should be something better…

WooCommerce customer list report

I’ve been playing around with WooCommerce a lot lately, and twigged on something that I think is a little design flaw.

It all started when I created a duplicate of one of the built-in reports (the Customer List report), with a few bits of different logic which aren’t really important right now. (Incidentally, creating a report is dead easy with just a few action hooks like woocommerce_admin_reports and wc_admin_reports_path, and can be done right in your theme). This new report keeps the same columns as Customer List, including:

  • Orders, the total number of orders for that customer
  • Money Spent, the total money spent by that customer
  • Last Order (date and link to the customer’s last order)

But while testing my report I noticed that some of these numbers weren’t quite right; they weren’t right in the Customer List report as well. What was happening is that some of this client’s customers had submitted orders using their account email but without logging in. This meant that those orders did not get counted against their account totals, even though I think they should have because they have to be the same person.

Here’s how WooCommerce links orders to accounts: if the user is logged in, the order gets a piece of meta data with key ‘_customer_user’, and the value is the user ID. So far so good. But if the order was submitted anonymously, then the best we have is the billing email (meta key ‘_billing_email’). To display the relevant data, the Customer List report uses two built-in functions, wc_get_customer_total_spent() and wc_get_customer_order_count(), defined in file woocommerce/includes/wc-user-functions.php. Both take a user ID as parameter, and run similar SQL queries. For example:

global $wpdb;
$spent = $wpdb->get_var( "SELECT SUM(meta2.meta_value)
  FROM $wpdb->posts as posts

  LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
  LEFT JOIN {$wpdb->postmeta} AS meta2 ON posts.ID = meta2.post_id

  WHERE   meta.meta_key       = '_customer_user'
  AND     meta.meta_value     = $user_id
  AND     posts.post_type     IN ('" . implode( "','", wc_get_order_types( 'reports' ) ) . "')
  AND     posts.post_status   IN ( 'wc-completed', 'wc-processing' )
  AND     meta2.meta_key      = '_order_total'
" );

The logic to get the latest order is defined in woocommerce/includes/admin/reports/class-wc-report-customer-list.php and uses get_posts() with a meta query on key ‘_customer_user’, like so:

$order_ids = get_posts( array(
	'posts_per_page' => 1,
	'post_type'      => 'shop_order',
	'orderby'        => 'date',
	'order'          => 'desc',
	'post_status'    => array( 'wc-completed', 'wc-processing' ),
	'meta_query' => array(
		array(
			'key'     => '_customer_user',
			'value'   => $user->ID
		)
	),
	'fields' => 'ids'
) );

So my solution is simply to tweak those three queries. The meta query part should change to:

'meta_query' => array(
  'relation' => 'OR',
  array(
    'key'     => '_customer_user',
    'value'   => $user->ID
  ),
  array(
    'key'     => '_billing_email',
    'value'   => $user->user_email
  )
),

Both functions wc_get_customer_total_spent() and wc_get_customer_order_count() will need to load the user object, so that we can get the email. So the queries should look like:

$user    = get_user_by( 'id', $user_id );
global $wpdb;
$spent = $wpdb->get_var( "SELECT SUM(meta2.meta_value)
  FROM $wpdb->posts as posts

  LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
  LEFT JOIN {$wpdb->postmeta} AS meta2 ON posts.ID = meta2.post_id

  WHERE   (
    (meta.meta_key = '_customer_user' AND meta_value = $user_id)
      OR 
    (meta.meta_key = '_billing_email' AND meta_value = '{$user->user_email}')
  )
  AND posts.post_type     IN ('" . implode( "','", wc_get_order_types( 'reports' ) ) . "')
  AND     posts.post_status   IN ( 'wc-completed', 'wc-processing' )
  AND meta2.meta_key      = '_order_total'
" );

WordCamp Vancouver 2014

It was time for Vancouver’s annual WordCamp extravaganza, this time a Developer Edition (the first since 2011)! Good timing, too: last year’s came smack in the middle of the Queer Film Fest and I had to miss one of the shows!

Opening remarks

Nothing too special here: a lot of thanking all the attendants, our fabulous sponsors and equally fabulous volunteers, but one thing did stand out: the organisers mentioned the “WordPress Rangers,” volunteers dressed in distinctive t-shirts, that would circulate around the Camp and how attendees could come to them with any questions or concerns, including if they were being harassed.

A few people laughed, and that was the interesting part. Was it because it came up out of the blue? Or because they didn’t think that could ever be an issue at a WordCamp, or in Vancouver? I don’t know, but I think it’s great that the conference has a code of conduct that explicitly forbids harassment and discrimination, and that the organisers went out of their way to let people know about it.

Now, on to the talks:

Curtis McHale: Getting Started With Unit testing

Curtis McHale stepped us through the theory and practice of test-driven development, focusing specifically on PHPUnit. I have to admit it’s something I sorely need to work on. Some of the tools and tips were WordPress-specific, (e.g.: turning on WP_DEBUG, using WP_UnitTestCase), but most could be applied to any development project. Good stuff.

Mel Karlik: How to Build a Custom Widget

Mel Karlik gave us a quick how-to on creating a widget as a plugin. I’d already tried my hand at this before, but it was nice to get a refresher course. Plus, I learned about Genericons! I thought they were the same icon font as the 3.9 admin dashboard uses but no, that’s Dashicons. Still, same idea.

Ben Lobaugh: Securing your plugin / theme

On the bright side, Ben says, even the pros introduce security flaws in their work. When it happens, the thing to do is own up to it and push an update.

This talk contained a million tips on writing secure code: follow the coding guidelines in the Codex; enable WP_DEBUG to ensure you don’t miss anything like deprecated hooks; don’t trust your users; sanitise everything; use the API, it’ll keep your code simple and do the heavy lifting for you; check user permissions; and so on and so forth. Great stuff, I just need to sit down and be mindful of it when I’m doing my coding.

Morten Rand-Hendriksen: Future Responsive Today

Morten’s talk was in two parts: one on the HTML5 <picture> element, which is intended to replace the tag, and Flexbox, which is pure awesomesauce magic to let you lay out elements in far more flexible ways than we ever thought possible.

<picture> was new to me though flexbox wasn’t; still, I loved the demos and seeing all that it could do.

(Slides here: http://mor10.com/presentations/flexbox/)

Merrill Mayer: Advanced Custom Fields: Beyond the Basics

In the first of 4 lightning talks, Merrill Mayer walked us through her solution to a particular client problem: they needed a custom date field for a custom post type, along with custom “previous post” and “next post” functionality (getting links from this field, not the usual post date), and some customising of the dashboard post list. You can see it in action at bbrc.net/speakers. It was a neat exercise, using a variety of tools (custom wp_query, filters for the links, more filters for the dashboard, etc…)

Tanner Moushey: Introduction to the command line

Second lightning talk: Tanner Moushey introduced us to command-line tools, and why on earth we’d ever need them. Except for mentioning WP-CLI it was very general: Git, ssh, scripts, and vim. Nothing really new to me, but again it’s good to bring it all together.

Christine Rondeau: Responsive web development made easy with CSS and the mobble plugin

Third lightning talk: Christine Rondeau talking about a few tools for responsive design. The first technique is to hide  or reveal content at certain breakpoints, using CSS. It’s simple but effective and flexible; however, the HTML content is still being downloaded, which is an issue. It’s good for small bits of code, but nothing too big.

That’s where mobble comes in: the plugin provides conditional functions for detecting a variety of mobile devices. Good stuff.

Robert Dall: How to create your own robot

Or, how to connect Github with Asana. How do you work with Github when your team is physically somewhere else? One solution is to have your commits show up in Asana.

(Slides and blog post here)

Zack Tollman: Cowboy coding

What’s cowboy coding? Zack explains that it’s risky coding, without testing or staging or any safety net whatsoever. It’s unpredictable and there’s bound to always be collateral damage. So he walked us through some tools to automate deployment and server update processes, to reduce the chances of human error bringing down whole sites or servers.

Tools to test updates locally (Vagrant, MAMP/WAMP); provisioning tools; deployment tools. And my first thought was that I never had to manage a server, but I have installed updates on live servers without proper backup plans. I need to avoid that in the future!

(Slides can be found here)

Luke Woodward: Little-known WP JavaScript helpers

I’ve already used a tiny little bit of the Ajax API, but I figured this talk would be take me to the next level. And it did! I’ll need more time to digest and apply the lessons here, so I won’t bother to summarise the talk. Here are the slides! And here’s his code example!

In conclusion

Success! I learned tons, reconnected with WordPress peeps and met a few new ones. Now I just need to apply everything I learned… and think about doing a talk myself? Hey, why not?

How to display post content via jQuery and the_content()

I solved an interesting little problem I ran across recently; investigating it led me to a fuller understanding of how WordPress does some things.

I solved an interesting little problem I ran across recently; investigating it led me to a fuller understanding of how WordPress does some things.

Here’s the setup: when you click a link, a particular div will have its inner HTML set to a particular post content via jQuery. The code would look something like this:

while ( $loop->have_posts() ) : $loop->the_post();
  $content = get_the_content();
?>
  Link to click
  

Could that be made more efficient? Maybe. I guess you could do it through AJAX, passing in the post ID and returning the content, if only to avoid having a potentially long string in the page source—which in this instance is not an issue. The bottom line is, though, we’re passing in the_content() in .html().

And JavaScript won’t take it. When I try, I get the following error:

Uncaught SyntaxError: Unexpected token ILLEGAL

It seems that the_content() adds an extra character (a newline) at the end, and it’s causing the hiccup. Note that get_the_content(), which returns the unformatted post body, does not cause this error.

My solution was to add a filter to remove that extra character. How? Like this, in my theme’s functions.php

function my_content_filter ($content) {
	return substr($content,0,-1);
}

add_filter( 'the_content', 'my_content_filter', 20);

As you know, this hooks a filter function to the ‘the_content’ action, so that my_content_filter() is applied before being rendered. As you can see all the function does is remove the last character of the content string. The last argument is he priority, and determines in what order the filter functions are applied. Naturally, I assumed that since the default priority is 10, anything greater than that meant that my filter would be applied to the formatted content, having already gone through the default filters—adding <p> tags and educated quotes and that pesky illegal unexpected token.

And it worked! However, I was still curious. I’d never delved into filters before, so it seemed like a good time. Starting with the_content(), defined in wp-includes/post-template.php:

function the_content($more_link_text = null, $stripteaser = false) {
  $content = get_the_content($more_link_text, $stripteaser);
  $content = apply_filters('the_content', $content);
  $content = str_replace(']]>', ']]>', $content);
  echo $content;
}

Pretty straightfoward, right? You retrieve the content, apply any filters, and return it. I’m not sure what str_replace(']]>', ']]>', $content) is supposed to do, though…

As for the default filters, they’re set in wp-includes/default-filters.php

add_filter( 'the_content', 'wptexturize'        );
add_filter( 'the_content', 'convert_smilies'    );
add_filter( 'the_content', 'convert_chars'      );
add_filter( 'the_content', 'wpautop'            );
add_filter( 'the_content', 'shortcode_unautop'  );
add_filter( 'the_content', 'prepend_attachment' );

We can see that the filters are indeed set with the default priority. And, the formats are defined in yet a third file, wp-includes/formatting.php. It’s ‘wpautop’ that interests us. This is the filter that creates <p> and <br /> tags from single- and double-returns. And yes, it does add a “\n” at the very end. So there you go, question answered and problem solved. I know that my solution works, and I know why.

I’m wondering if I could do better, though. Would remove_filter() be useful? But then I’d still have to re-implement something like wpautop—basically, reinventing the wheel. Nah, I won’t worry about it. This is good enough for now.

Northern Voice 2013, Part 3: Tools of the Trade

A couple of talks about social media used not to change your life or tell stories, but for more specific purposes.

A couple of talks about social media used not to change your life or tell stories, but for more specific purposes.

Darren Barefoot: Living the Quantified Life

Darren started out with a personal anecdote, about having a box of his old comics stolen from his storage locker. Fortunately he’d made an obsessively detailed spreadsheet of every comic he had, so insurance was not a problem. Came out really well! That was one early reward the quantified life.

Using technology to monitor and share your input, life and performance has become possible thanks to smaller sensors, computers you can carry with you, omnipresent social media, and the cloud for aggregating and storing. Darren made the distinction between self-documentation such as blogging, and self-quantification, which is more about numbers and the aggregates thereof. There’s also passive vs. active. For example, the Nike Fuel Band (should I add a ™?) monitors your heart rate and steps taken without any input from you. Just wear it and away you go.

Why would you do this? Well, there’s the old saying: “The unexamined life is not worth living”. Measuring your current status and your progress can be a powerful motivator to change some things about your life, and focus on what’s needed. On the other hand, you could be missing the forest for the trees, either looking at the wrong things, or spending so much time measuring and aggregating that you never get around to doing.

It all comes down to what’s best for you. Personally, I use two tools: a fitness app that conveniently lets me record my weight, workouts and food intake (including calories and protein); and Toggl, which lets me record my time.

Brad Ovenell-Carter: Twitter As A Note Taking Tool

Brad Ovenell-Carter spoke about his project to use Twitter as a note-taking and collaborative communication tool in his Grade 11 Philosophy class. Students get to communicate with each other and the teacher during or outside of class (using a particular hashtag to define the class’s online space), but unlike e.g. Facebook, all their conversations are 100% public. Since other adults are part of the digital space, they get to model good online behaviour.

In addition to Twitter, Ovenell-Carter’s class is divided into small discussion groups where students rotate through specific roles: note-takers take notes (obviously), researchers find answers to specific questions (if I remember right, they are the only ones who get to use the wider WWW during class), and so on. Students help each other out, which leaves the teacher free to do more one-on-one coaching. A win all around, it seems.

An important point is that students tweet under their own names: not only is this to keep them accountable, but they’re already building their online brands!

Now, this is a Philosophy class in a private school. Not all subjects are so discussion-heavy vs. information-heavy, so Twitter might not be so useful. Furthermore, the question of access to technology is an important one: not all public school students have a computer that’s all their own and that they can use for Twitter. All important questions, to be sure. Still, it’s a fascinating look at how education will change in the coming years and decades.

Northern Voice 2013, Part 2: Bigger on the Inside

No, this post isn’t about TARDISes, but about stories and the art of storytelling. Neil Gaiman wrote (in Sandman, I think) that everybody has worlds of stories in them, no matter how mundane and small they seem on the outside.

No, this post isn’t about TARDISes, but about stories and the art of storytelling. Neil Gaiman wrote (in Sandman, I think) that everybody has worlds of stories in them, no matter how mundane and small they seem on the outside.

Theresa Lalonde: #story

Theresa Lalonde loves stories. Listening to other people’s struggles and how they’re navigating this difficult world, creates a sense of connection and gets her own creative juices going.

The real gift of a good storyteller is to respect the people whose stories you’re sharing. You have to make them feel that they’ve’ve been heard. Her motto is “Everybody else is more interesting than me”. Which sparked a bit of discussion—no, it shouldn’t be taken literally to mean you’re the least interesting person on the planet. A good storyteller has to be an interesting person, to properly reflect other people. But when you’re in storytelling mode, you’re not supposed to take centre stage yourself. Let other people be the stars.

There are stories everywhere, it’s just a question of watching out for them. Theresa told the of Fraserview Cemetery, where the paupers’ graves hold three people deep, and are recycled every 40 years. But the names are still there, and you can find out their stories.

(She might have told the story of one such pauper, but I don’t have it in my notes. The mention of cemetery names might have got me thinking about this amazing Vlogbrothers video, which led to this even more amazing video, and if that’s not the perfect illustration of Theresa’s point, I don’t know what is.)

She also dispensed some more practical advice: use the “zoom-in” factor, to focus on small details of a story thereby making it more immediate and personal. Also, need to overcome the fear of the blank canvas? Dirty it up, scrawl some doodles and/or nonsense words, that’ll dirty it up and get you ready for the real work.

Photowalk

Friday afternoon was the photowalk, hosted by Vivian McMaster. For some reason this was the first ever photowalk I’ve been on. I think last year the weather was too bad, and in previous years… I guess there were interesting talks going on at the same time.

Anyway, Vivienne took a bunch of us around Vanier Park, offering a few tips on thinking outside the box: trying different angles, different perspectives, wave your camera around and capture the motion blurs. I took a number of photos and I’ve decided to post them all, even the out-of-focus ones, even the repetitive ones, without any self-censorship. It’s liberating, and intriguing. There are actually some nice shots in there… And here they are.

Near the end a couple of us were chatting and I mentioned how I’m a very left-brain person, and how sometimes I doodle to work out my right brain. They suggested doodling with my off hand to really give my right hemisphere a workout. Sounds like a good idea!

Dave Olson: Vancouver: The Untold Stories

What can you say about DaveO that hasn’t already been said? He’s smart, hilarious, and insightful as hell. I won’t go into too much detail his talk because (a) I couldn’t do it justice, and (b) I wasn’t taking notes at all (more on that below). Partly it was about the roots of today’s social media in much older technologies (home-printed zines, CB radio—a recurring theme, he talked about that one in a previous NV), the history of Vancouver’s music scene (complete with tickets and posters), and tips on finding stories.

Basically: get out of your comfort zone, and find weird things. And you don’t even have to look that hard, either. The weirdness is all around, from a street in North Vancouver named after one of the Group of Seven who lived in Kits for a bit and kick-started Vancouver’s art scene, to anecdotes of bands playing in long-gone clubs, every block in the city is crammed with stories that just need a little digging.

DaveO did some of the work for us, too. He had a couple of large envelopes from which the audience could pick out cards with names to start from. I picked Frederick Varley (the aforementioned Group of Seven artist). I was a bit relieved, since it seemed easier than digging up dirt about old Gastown clubs and whatnot, but maybe not. Who knows what this’ll lead to?

One last point about the talk: at the start Dave invited a few audience members to come up on stage and handle some of the props (posters, records) while he sat in his comfy chair and babbled on. So I went up, sat by the fire and put a couple old records on.

A friend told me afterwards that I was brave for going on stage. Brave? Maybe, I don’t know. I’m not usually one for the spotlight and yes, there was a little part of me that hesitated, but I made the leap without trying too hard. I guess bravery is in the eye of the beholder. Going up to talk to a cute guy? Really hard. Being on stage in front of a few hundred people, at least when I’m not the centre of attention? Apparently not that hard. Go figure.

Northern Voice 2013, Part 1: We’re on a Journey

Another amazing Northern Voice conference, this time at the HR MacMillan Space Centre. An excellent venue, and conference-goers got to visit the Museum of Vancouver for free! As a bonus, a really cool avant-garde play, which I will totally be blogging about.

Just like last year I’m writing several Northern Voice posts, each grouping related talks together.

Another amazing Northern Voice conference, this time at the HR MacMillan Space Centre. An excellent venue, and conference-goers got to visit the Museum of Vancouver for free! As a bonus, a really cool avant-garde play, which I will totally be blogging about.

Just like last year I’m writing several Northern Voice posts, each grouping related talks together.

Mark Blevis and Bob Goyetche: Podcasters Across Borders

The morning keynote speech by Mark Blevis and Bob Goyetche, recounted the history of Podcasters Across Borders from its first year in 2006, until they chose to close it down in 2012.

It was a fascinating story. I’d never heard of PAB before, not knowing anything about the podcasting scene, but I could see the parallels between it and Northern Voice. The first few years—aka the “Podeozoic Era” were apparently focused on the technology. Producing audio, editing, all the nitty-gritty details. 2009 was the transition year, named the “Jowisic Era” after Jowi Taylor, creator of the Six String Nation guitar; from then on (the “Creatious Era”, 2010–2012) the focus was much more on creativity and building community. (I’ve noticed this trend in NV as well.)

It was a great look at two very passionate people who created “a conference about journeys”, and the lessons they learned along the way:

  • About values: you must have values and be committed to them. They’re much more than goals; goals will tell you what you’re trying to achieve, but not why, nor how you got to where you are in the first place.
  • How to nurture creative spaces: for instance, having only a single track, to strengthen bonds between attendees. And they experimented with room layouts, to make the talks a bit more intimate. In 2010 they moved the conference to the National Arts Centre in Ottawa from Kingston, which apparently gave the creatives the upper hand and scared off all the marketing people.
  • Choose to grow. “Look beyond the fishbowl” and don’t be afraid to experiment. You may lose attendees who expect the same things year after year, but you’ll pick up others who will grow with you.
  • Trust yourself, your instinct and your passions. A strong and safe creative space needs curators, and this was something you couldn’t organise by committee. Apparently in any arguments they had, whoever was most passionate won.

And then PAB ended, because they wanted to move forward to other projects; to do that, they had to close that chapter of their lives.

Mike Vardy: Life Changing Blogging

Okay, so this is the second slot, and already another talk about journeys? I’m sensing a theme…

Not that I’m complaining. I love listening to Mike Vardy, his nerdy shout-outs, the inspirational story of his life. From Costco employee out east, to Costco employee in PoCo, to comic, podcaster, writer and productivyist, which is totally a word now.

My take-aways:

  • You have to do, not try. Yes, I know Yoda said if first, but it bears repeating. If you’re going to go for something, you have to really commit to it, not just half-ass it and tell yourself that was good enough when/if it fails.
  • You have to work to live, not live to work. The much more laid-back West Coast lifestyle was a bit of a culture shock to him, where employees would call in sick because the weather was too nice to work. Your job is there to let you lead a fulfilling life.
  • Sometimes you have to move on. See the PAB story above. There were some projects he had to let go, in order to start something else. Because there’s no such thing as multitasking. The important thing for creatives is to be open to these kinds of shifts.
  • But if you focus, you can achieve amazing things.
  • You have to keep your integrity and care about your reputation. He doesn’t do infomercials, doesn’t shill, and in the past he quit a lifehacking site (I forget which) because his boss wanted him to write about recipes. Something about how to arrange the lettuce and the buns in a hamburger? It sounded pretty inane, anyway.

John Biehler: How Blogging Changed My Life

This is the story of John Biehler’s adventures in the last couple years since he blogged for the 2010 Olympics and then the Paralympics: he took 6 weeks off work and blogged the hell out the events. Then got invited up to the Yukon. Then Chevy offered him an electric car to drive to SXSW in; then, a trip to Alaska

The focus here was different than Mike’s speech. It’s not so much about a lifelong journey from here to there, but more about choices that changed his life very quickly.

The main take-away for me is: seize the day. I haven’t done enough of that so far, and I need to step up my game. And also, that personal integrity doesn’t have to stop you from having fun.

Last take-away: 3D printing is fucking awesome. That’s his latest interest, and he brought a couple of his creations for us to gawk at. The future is here!

CSS3 tidbits: background-size and box-sizing

A couple little CSS lessons I learned recently and wanted to pass on…

background-size

I learned about this property a couple of months ago, in a local WordPress meetup. As the name implies, this is used to set the size of background images, as a value or a percentage of the element’s dimensions—and you can set the background height and width separately. There are also a couple of keywords (“contain” and “cover”) to do a couple of extra tricks with the image.

It’s a neat little property that so far I haven’t used myself; but in the example given, the Ridge Meadows Recycling Society, it looks very good. A couple points that were brought up:

  • Apparently, if you’re applying background-size to the entire page, it has to be set for <html>, not <body>. I’m not really sure why that is, though.
  • If you’re using the cover or contain keywords, you need to make sure your background image is at least as big as your element; otherwise it’ll stretch, and obviously not look as good.
  • Not CSS-related, but one of the presenters mentioned working on WordPress templates through text editors + FTP, not through the admin dashboard. That had honestly never occurred to me before, but it makes sense: in the dashboard you can’t roll back your changes since there are no backups, and if you mess up it could take down the whole site (and then you’d have to fix it via FTP anyway).

box-sizing

I needed to set a form field’s width to be exactly as wide as its surrounding div, even counting padding and border, and since I wanted to make the theme responsive I couldn’t set fixed width values. The answer is the box-sizing property. Set its value to “content-box”, and it will draw the element as you’d expect, with the padding and border adding to its width. Set it to “border-box”, and the padding and border will be drawn inside the border box, thus allowing for a snug fit if you write a rule like so:

#sidebar .searchform .s {
  display:block;
  box-sizing:border-box;
  width:100%;
}

box-sizing is supported by most browsers nowadays except Firefox. Not to worry, there are vendor-specific rules you can use (-moz-box-sizing, etc…)

Drupal and Node.js

I’ve been working on a project that required real-time Javascript updates based on user actions (including other users’ actions). At first the only solution I could see was polling the server every second via Ajax, but that (a) was very resource-intensive, (b) didn’t give me as much freedom with the JS as I wanted (though maybe that was my relative lack of experience with jQuery), and (c) though pretty close, wasn’t exactly real-time. I did my best to reduce the computations on the server as well as the bandwidth, but the result was still not that good. I had to find another solution, and I did: Node.js.

I’ve been working on a project that required real-time Javascript updates based on user actions (including other users’ actions). At first the only solution I could see was polling the server every second via Ajax, but that (a) was very resource-intensive, (b) didn’t give me as much freedom with the JS as I wanted (though maybe that was my relative lack of experience with jQuery), and (c) though pretty close, wasn’t exactly real-time. I did my best to reduce the computations on the server as well as the bandwidth, but the result was still not that good. I had to find another solution, and I did: Node.js.

As it happened, there is a Node integration module, but it still meant I had to set up a Node server somewhere. My localhost seemed like the logical place to start, especially since a Mac installer was available to download. The installation process wasn’t too hard; it did force me to dig into the command-liney parts of my Mac, which was a new experience. Likewise, I gave myself a crash course on Apache virtual hosts and iptables and a few related tasks. Problem, I could never get it to communicate with my local Drupal install, and I’m still not sure what I was doing wrong. In hindsight I probably should have taken better notes.

Things went a lot better when I installed Node on the actual production server. Different system, so I had to find different instructions: here for the Node server, and here for the Drupal side of things. And it worked, pretty much right out of the box! I say “pretty much,” since the instructions are kind of incomplete, and in at least one case the automatic config generator was wrong, and I had to tweak the config file manually. I’ll be blogging more about that, as well as raising the issue with the node.js project. As soon as I double-check that it really is an issue.

So yeah, we do live and learn. I’m glad this project pushed me out of my comfort zone and made me learn Linux development… not to say I’d ever do it full-time, but hey, knowledge is never a bad thing.

Argument 2 passed to db_delete() must be an array, string given

Sometimes it’s the stupid little things that trip you up. Just recently, I encountered an odd problem with db_delete() which I couldn’t figure out at the time; and, in fact, kept barking up the wrong tree until I found a proper example to work from. Even then I didn’t realise what I’d been doing wrong until yesterday.

Sometimes it’s the stupid little things that trip you up. Just recently, I encountered an odd problem with db_delete() which I couldn’t figure out at the time; and, in fact, kept barking up the wrong tree until I found a proper example to work from. Even then I didn’t realise what I’d been doing wrong until yesterday.

Very simply, I was calling db_delete() with an IN condition, like so:

$mytable_res = db_select('my_table','mt')
  ->distinct()
  ->fields('mt',array('key'))
  ->execute();

$key_arr = array();
foreach($mytable_res as $row) {
  $key_arr[] = $row->key;
}

db_delete('my_other_table','mot')
    ->condition('iid', $key_arr, 'IN')
    ->execute();

The first thing you’ll notice is that I should have been using db_query->fetchCol() to get the array, instead of tediously building it via PHP. I didn’t twig on that until later, because running that db_delete gave me an error: Argument 2 passed to db_delete() must be an array, string given

What did that mean? Googling didn’t really help. It was either something so obvious it didn’t bear mentioning, or something so obscure no one was talking about it. After a bit of thought I found an example to work from in the Drupal code base; specifically, in function aggregator_aggregator_remove modules/aggregator/aggregator.processor.inc:

$iids = db_query('SELECT iid FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchCol();
if ($iids) {
  db_delete('aggregator_category_item')
    ->condition('iid', $iids, 'IN')
    ->execute();
}

I adapted that to my own tables and fields (using fetchCol(), too), and it worked fine, but I still didn’t understand why my first attempt didn’t work. Finally, after some trial and error, I found the answer: db_delete doesn’t take a table alias. I was so used to using them in select functions that I automatically added one this time, and then kept on having a blind spot about it.

So, yeah. I wasn’t sure if I wanted to blog about this, but hey, we all make stupid mistakes, right? Might as well own it, learn, and move on.