When you’re building a WordPress site some things belong in themes and some in plugins. Usually anything having to do with display is in the theme and anything that might be used elsewhere should be in a plugin.
As an example: take a custom post type and a custom meta box. Those are things that your end user will probably want to keep around after a theme change. They both belong in a plugin. Because you’re a really good WordPress dev, you don’t use magic strings and instead built a nice set of wrapper methods to fetch those custom meta values for your post type. Maybe as a nice object that someone can pass a post ID into and use.
Should your theme use that class from the plugin? No. Not if you’re building a decoupled WordPress theme it shouldn’t. What happens if your plugin is deactivated? Things break. Instead we should wrap up our API with something in our theme that hides the fact that there’s a plugin involved:
A More Practical Example
The above is a bit obscure, so let’s use something more real world. Let say you’re relying pretty heavily on Advanced Custom Fields (ACF). Should you use get_field
like the documentation says?.
Nope. It’s the same idea as above: a deactivated ACF plugin causes the theme to fail with a function does not exist fatal error. Use a wrapper:
But What if My Theme Depends on a Plugin
Depends means that the theme can’t do its job at all without a plugin. That’s okay, just change your wrapper a bit:
Not having a dependency you need is a truly exceptional situation so throw an exception and let your error handler log it on production or show the error on dev.
Why is this different from a fatal error? Context. The exception tells you exactly what’s wrong and how to fix it. This context is not for you or the person who did the ACF integration. It’s for other developers who might not have reviewed the latest changes. Or, more likely, it’s for future you who will have totally forgotten the context by the time the error crops up again.
What About Just Using Actions in Theme Templates?
Here’s an example:
I think this is a bad solution. It’s the theme’s job to do frontend display. Mixing that into plugins is a recipe for an unmaintainable blob. Sometimes it makes sense to do the above, but most times it doesn’t. Let the WordPress template system do its job and decouple by creating nice abstractions within your theme.