Ajax in WordPress is something I’ve discussed a few times on this site. For example, I’ve touched on:
But you know how it goes: Over time, things change. WordPress matures, we grow as developers, and techniques and methods that we might have used yesterday (or last year) aren’t necessarily the best way to achieve the same thing as today.
And Ajax in WordPress is one of those things. Although the actual API may still be the same, the way in which we can build object-oriented solutions that communicate with it can be refined a bit more (at least in contrast to what I used to do).
Ajax in WordPress
Assume for a moment that I have a menu page that has a feature that will leverage Ajax functionality. Perhaps this is at the template level; perhaps it’s at the partial level.
For this post, it doesn’t matter. The point is that the menu page will be communicating with the WordPress API to wire up its Ajax functionality.
But, as discussed in several previous posts, this is not necessarily functionality that the options page should be responsible for maintaining. Instead, let’s say the there is a class responsible for registering all of the Ajax requests.
1. An Ajax Manager
Perhaps a descriptive name for this class is Ajax_Manager. The implementation may be something as straightforward as the following code:
The implementation of the class is simple:
It accepts an object
argument and a method
argument. The object is the class in which the method resides that will perform the action when invoked via JavaScript.
Obviously, this could be grown into a more mature class but remember that this class has a single responsibility: Connect an existing class and it’s methods with the WordPress Ajax API.
2. The Page Knows The Manager
Next, since the menu page is the object (in this example) that has the functionality to invoke via Ajax, we can pass this class as a reference in the menu page’s class constructor and set it as a property.
The constructor will look like this:
And the invocation of the class will now look like this:
Now, this example is about as simple as it gets. The thing is, the Ajax_Manager could be passed around to as many classes that need it and though that’s the big idea behind this post, this showing but one way to do this.
3. The Partial Defines The Functionality
At this point, we can now register an asynchronous event on an existing class and its methods. Let’s say that we have a partial that’s part of our options page. This partial, $event_partial
, is responsible for rendering options related to a calendar event.
Specifically, part of that functionality will result in retrieving the taxonomies for a certain custom post type. This method that will achieve this is a method called the_taxonomies
.
Since the options page is composed of partials and since the above partial has a method that will be used via Ajax, the Event partial doesn’t have to do anything but define the method to be invoked via JavaScript.
Everything else is wired up via the page and the Ajax_Manager. More specifically, there are no calls to add_action( 'wp_ajax_{action_name}', 'method' );
.
4. The JavaScript Asks For Information
Finally, the associated JavaScript file (which should ideally be a single JavaScript object all on its own) can make the call like this:
All that’s needed is to make sure that the script is properly registered and enqueued within WordPress.
Alternative Implementations (Always)
As mentioned, this example is meant to demonstrate the advantages of having a single Ajax_Manager class that can be passed around to other classes in the plugin that need Ajax functionality.
This allows classes that need Ajax functionality to maintain a property to an external object that’s responsible for registering it with WordPress without having to implement the details on their own.
This also allows us to easily have a single class to introduce for advanced Ajax functionality in WordPress. Finally, it allows us to keep the JavaScript as independent as possible. All it needs to know is the name of the action to call and the name of the method.
I’m sure there are other ways to go about doing this, but this is one of the easiest, more reusable ways I’ve found when implementing Ajax in WordPress that’s not only useful in a given project, but useful in any project that needs Ajax functionality.
Hey Tom,
First of all: There’s a typo in your page’s constructor in line 8. Also, if you’re okay with PHP 5.3, you should use the shorthand ternary operator:
$this->options = get_option( 'acme-options' ) ?: array();
I have just written a real-time log viewer page this morning (tailing last 30 lines without loading the file into memory), and I had to include a bit of AJAX code coupled to an admin page created through the WP Settings API, so a pretty similar setup to yours (Impeccable timing, I would say!).
I haven’t yet created an AJAX class (thinking about it, but haven’t solidified my thoughts yet). But I have a reusable
Settings
component, and a reusableDependencies
component. These can be combined, so if I pass in aDependencies
object into the constructor of theSettings
class, this will ask theDependencies
object toenqueue()
any dependency when it encounters a handle that is registered as a dependency of a page.My dependency in this case is a JS script, and it comes with “localization” included, meaning that there’s additional code passed through
wp_localize_script()
that allows me to have the JS script’s configuration in my PHP Config files.I’ve copy-pasted some of the code I use in a gist for you to take a look: https://gist.github.com/schlessera/964e12b904457ea7f425a4b68e3ce8da
This code won’t work as is, of course, as it depends on a Service Locator and a Dependency Injector.
I’ll let you know if I have found a way of properly including an AJAX component as well! ;)
Typo’s been fixed. Nice catch. Thank you sir!
For the ternary operator, I tend to be careful when using it. There’s this which is covered in the Coding Handbook and then there’s what you offered.
If someone is brand new to coding, then the ternary operator itself can be a little weird but I think the more explicit version reads a bit more like a sentence. Your version, I like, but I tend not to share that type of code publicly because it requires an additional set of explaining — just trying to strike a balance, really :).
Weird timing..! Do you have this publicly shared anywhere? Right now, I just use Console to monitor the logs whenever I’m working on a project but having a real time log viewer would be nice and maybe a bit lighter (at least, maybe?).
So is there a single method on the dependency object that, when called, will iterate through a list of dependencies? This is kind of off topic from the initial post but now my curiosity is piqued (and why aren’t you blogging about this stuff? :).
Yes — internationalizing JavaScript is not something that should be omitted (I’ve shared some things about it here with obvious inputs from others). Again, for me it’s about breaking down the topic to the core issue rather than trying to “explain the world,” so to speak.
Oh nice — should’ve read this prior to commenting. Oh well :). I like to comment in real time.
Yes — actually this is another thing that I’m looking to cover in a future article (well, starting with a Service Repository and then possibly talking about a greater organization of a service layer and all that can come with it).
I don’t know if I’ll do the latter or not. Depends, really.
Please do. Right now, this is obviously a very simple example but the idea is to continue thinking about single responsibility and how we can spread it across a variety of classes in our WordPress plugins.
There’s obviously more advanced things we can do, but I tend to start from the simplest examples and build up from there. Sometimes, time permits; other times, not so much.
Yes, it might look confusing for someone who does not know about that syntax. But then again, so does every other syntax as well, the first time one encounters it.
I prefer the shortened form, because it makes the code DRY. In your example above, you not only have to type the same exact option name twice (which can already lead to issues ;) ), but you’re effectively doing a database request twice (ignoring the fact now that there’s caching involved).
Hehe, the joys of live commenting! If you look through the code, I think it can mostly be used as is (if you pull in the three required packages through Composer), but you need to change how it fetches the names of the log file(s) to read.
In my case, there’s a centralized logging system involved which can have an arbitrary number and combination of loggers, and the log viewer asks the log managers about all registered file streams (because their could be other logging streams, like graylog, slack, …).
Each page can define handles of dependencies it needs (https://gist.github.com/schlessera/964e12b904457ea7f425a4b68e3ce8da#file-config___defaults-php-L39-L41). When that page gets rendered, it will request these handles from the dependencies object (https://github.com/brightnucleus/settings/blob/master/src/Settings.php#L140-L145).
This system works pretty well, I also use it in my Shortcodes component. Dependencies are registered from the start by the Dependencies component, but are only enqueued when they are actually needed (normally in some render code).
Oh well, looking through my components, I see that there’s still lots of documentation to write…
I try to blog about these things. But one issue I face is that I’m not a natural writer. It takes a lot of time & effort for me to come up with a basic post (add to this that I write in a foreign language). The other one is that I don’t know where to start with the more advanced topics. “Oh, btw, here’s how to have an abstraction of your dependencies that you can pull in through your Config files for each of your view renderers…”
I’m currently trying to spread the word about Config files and their benefits first, because a lot of the other concepts don’t make much sense if you’re still basically starting from scratch with each new project. I have an upcoming talk at WordPress Frankfurt about that, so I might have a video to share soon.
The name of the
wp_localize_script
is misleading… although you can provide translations through it, it is rather a general way of providing data from server PHP code to client JavaScript code. Whatever you pass through this function will end up as a data object in your JavaScript namespace. This allows you to DRY up your code and have a single source of truth (the Config file in my case).In the Config file, you can see that I create an object of name
gaaLogViewerData
with is filled with the contents returned by a Closure (https://gist.github.com/schlessera/964e12b904457ea7f425a4b68e3ce8da#file-config___defaults-php-L12-L23).Then, within the JavaScript file, I can reference this object to get at the data I passed into it. I do this for the following elements:
AJAX action & nonce: https://gist.github.com/schlessera/964e12b904457ea7f425a4b68e3ce8da#file-assets___js___gaa-log-viewer-reload-js-L8_L9
Name of the log file: https://gist.github.com/schlessera/964e12b904457ea7f425a4b68e3ce8da#file-assets___js___gaa-log-viewer-reload-js-L10
ID of the HTML element to put the contents in: https://gist.github.com/schlessera/964e12b904457ea7f425a4b68e3ce8da#file-assets___js___gaa-log-viewer-reload-js-L15
Interval of the refresh: https://gist.github.com/schlessera/964e12b904457ea7f425a4b68e3ce8da#file-assets___js___gaa-log-viewer-reload-js-L20
In this way, I can easily make changes in one place, the Config file, and the rest of the code (PHP & JavaScript) will adapt as necessary.
Ah, another “nitpick”!
In the code above, you’re using doc-blocks (
/**
) in the page’s constructor, where they don’t make any sense. These should be normal block comments (/*
) instead.Yeah, that’s been fixed. It’s a bad habit, TBH — using docblock comments in place of multiline comments. :shrugs:
Having you code review this stuff live makes for more fun blogging :D.
Great tutorial :) Just a question about the code structure. I noticed you used namespaces and some special class (Section 2). Did you use a php framework or a specific pattern to develop the examples, such as MVC?
If yes, can you point me the related one. I’d like to start a new WP plugin with modern tools.
Thank you! :)
No, all of the code you see is written in vanilla PHP.
The ‘special class’ to which you’re referring can be seen in more detail in this post.
Hope this helps!
Thank you for your reply. I appreciate it! :)