Using The SEO Framework with Advanced Custom Fields

I’m going to go out on a limb and guess that I am not the only WordPress developer who in recent days (in the wake of their obnoxious Black Friday dashboard ad) has switched allegiance from Yoast to another SEO plugin, and that many of those who find themselves in a similar boat (to mix metaphors) have switched to The SEO Framework.

I’ve only been using it for a couple of days, but I already love it. It does all of the things I actually used Yoast for, without any of the other stuff I did not use it for. I mean honestly, maybe readability scores and “cornerstone content” do provide an SEO boost, but I barely understand how to use these tools, so good luck explaining them to my clients in a meaningful way. I suppose they’re more of a tool for full-time SEO consultants who need to pad out their billable hours. (Sorry not sorry. My opinion on the business of SEO hasn’t changed all that much since 2011.)

It wasn’t until the Black Friday ad that I really admitted to myself how much I don’t like Yoast. It does a lot of important things, and does them very well. But it’s obnoxious as hell about it. Pushing features you don’t really want or need into every page of the WordPress admin, and plastering its own over-designed admin screens with tons of garish ads promoting its “premium” features.

Yuck.

The SEO Framework encapsulates all of the key features I liked about Yoast into a single configuration screen, which kindly adheres to the standard WordPress admin UI design language instead of infusing its own brand style into every button and metabox border. It’s refreshingly boring to look at. And it just has the stuff I actually use, like title and description, OpenGraph tags, sitemap XML, the basic elements of SEO that unequivocally matter and can be a pain to build and maintain on your own.

But enough about all of its great features. There’s one key thing it lacks: support for Advanced Custom Fields. My standard “modular design” theme relies almost entirely on ACF’s Flexible Content fields to work its page layout magic, and with all of the page content stored in custom fields instead of post_content, there’s nothing for The SEO Framework to latch onto to auto-generate meta descriptions.

Fortunately, the developer has built in some hooks to allow you to customize the meta description output.

Here’s a barebones starting point:

function my_seo_framework_description($description, $args) {
  if (empty($description)) {
    $description = ''; // Add your own logic here!
  }
  return $description;
}
add_filter('the_seo_framework_custom_field_description', 'my_seo_framework_description', 10, 2);
add_filter('the_seo_framework_generated_description', 'my_seo_framework_description', 10, 2);
add_filter('the_seo_framework_fetched_description_excerpt', 'my_seo_framework_description', 10, 2);

As the developer notes, it’s very important for SEO not to just output the same static description text on every page. You need to have a function of your own that will read your ACF field content and generate something meaningful here.

Fortunately in my case, I had already done that, for generating custom excerpts from ACF content, so I was able to just stick a call to that function into the // Add your own logic here! line. You’ll need to customize your function to suit your specific content structure, but here’s the post that I used as a starting point for my function.

Have fun!

Why live shipping calculations for ecommerce are the bane of my existence

I’ll be frank: I don’t really like ecommerce. I’ve floated for most of my 23-year career in web development in the fantasy of a Platonic ideal of an online world: bytes streaming from point to point on a perfect network. No messy complexities of the physical world to concern me.

Well, obviously none of that is true. And I’ve been dealing with ecommerce in some form or another for at least 20 of those 23 years. But I still find the nitpicky details of sales tax and shipping logistics both mind-numbingly dull and infuriatingly (seemingly pointlessly) complex.

Which brings me to the crux of today’s rant. Bringing simplicity to all of this… or, at least, to the part I have to deal with.

Granted, I am not an accountant, and I don’t have to deal with balancing my clients’ numbers at the end of the quarter. But from my perspective, there are some simple solutions to the fact that calculating shipping sucks.

These days I deal mainly with WordPress, and therefore with WooCommerce. There are a lot of extensions to WooCommerce now for live shipping rate calculations. You can pay an annual fee for tools that connect you to shippers’ APIs to give you exact shipping rates. And all you need to do is:

a) determine exact weights and dimensions of all of your products,

b) figure out exactly what types of boxes/packages you’re going to use (and determine their exact weights and dimensions), and

c) plug all of those numbers into WooCommerce, and also

d) decide which of those types of packages are applicable to which products, create the appropriate Shipping Classes, and apply them to the corresponding products; then

e) set up Shipping Zones for each part of the world you’ll ship to,

f) add shipping methods in each of those zones for each of the shippers you’re working with,

g) go into each shipper, in each Shipping Zone, and select from the dozens of shipping services each shipper offers (although you might have to do this multiple times, if you want to restrict the methods you offer based on your Shipping Classes), then

h) test adding these products to your cart, enter an applicable shipping address, and see if you get the right shipping charges… or any shipping charges at all… because you very likely won’t, because for instance you may not realize that

i) WooCommerce Services no longer offers free access to USPS live shipping calculations for new sites created after WooCommerce 3.6, and you now need to buy a separate extension for $79/year to do that. But the free plugin won’t give you any indication that this is the case; it will still show you all of the shipping methods in the configuration pages, it just won’t give you back any rates on the checkout page, and you won’t have a clue as to why until you stumble upon a small note to this effect on the WooCommerce documentation website.

In case you didn’t guess, I just went through all of this today. And this was on a site that only has 7 SKUs!

Of course, there’s a different way to approach this. You could, instead:

a) offer flat-rate shipping.

Obviously it’s not quite that simple. But here’s the thing: there’s no law saying you can’t charge customers more for shipping than it costs you. That’s the whole idea behind “shipping and handling.” In fact, whether you’re doing the fulfillment yourself, or you’ve hired an outside fulfillment vendor, you’re paying more to ship those products than the shipper’s calculated rate anyway, so you should charge more.

Here’s what I recommend.

First, decide on a shipping method. Or maybe a few. Depending on what you sell, you might want to use USPS Media Mail (if it’s applicable). Otherwise, you’ll most likely want to offer two shipping methods: a) ground (cheap but slow) and b) 2-day air (more expensive but reasonably fast). You choose the vendor you like best: USPS, UPS, FedEx, some guy they call “Crusty Pete” with a rusty old pickup truck, etc.

Find out approximately what it will cost, on average, to ship each of your products, by each of these methods. (This is a good use for Shipping Classes.) Then tack on an arbitrary “handling” fee, say $1 per item for ground and $2 per item for 2-day air.

Now, decide: do you want customers to see a shipping charge, or do you want to offer “free” shipping? My logic for this is relatively easy. You just need to balance these two considerations:

  1. People like to see free shipping.

  2. People don’t like to see ridiculously high prices for individual items.

If you are going to offer “free” shipping, you’re really going to take that approximated shipping cost for each item and add it to the selling price of the item itself. Does that make the price of the item seem exorbitant? If not, go for it. People like free shipping.

As long as the shipping price itself isn’t so huge that it causes people to balk when they see it on the checkout page, you can keep the item prices low and present the shipping charge there. Or, a mix: offer “free” ground shipping, included in the price of the item, and then at checkout give the option of “free ground shipping” or paid 2-day air, with the 2-day air prices set at whatever you determined they should be, minus the amount you had already rolled into the item prices for free ground shipping.

Flat-rate shipping isn’t literally flat rate, as in one fixed price no matter what you’re buying. Generally it’s a formula… a base price, plus a multiplier based on the number of items.

For instance, your flat-rate formula might be $5 base plus $2 per item. The customer buys one item? $7 shipping. Two items? $9 shipping. You just plug the formula into the settings, and the rest gets calculated automatically.

The whole purpose of having shipping charges at all is to cover your expenses. Paying an annual license fee for a live shipping calculator extension, plus paying a developer or consultant for their time in helping you wade through all of these complicated configurations, and troubleshoot the problems that crop up, only to end up charging customers exactly what you’re paying for shipping and not a penny more just ends up costing you money. And the fact that these extensions include an option to add on an arbitrary amount above the calculated rate gives the game away… You don’t have to charge customers exactly what you’re paying for shipping. You just need to cover your expenses.

Your time and (my) mental wellbeing are expenses too. As long as your accountant doesn’t get too frustrated with the variable amount of ancillary income you’re getting from the modest overage you’ve calculated into shipping and handling charges, free or flat-rate shipping is a much simpler and more manageable alternative to live shipping calculations.

Fixing a redirect loop on WordPress sites with WooCommerce when converting the site to all-SSL

Best practice these days is to run sites on all-SSL, not just the parts of the site that “need” it. But not long ago, it was common to apply SSL only when it was absolutely necessary, because SSL encryption meant a performance hit. Not anymore.

You may find, if you’re trying to convert an existing WordPress / WooCommerce site to all-SSL, that reconfiguring your URLs, by using a tool such as interconnect/it’s super-slick, powerful (and dangerous) Search Replace DB tool, that once you’ve made the changes, your home page kicks into a redirect loop, indefinitely cycling between http:// and https:// versions of your URL.

WooCommerce may be to blame!

Specifically, a setting in WooCommerce called “Force HTTP when leaving the checkout”. Head on over to WooCommerce > Settings > Checkout and… um… check it out.

Simply uncheck that box, and, while you’re at it, uncheck “Force secure checkout” since it’s unnecessary on an all-SSL site, save your changes, and your home page should come back to live!

Internet Explorer and the centered background SVG problem

We are rapidly approaching, I believe, a time when we no longer need to worry about things that don’t work in Internet Explorer.

But we’re not there yet.

I’ve started using SVG images on the web a lot, especially for logos, which I tend to put into a flexible-width container at the top left corner of pages. Which is great, except IE has this annoying habit of resizing the canvas for SVGs to fill the container they’re in. The whole logo appears on the page, and scaled properly… but it’s horizontally centered in the container, rather than flush against the left edge.

There appear to be many possible solutions, but for a solution that is 100% fluid/responsive (that is, it will scale with the container), this does the trick.

It does involve manually editing the SVG code, but it’s simple and you only need to do it once as part of the image prep process. In the <svg> tag, look for the viewbox attribute. It will most likely consist of four numbers… 0 0 followed by the width and height of the canvas. After this attribute, simply add width="x" height="y" with the same x and y values from the viewbox attribute.

It works!

How to modify WooCommerce to prevent users from selecting UPS shipping for P.O. Box addresses

Anyone who’s dealt with e-commerce in any capacity probably knows that UPS won’t deliver to P.O. boxes. Well, technically they can’t deliver to P.O. boxes. And apparently they’ll forward packages on to the box owner’s physical address, but they charge a big extra fee to do it. So, you want to avoid it.

Unfortunately, WooCommerce and its UPS Shipping add-on do not account for this, and will accept UPS orders to P.O. box addresses. Not good.

The official WooCommerce developer documentation has an article on how to block P.O. box shipping, but it applies to all shippers. Not what we want.

Also, I’m not sure if the documentation is outdated or what, but their code sample didn’t work for me with the latest version (3.4.3) of WooCommerce, because of the wc_add_notice() function.

I’ve modified the original code to add a check for UPS shipping, and also to use the $errors variable. (I also considered removing the global $woocommerce; line since it seems unnecessary, but I didn’t take the time to test whether or not it’s definitely safe to remove, so I left it in.)

add_action('woocommerce_after_checkout_validation', function($data, $errors) {
  global $woocommerce;
  if (isset($data['shipping_method'][0]) && strpos($data['shipping_method'][0], 'ups') === 0) {
    $address1 = (isset($data['shipping_address_1'])) ? $data['shipping_address_1'] : $data['billing_address_1'];
    $address2 = (isset($data['shipping_address_2'])) ? $data['shipping_address_2'] : $data['billing_address_2'];

    $replace = array(” “, “.”, “,”);
    $address1 = strtolower(str_replace($replace, '', $address1));
    $address2 = strtolower(str_replace($replace, '', $address2));

    if (strstr($address1, 'pobox') || strstr($address2, 'pobox')) {
      $errors->add('shipping', __('Sorry, UPS cannot deliver to P.O. boxes. Please enter a street address or choose another shipping method.' . $datadump, 'woocommerce'));
    }
  }
}, 10, 2);

Important notes:

1. This code may not immediately work for you; I believe the 'ups' string in the conditional line may vary depending on your Shipping Classes settings, so you may need to investigate exactly what values are returned in $data['shipping_method']. Since this code is fired off by an AJAX call, it can be difficult to debug. I was able to crudely debug it by commenting out the conditional, then appending print_r($data) to the error string.

2. This is using an anonymous function, so it won’t work in PHP versions below 5.3. But you’re not using a PHP version that old, are you? ;)

3. The original version checked the address line 1 and the postcode field, rather than address lines 1 and 2. I’ve United States-ified my code because that’s what I needed. If you’re part of the other 95% of the world, you may need to add that back in, with appropriate adjustments to the nested conditional. (I’m not really sure if this issue is as UPS-specific outside the US, so my modifications may not be relevant.)