Tag: wordpress

Exclude certain posts from search

Excluding posts (or custom post types) from search results when they meet certain criteria is easy in theory. Just use the pre_get_posts action to modify the WP_Query arguments used to get the results. However there are a couple of small pitfalls to watch out for. Below are brief explanations and snippets for excluding posts based on a taxonomy term and a custom meta field value.

Excluding posts by custom meta field

This seems obvious – a meta_query with a != compare statement. However if you stop there, you will only get posts which have the custom meta field you are matching, but not the value. Posts – e.g. different post types – that don’t even have the meta field will be excluded. To work around this we need to add another part to the query that uses the a NOT EXISTS compare and join them with an OR relation.

Exclude posts by taxonomy term

Taxonomy terms are slightly easier – but the trick is in the choice of operator – it has to be NOT IN rather than NOT EXISTS or  !=. While they all sound like they would work, only using NOT IN will allow you to exclude posts that have that term, while including all posts with other terms in the same taxonomy and also posts which do not have that taxonomy at all.

Here’s the full snippet:


Ignore spaces when group MySQL query results

Let's get a list of all duplicate postcodes (could be any string of course) out of a specific field in the database. This could be handled in PHP but, with a decently spec'd server especially, it's quicker to handle it in the database.

The basic query looks like this: (this example is from WordPress but the principle is the same regardless)

global $wpdb;
$meta_key = 'customer_address_postcode';
$postcode_counts = $wpdb->get_results( $wpdb->prepare(
SELECT meta_value as postcode, COUNT(*) as count
FROM $wpdb->postmeta
WHERE meta_key = %s
GROUP BY meta_value
HAVING count > 1
) );

This will return an array of objects each with 2 values – postcode and count, in descending order of the number of times they occur, and only if they occur more than once.

However, spaces have no meaning in a string like this. BS1 3XX, BS13XX and even B S 1 3 X X are all the same postcode and need to be considered duplicates.

Ignoring spaces

I'm not a SQL expert, and there are a lot of functions that I have never heard of, but there is the very sensibly named REPLACE function that replaces all occurances of a specified string within a string. It would perhaps be more common to see it used when updating the database, but we can use it to ignore spaces in our GROUP BY.

global $wpdb;
$meta_key = 'customer_address_postcode';
$postcode_counts = $wpdb->get_results( $wpdb->prepare(
SELECT meta_value as postcode, COUNT(*) as count
FROM $wpdb->postmeta
WHERE meta_key = %s
GROUP BY REPLACE( meta_value, ' ', '' )
HAVING count > 1
) );

This comes with a performance penalty of course but depending on how frequently you need to run the query, and if you can cache the results, it is an effective solution to matching like this. You could also add the same REPLACE into the SELECT if you want the results to be spaceless as well.

Reverse relationship query for last ACF repeater sub-field

Advanced Custom Fields (ACF) is a great WordPress plugin for adding custom meta fields. It has a very useful relationship field that can be used to denote a connection from one post to another – importantly this is a one-way relationship. When you are on PostA you can generate a list of all the posts that it is linked to.

Going in reverse

ACF documentation highlights how a clever WP_Query can be used to do a `reverse query`, i.e. when you view PostB you can get a list of all the posts that link to PostB.

What about sub-fields

The reverse query works fine as it is for top level fields, but does not work for sub-fields within, for example, a repeater. Luckily Elliot on the ACF support forum shared some code for doing a reverse query against a sub-field.
The key is just using a LIKE for the meta query.

Note – for a Relationship field, where the value is a serialised array, use:

'meta_query' => array(
		'key' => 'fieldName', // name of custom field
		'value' => '"' . get_the_ID() . '"', // matches exaclty "123", not just 123. This prevents a match for "1234"
		'compare' => 'LIKE'

but for a Post Object field, where the value is an integer, use:

'meta_query' => array(
		'key' => 'fieldName', // name of custom field
		'value' => get_the_ID(),
		'compare' => '='

Latest item in repeater only

Just to get more complicated, now let’s do a reverse relationship query, against a sub-field, but only the sub-field within the latest item in the repeater…
Imagine a post type of Business that has a repeater called ‘Audit’, and within it sub-fields for ‘Audit firm’ and ‘Fee’. The ‘Audit firm’ sub-field is a relationship to another post type called Auditor. On the single Auditor pages I want to show the name of each Business who they are currently auditing, i.e., where they are the ‘Audit firm’ in the last repeater entry.

To get a list of Business post IDs we have to use a $wpdb query; the key is the use of MAX(meta_key) to get the last item in the repeater. This works because ACF names it’s repeater fields repeaterName_X_fieldName, where X is the number denoting when the item was added.

The solution

The code below is heavily based on a Stack Overflow answer from Elliot (coincidence?) with added WordPress and ACF magic and help from Luke Oatham.

$meta_key = 'audit_%_audit_firm'; // meta_key for repeater sub-field.
$meta_value = '%"'. get_the_id() . '"%'; // meta_value, wrapped for use in a LIKE.
$post_status = 'publish';
$businesses = $wpdb->get_col( $wpdb->prepare(
	SELECT businessMeta.post_id // Field we want in the returned column.
	FROM $wpdb->postmeta businessMeta
		(SELECT post_id, MAX(meta_key) AS latestAuditRepeater
		FROM $wpdb->postmeta
		WHERE meta_key LIKE '%s'
		GROUP BY post_id) groupedBusinessMeta
	ON businessMeta.post_id = groupedBusinessMeta.post_id
	AND businessMeta.meta_key = groupedBusinessMeta.latestAuditRepeater
	WHERE meta_value LIKE '%s'
	AND abMeta.post_id IN
		FROM $wpdb->posts
		WHERE post_status = '%s')
) );

Extending WP CLI – wp config update

One gap in the abilities of WP CLI at the moment is the ability to modify an already existing wp-config.php file.

v1.2 introduced the --force flag to overwrite an existing one, but that is the sledgehammer option – so I started working on it myself. I have put a very initial version on GitHub and would welcome feedback and pull-requests. Please don’t use this on a live site!

Is this something you would use? What features do you think it should have? Let me know in the comments.

Displaying you favourite plugins

Updated 02 – 05 – 18: plugins object is now returned as an array.

On a whim this May bank holiday, tucked up on the sofa watching movies, I decided to create a plugin to display my favourited plugins on

After a bit of digging I found you can use the plugins_api() function and pass it a username, e.g.:

plugins_api( 'query_plugins', array( 'user' => emirpprime, 'per_page' => '-1' ) );

This returns an object with some information about the results, then an array of plugins. A stripped down example of the structure is below:

stdClass Object
    [info] => Array
            [page] => 1
            [pages] => 0
            [results] => 57
    [plugins] => Array
            [0] => Array
                    [name] => Autoptimize
                    [slug] => autoptimize
                    [version] => 2.1.0
                    [author] => <a href="">Frank Goossens (futtta)</a>
                    [author_profile] =>
                    [requires] => 4.0
                    [tested] => 4.7.5
                    [compatibility] => Array
                    [rating] => 94
                    [ratings] => Array
                            [5] => 419
                            [4] => 23
                            [3] => 12
                            [2] => 8
                            [1] => 22
                    [num_ratings] => 484
                    [support_threads] => 121
                    [support_threads_resolved] => 81
                    [downloaded] => 1349088
                    [last_updated] => 2016-12-14 5:45am GMT
                    [added] => 2009-07-09
                    [homepage] =>
                    [sections] => Array
                            [description] => Autoptimize makes ...
                            [installation] => Just install from your WordPress...
                            [faq] => Installation Instructions...
                            [short_description] => Autoptimize speeds up your website and helps you save bandwidth by aggregating and minimizing JS, CSS and HTML.
                            [download_link] =>
                            [screenshots] => Array
                            [tags] => Array
                                    [css] => css
                                    [html] => html
                                    [javascript] => javascript
                                    [js] => JS
                                    [minify] => minify
                            [versions] => Array
                                    [0.1] =>
                                    [trunk] =>
                            [donate_link] =>
                            [contributors] => Array

A simple loop over the plugins array will get all the details about a plugin you could want.

A short while later, and a rough and ready version is complete – a basic plugin that registers a shortcode and returns a list of a user’s plugins.

What does the plugin output?

Here are my favourites as an example – the markup is basic but easy to style, with a couple of classes for targetting. I’ve kept it simpler than the layout in wp-admin or the plugin repository, but hopefully with enough info to be useful:

Found: 64

  • Add Descendants As Submenu Items by Alex Mills (Viper007Bond)
    Automatically all of a nav menu item's descendants as submenu items. Designed for pages but…
  • Autoptimize by Frank Goossens (futtta)
    Autoptimize speeds up your website by optimizing JS, CSS, images (incl. lazy-load), HTML and Google Fonts, asyncing JS, removing emoji cruft and more.
  • Block List Updater by pluginkollektiv
    Automatic updating of the comment block list in WordPress with antispam keys from GitHub.
  • Broken Link Checker by WPMU DEV
    Check posts, pages & all content for broken links & missing images to improve SEO.…
  • Bulk Add Terms by Sohan Zaman
    A lightweight plugin to add thousands of taxonomy terms in one go.
  • Classic Editor by WordPress Contributors
    Enables the previous "classic" editor and the old-style Edit Post screen with TinyMCE, Meta Boxes, etc. Supports all plugins that extend this screen.
  • Classic Editor Addon by Pieter Bos, Greg Schoppe
    This free "Classic Editor Addon" plugin makes sure that the new block editor cannot be…
  • Client-proof Visual Editor by Hugo Baeta
    Simple, option-less, plugin to make TinyMCE - the WordPress Visual Editor - easier for clients…
  • Cloudflare by Cloudflare, Inc.
    All of Cloudflare’s performance and security benefits in a simple one-click install.
  • Code Snippets by Code Snippets Pro
    An easy, clean and simple way to run code snippets on your site.
  • Coming Soon Page, Maintenance Mode & Landing Pages by SeedProd by SeedProd
    The #1 Coming Soon Page, Maintenance Mode & Landing Page plugin for WordPress.
  • Custom Post Type UI by WebDevStudios
    Admin UI for creating custom post types and custom taxonomies for WordPress
  • Debogger by Simon Prosser
    Debugging tool for theme authors and reviewers.
  • Developer by Automattic
    A plugin, which helps WordPress developers develop.
  • Disable Comments – Remove Comments & Protect From Spam by WPDeveloper
    Allows administrators to globally disable comments on their site. Comments can be disabled according to post type. Multisite friendly.
  • Disable REST API by Dave McHale
    Disable the use of the REST API on your website to site users. Now with User Role support!
  • Disable WP REST API by Jeff Starr
    Disables the WP REST API for visitors not logged into WordPress.
  • Duplicator – WordPress Migration Plugin by Snap Creek
    WordPress migration and backups are much easier with Duplicator! Clone, back up, move and transfer…
  • Email Address Encoder by Till Krüss
    A lightweight plugin that protects email addresses from email-harvesting robots, by encoding them into decimal and hexadecimal entities.
  • Enable Media Replace by ShortPixel
    Easily replace any attached image/file by simply uploading a new file in the Media Library edit view - a real time saver!

    What do you think? Is there any other info you think would be useful to include? Let me know in the comments.

    Sending your WordPress posts back / forward in time

    Fancy sending your WordPress site back (or forward) in time for a day? Thanks to the multitude of filters it only takes a few lines of code. In this example I’m just going to filter the_time(), but in a future post I will also show you how to put together a more comprehensive function that uses multiple filters (and has a more practical use).

    The filter for the_time() passes two values in – the value it was going to display, and the formatting string used to create it.

    Hooking in to the filter

    This is fairly standard – pass the name of the filter we are using to the function add_filter(), along with the name of our custom function that is going to modify the value:

    add_filter( 'the_time', 'cc_time_machine' );

    However, since we need both of the arguments that the filter passes, we have to add in the optional values for ‘priority’ and ‘accepted arguments’. The default priority is ten, and as mentioned there are two arguments – so that gives us:

    add_filter( 'the_time', 'cc_time_machine', 10, 2 );

    Travelling in time:

    Now we can receive the data in our function and modify the value. None of the arguments relate to the ID of the post being dealt with, but we can use the global `$post` variable to retrieve it. Then we use the get_the_time() function to retrieve the date/time of the post in timestamp format – this makes it easy to manipulate. Let’s send everything 30 days into the past:

    $timestamp = get_post_time( 'U', true, $post );
    $adjusted = $timestamp + ( -30 * DAY_IN_SECONDS );

    Finally, we need to return the new value. Utilising the second argument ensures we format the date in the same way as it was originally requested:

    return date( $format, $adjusted );

    The complete function:

    N.B. this version reads the time travel value ($offset) from the database so it can be controlled through wp-admin.

    Managing multiple WordPress installs with bash and WP CLI

    Jump to the bottom if you want to go straight to the script

    I recently set up a new VPS on DigitalOcean and chose to manage the web stack and sites with EasyEngine. I’m very impressed with EashEngine, but the fact it makes deploying sites so easy shows up how much overhead there is in staying on top of multiple WordPress installs.

    As the recent vulnerability in the REST API showed, keeping on top of updates is really crucial. In the past I’ve used a management system called InifinteWP, but I’ve decided I would rather use fewer tools and instead rely on WP CLI.

    The key commands

    There are four basic commands key to staying on top of updates:

    wp core check-update
    wp core update
    wp plugin list
    wp plugin update --all

    They’re self explanatory, and with these you can find out if there are any updates available, and apply them.


    But logging in to a server, navigating to the web directory, and running potentially four commands is not exactly time saving. Especially when you need to be doing this in a daily basis to ensure critical patches are applied as soon as possible. (Monitoring the vulnerability disclosure lists is a topic for another day.)

    Luckily we can easily automate this with a simple bash script with just a few essential steps:

    • find all WordPress installs and loop over them
    • navigate into their directory
    • run the two WP CLI commands needed to check for updates to core and plugins
    • repeat

    Once the basics work the script can be easily extended with options such as a choice between checking for updates or doing updates.

    EasyEngine hiccups

    The standard way of finding a WordPress install so you can use WP CLI is to search for wp-config.php files since you can be certain it exists. Then navigate to the directory where you found it, and execute the command.

    However, EasyEngine uses a security conscious directory structure with wp-config.php outside htdocs. This is very sensible, but impacts WP CLI the commands won’t run here – we need to move down into the htdocs directory. One solution is to just add a cd htdocs, but that would mean the script becomes specific to this server setup. Instead, just choose another core file / directory to search for – I went for /wp-admin.

    The script

    There are many ways this could be extended or customised – but this gist covers the basics and should be flexible enough to cover both EasyEngine and non-EasyEngine setups:

    Why ask when you can be told

    The last piece of the puzzle is combing the script with cron and mail.

    Instead of logging in each day to run the script and check for updates, we can use cron to run it and email the output. This means I can wait for the server to tell me when I need to log in and run an update, and not have to constantly check.

    For example, on Ubuntu you can $ sudo crontab -e then add 30 6 * * * su myuser -c '/home/myuser/ | mail -s "WP Helper update checker" "" # run at 0630 daily and email.' to run the script at 630 am every day and email the result. Note – this adds the cron job to the root crontab; this means that it will be run as root and so WP CLI will throw a warning. To avoid this su myuser -c runs the command as a chosen user.

    I want to review changlogs and test before updating, so am only running the script in check mode. If you are happy auto-updating you could either pass the relevant arguments to the script or use the native WordPress functions.

    Get terms from a shared taxonomy used on a single post type

    WordPress has a handy get_terms() function that retrives a list of all the terms for a taxonomy – this is great if you are, for example, building a <select> box for filtering a custom post type listing page. But there’s one big problem, if you use this on a shared taxonomy, it will show all terms even if they aren’t used on the particular post type you are dealing with. There is a hide_empty argument that you can pass to get_terms, but this only excludes terms that aren’t used for the default “post” post type.

    What to do about it

    Facing this today, I ended up with this little snippet that utilises a $wpbd query along with get_terms to achieve what we want:

    • First it uses a nested select query to get the IDs for all posts in our custom post type, this is then immediately utilised by the outer select query to grab a list of term IDs from the term_relationships table
    • Then this list of IDs is passed into get_terms
    • Finally it’s output to build the select box

    You’ll also notice in there it is being cached as a transient for 4 hours. Depending on the nature of your site and server you might not need this, or may need to adjust the duration.

    The key parts to modify if you want to utilise this is post_type='cpt' on line 8 and 'taxonomy' => 'country' on line 9. These set the custom post type you want to retrive terms for, and the name of your taxonomy respectively.

    Using Garlic.js with TinyMCE WYSIWYG editor

    Garlic.js is a great library – it uses localStorage to save the state of forms, so if your users accidentally close the tab or browser before submitting, their entry isn’t lost. This works seemlessly for almost all form elements, however WYSIWYG editors like TinyMCE present problems. Luckily it isn’t hard to fix – there’s a comment on the github repository for the project with the snippet you need to add to the init function:

    setup : function(editor) {
        editor.on("change keyup", function(e){
            //tinyMCE.triggerSave(); // updates all instances
  ; // updates this instance's textarea
            $(editor.getElement()).trigger('change'); // for garlic to detect change

    So far so easy. But today the challenge was a little harder as WordPress was in the mix too.

    At Helpful Technology we have a training product called Crisis90 that allows users work through a scenario and compare their responses. It is built on WordPress and makes use of GravityForms to allow users to submit responses to the crisis scenarios presented (there’s also a simulated Twitter environment for practicing real-time responses). When asking users to draft a press release, for example, we provide a WYSIWYG editor – this is native to GravityForms which in turn leverages the bundled TinyMCE library in WordPress. But this means that TinyMCE is being loaded automatically without me being able to modify the init function. Luckily there is a filter for that – tiny_mce_before_init. This filter lets you modify the settings array used to initialise TinyMCE. Despite the lack of a setup item in the defaults or example, it is perfectly valid. You can use this filter in a simple plugin or in your theme’s functions.php:

    As you can see, we’re just adding a setup key to the $settings array containing the snippet from above. I’ve also included a little check that we aren’t in the admin area as I only want this to apply to the instances of TinyMCE used in forms on the front-end of the site.
    Now Garlic.js will function correctly with TinyMCE, and a slip of the finger won’t lead to any lost work.

    Performance revisited – 3 easy steps to speed up your site

    I decided to write a short follow up from the post from last week about the performance of this site, but framed more as a “how to quickly speed up my site”. Even if you can’t or don’t want to go down the line of changing themes (or writing your own), profiling plugins to find the fastest options, or changing hosting company, by spending 5 minutes installing these 3 plugins you can get a significant performance boost.

    1. Squish those images

    Images generally make up the bulk of the download weight of a site, especially in badly behaved themes. While WordPress does compress the smaller versions it creates when you upload a new one, and got better at it in version 4.5, a bit more can go a long way to speeding things up.

    There are plenty of plugins to do this, both premium and free, and they fall into two camps technically – those who offload the processing to another server, and those that do it on your server. The big reason for using the external server type is to lower the resource use on yours or because yours doesn’t have the required libraries (normally cheap shared hosting). However most decent hosts will have no problems, and I prefer to keep things under my control when I can, so I’ve chosen to use Ewww Image Optimizer. Ewww has come out ahead in various comparisons but I’ll leave it to you to choose from the competition. Once you’ve installed it, any new images uploaded will be automatically optimised, both the full size originals and the smaller ones. There’s only one more step – head to Media -> Bulk Optimize and click on Start Optimizing. This will scan the media library and optimise everything previously uploaded. If your theme comes with built in images, you can also click Scan and Optimize which will catch those too.

    Images squished, quick and easy – on to step 2.

    2. Cache money business (to quote Mark Jacquith)

    WordPress is built on PHP and a MySQL database. While the browser needs to be sent HTML files, on the server PHP is used to generate that HTML by reading information from the database. All this takes time, and also resources. When you have more concurrent visitors then the server uses more resources, which causes it to slow down, and eventually become unable to serve them. One way around this is to buy a bigger server, but a cheaper and easier solution exists – caching. Caching is the process of grabbing the HTML that PHP generates, and saving it for a set time. Instead of each visitor going to PHP and the database to generate the HMTL, it is sent to them directly saving both time and load on the server. After a chosen time, or a trigger like publishing a new post, the saved HTML is thrown away and a fresh copy created. This ensures visitors get an up to date version. The exact impact will depend on your site and server, but as an illustration I was running a load test on a client server last month: without caching the site became unresponsive after 36 concurrent visitors, with caching it was still going strong at 1500! NB – the site in question has some very complex pages making large numbers of database queries; also, these ‘users’ all doing indentical actions at exactly the same moments which would equate to a larger number of real-world users.

    This was achieved using the popular free plugin WP Super Cache. Caching is more complicated than image optimisation, but WP Super Cache strikes a good balance between its simplicity and power. Even just installing the plugin and using the default settings on the Easy screen will give your site a huge boost. But it’s worth having a read of the Advanced and Preload tabs to refine things – the settings all have good inline documentation. For most turning on all the Recommended items on the Advanced screen is a good start, as well as reviewing the Expiry Time & Garbage Collection values (the text under the setting box has good examples).

    Your site will now be both quicker and able to cope with more visitors. But there’s one more thing to do.

    3. Minify, compress and concatenate HTML, CSS and JavaScript

    Images squished, HTML pre-generated – but there’s also CSS and JavaScript being sent to users. Have a look at your site using View Source in your browser of choice. Since most themes are fairly badly behaved you’ll likely see multiple CSS and JavaScript files relating to the theme, and on top of that there will probably be ones added by plugins. Each separate file is both adding bytes to be downloaded, and also a connection that has to be opened from the browser to the server which takes time.

    The solution to this threefold:

    • minification – remove spaces and rewrite files to shorten the length of them.
    • concatenation – join files together so that fewer have to be downloaded.
    • compression – server the files from the server with file compression to make them smaller, and send special headers to tell the browser to cache them locally to save downloading them again.

    One plugin can so all this for us – Autoptimize. Install, activate, and turn on the 3 basic options to optimise HMTL, CSS and JavaScript. This one does need a bit more care – there is potential to break JavaScript code in particular. I’d recommend giving your site a good test after this basic activation to check everything is ok. If so, feel free to enable the Advanced Settings and tweak it to squeeze a bit more performance out – it’s all well documented. One advanced setting in particular to look at is inlining CSS. If your theme is quite a light one, inlining it all is best. If your theme is more complex, inlining ‘above the fold’ CSS will drastically improve the apparent loading speed of your site. This is because the browser will have enough information to render the visible area of the site even while it is still loading the rest.


    5 Minutes, that all it will take – probably less time than it took to read this ramble – and you will have netted a substantial increase in performance. If you’re still after more speed things get a bit more complex – a faster server / host or a better theme would be the next on my list. Beyond that there’s object caching, server caching, CDNs, server optimisation, code optimisation… It goes on, but the 3 simple steps here should be all that 90% of sites need. To take things further I’d highly recommend reading this detailed article on WordPress performance and bottlenecks at Smashing Mag.