WordPress settings missing on the live website?!

When you have been working on a WordPress website with a team you probably know one of these scenarios:

1. A colleague has been working on a specific part of the website and had to install and configure a plugin. On your local environment the website doesn't look the same and in a worse case doesn't work at all because of a missing plugin or setting.
2. A plugin required a tremendous amount of settings to be configured. On the local environment it was all tested but a few settings where not correctly set in the production environment.

These scenario's bothered us enough times to search for solutions.

Our first approach was something like this: We started to share complete databases and uploads. This did work properly during development and for the initial release. Some project got a mayor second release and then we first downloaded the database and started working from there. A few weeks later when we would release the new changes it was no longer an option to put our database back because of new content. So any changed options still had to be made by hand.

We also do projects with Laravel and where kinda spoiled by their neat migrations. So the idea arose to create something similar for WordPress. A first implementation was made by taking a good look at the Illuminate\Database\Migrator from Laravel. The biggest change was storing the ran migrations in an option instead of a separate table. This plugin was working fine for the first project, we where now able to create migrations for options.

A few weeks later we started working on another project and tried to use the previously made plugin. It didn't turn out to be such a success. The main problem was not being able to quickly find out what option was changed. WordPress doesn't have any updated timestamps on options and therefore we had to search in the code of other plugins to see what option names they used. For example some plugins use different prefixes for their options so guessing an option name was not an option.

To get around this issue we've extended the plugin to keep track of all changed options. The goal was to see a list of options changed with the name of plugin or theme next to it. And we dreamed of generating migrations right from that page or viewing a nice diff between the changes. It appeared nearly impossible to determine the origin of the changed option but the overview was working! And for the same project we made migrations to download plugins, the same way as WordPress does, and install zip files of bought plugins.

The plugins however weren't activated after being downloaded and the zip files weren't installing at all because of some yet unknown reason. After a few attempts to fix the migrations there was a light bulb moment. We should be handling plugins at all with migrations but we should use composer for it. WordPress Packagist is a perfect mirror of the WordPress plugin directory and in combination with custom installer paths plugins where installed in the correct location. For paid plugins we download the zip and store it in the repo outside of the webroot. The installation is done with a post-install-cmd in composer.json using WP-CLI.

For example:

"post-install-cmd": [
    "wp plugin install --activate resources/plugins/js_composer.zip",
    "wp plugin install --activate resources/plugins/gravityforms.zip",
    "wp plugin activate --all"

This setup serves us well and the combination of composer and the wp-migrations plugin makes WordPress development a bit more fun.