In any CMS system there is usually some form of convention for how data is organized into structures we can understand and put a label on. In WordPress this is the “post system”. By default you get 2 post types, “posts” and “pages”. The pages are a unique type of post, basically the first and only default “custom post type”. These pages support a hierarchal structure (although we don’t see that used a lot in practice). What makes the system versatile and powerful is of course it can be extended. Like most aspects of WordPress, developers (like us at SaberWP) can tap into the post system and register custom post types. A significant percentage of all the plugins in the WordPress directory that involve some form of content such as an events plugin or any type of ecommerce plugin, portfolio plugins all register at least one, and often several custom post types.
Now we’re not here to bash WordPress, but we’re also not here to pledge allegiance to it either. We’re here to evaluate honestly what we think works and what doesn’t work, and think about why. And having worked with WordPress over 10 years at a code level, I’m in a decent position to say what does and doesn’t matter on real world projects including sites that have unique and sophisticated requirements. As I proposed in the title of this post, WordPress fails to structure data properly. Now this is a bit of a bold statement, because I’m not sure that if we survey 100 developers from different backgrounds we’ll get any real consensus on how we should “store data properly”. After all storing data is one of the key things we do as developers, so there are almost certainly different preferences and different ways of looking at things. However, there are also certain conventions and principles of data management which do apply in a CMS and certainly do apply when you think in terms of API development and application development.
WordPress has a simplistic way of storing it’s post data. The main post data goes into a single database table named “posts”. You’ll find this usually prefixed such as “wp_posts”. Then any additional data, which is called meta data in the WordPress approach, this is stored in the “post_meta” database table. Again prefixed this will often be found as “wp_post_meta” if “wp_” is your database prefix. Now what is wrong with this structure? And let’s keep it practical. Well for basic read operations, in other words as a way to stuff some data into the database and then later read it and render it, this approach works pretty well. Popular field plugins for WordPress such as ACF (Advanced Custom Fields) use this storage to support a wide range of form field types. It’s convenient, it works, so why would we say it’s a bad design?
The fatal flaw of post meta is the complex joins needed when querying the data. Meta queries are by nature not good for scalability. They are unusually complex, unusually slow, and can cause your server to light on fire and burn down the entire building. Their really a health hazard. We often take over projects where post meta has been used (not successfully) to create custom functionality. At a code level it tends to look rather clumsy. More importantly, it scaled not well at all. And unfortunately, it’s not such a simple thing to fix. Generally the only way to return to a good data structure, is to more or less throw away the bad structure and start over. That is a costly mistake. And that’s why we have to suggest this is a “flaw in WordPress”, even though as you may already gather from this example, it’s actually the developers choosing the wrong solution that created the problem. WordPress has a MySQL database, and developers always have the option to use custom database tables. And when you need to be able to search or filter data or store data in a structured way, this is usually the right choice. And if you’re not sure if post meta is acceptable for your use case, just bear in mind how costly a rebuilt might be if you choose that route, invest into coding solutions for it, and later have to rework most of the code.
Now let’s examine that key point further, whose fault is it that WordPress has fundamentally weak data structures? Well, I actually might have given WP core a pass (as many other developers do on this topic) before I met Strapi. You see Strapi, the NodeJS CMS, a headless CMS, is not even remotely as successful in terms of usage compared to WP. Well, what is right? Yet Strapi manages to pack more power in this area than WordPress by a massive overwhelming margin. Strapi get’s it right, out of the box. In Strapi when you make a Content Type, a new database table is created. That in itself is a good start. What’s even better, is when you make fields, these are now columns in your custom table. And here is the knockout punch, when you have two content types and you need a relationship between then, with Strapi you get structure that supports a wide range of relationship types. Now these could just be called CMS features, or data structure features. But I’m not sure that is the right way to look at it. This is not just a feature, it’s a paradigm. It’s a way of organizing the entire system so that it works and supports the growth and development of the system.
In WordPress when we want good data structures, we’re on our own. That means we have to build the tables, on our own. If we want those tables installed during plugin installation, we have to write code to do that and it’s a bit of a pain to manage especially when plugins evolve and tables change structure. Any relationships between data, we have to build out the entire approach with code. WordPress in this sense, gives us nothing to work with. We are more or less just “bolting on” an application written in PHP, that sits inside of WP. And this is part of why we can’t entirely blame other developers for trying to do more with the post meta system or transients or options. It’s logical and even considered good practice to try to work with the system as it’s provided. Arguably building something that is more of a “bolted on solution” is not really “integrating with WordPress” as much as “working separately near WP”. It begs the question of course if your development work isn’t really helped by WP, then why build for WordPress at all? Well the key reason is because it allows the rest of the site to benefits from all the great things that the WordPress ecosystem has to offer. It’s because clients still find it valuable to start with a WP site, then bolt on more complex solutions over top of it. Switching away let’s say to Laravel would mean having to build out dozens of features that we take for granted in WordPress and might not even notice until they’re gone.
What are some of the options you can explore in the WordPress ecosystem already that will help create scalable data structures? One option is JetEngine’s support for “Content Types”, not to be confused with “Custom Post Types”. These work in a similar way but instead of the weak WordPress post meta system, they use custom database tables. Although there are less available features with this approach, compared to JetEngine’s existing CPT support, it is the difference between a solid data structure and one that has this performance and scaling limitation. We’ll be keeping an eye on that product as it evolves as well. Right now it’s arguably the closest thing to a Strapi style builder that creates scalable data structures but inside of the existing WordPress ecosystem. JetEngine is a paid product, one that we have used on this site and many client sites so we do endorse it, links below are affiliate links.
ACF (Advanced Custom Fields) that we mentioned earlier does in fact have an option in the 3rd party support ecosystem that you can use to store your data in custom tables.