Any similarities to website configurations, current or defunct, is probably somewhat intentional. But I’m not naming names here in this tale of woe.
The situation:
You have your website: domain.com
You have a staging version where you test all your code: stage-domain.com
Your coders edit the theme code, install plugins, configure them, etc. At the same time, your editors edit content, make posts, etc. The code (plugins etc) is managed by Git because you know you should be using versioning. The posts are not because… WordPress.
The Problem:
In order to post content, you have to push code and content up to the production server. This means that if you’re trying to upgrade WordPress, you have to put a hold out on content until the upgrade is done. How do you keep the two in sync, without goobering your content?
An Answer:
Decouple content and code.
First let’s version control the php files. This means your themes and plugins will all be edited on staging, and when you’re done, you check the code in and then check it out on production.
Next we attack the DB. Have a job that, when you check out files in production, it copies up only following tables:
_options _users _usermeta
This means that all your code (plugin settings, theme settings, etc) gets pushed live, and all your content remains isolated. If you have extra tables, you may need to make allowances for them (like, say _podpress or _badbehavior), but you can find them quickly looking at the current DB. You’ll want to add these as necessary, or not. I’d count _podpress
as ‘content’ and leave it out (we’ll get to what to do with it in a second), and _badbehavior
as transient data.
On production, you have your content makers be Editors. They edit the content live (because you trust them). If you want to be extra secure, lock down the server via IP rules. At the end of every day, run a reverse sync, where the staging DB’s posts and content are replaced by the live site’s, thus ensuring everyone has the ‘live’ data. Obviously you’d want to script in a serialization safe search/replace after every sync (and have an auto-backup taken before any messing about starts).
It’s in the copy back that we decide what to do with the other files. Either make an exclude list or an include list, whichever makes you feel safer.
Includes would be:
_commentmeta _comments _postmeta _posts _terms _term_relationships _term_taxonomy
And then anything else you want like _podpress
(see? I told you I’d get back to this).
Excludes would be anything I have in my push script from before, so _options
etc. To this I’d also add _badbehavior
or anything transient. I don’t really need it.
Pitfalls:
Content isn’t just posts and pages. What if your ‘editors’ need to edit widgets?
What if they don’t? StudioPress’s Genesis Framework has a totally awesome widget called “Featured Page” which lets you pick a page, check options, and off you go. What if you had a page called ‘Header Widget’ and in your widget area for Header, put that widget, pointed it to that page, and checked ‘Show Page Content’? Now your editors just need to edit that page!
What if they still do? Grab a plugin like Members and make a special role for “Super Editors” to let them make widget changes. Of course, now you have to uncouple _options
and you’ve lost some of the magic here. This is a bad idea. You want to decouple content and code. Using Widgets for this is a cool idea, but what if you did it another way entirely. What if instead you used custom post types, and just had it show the most recent in the ‘spot’ for that header? Say you wanted an ad to show at the top of the page. Make your CPT for ‘header ads’ and write a post. Then schedule another for Wednesday at 3pm. Boom, your ad gets replaced! This also makes it easy to go a step further, make a Role for Marketing or Advertising, and now only they (and admins) can mess with ads. Downside there is you may end up with a lot of CPTs.
What about when WordPress updates? Well, that’s something to be careful over. If you update staging to (say) 3.5.1, but production is on 3.5, the tables will get updated. Thankfully, WordPress rarely messes with posts and comment tables. The ones that normally get poked with an update is _options, and we’re syncing that anyway. Still, you should set aside some time to test in staging before you go ahead and push this. Since we’re only posting content on production, this will have no impact on your editors.
Alternatives:
Put the content tables on another database. While WP doesn’t make it as easy as it does for custom user and meta tables, certainly you should be able to fiddle with that. Then just sync the rest of the tables. Code like hyberDB already lets you split the DB for load balancing, so you could fork that.
Or … Sync the whole database. But that defeats the purpose.