For a while now WordPress has evolved outside the blog platform and allows to quickly develop websites. So if your are developing websites in WordPress odds are you will want different environments for production, development and possibly staging. Keeping track of plugins and themes needed can quickly become a pain to setup and keep track of. I’ve been using this method for my installs and I find it makes WordPress easier to maintain and make it portable across mulltiple setups.
Here are the tools we’ll be using:
- Composer – to manage and list needed plugins and other dependencies;
- WP-CLI – to install and upgrade the WordPress core;
- Git – for source control of child theme files, configs and more.
Composer to Manage WordPress Plugins and Other Dependencies
Composer allows you to define what your application depends on in a single file. Then you can run a simple command to install and update those items as they. It makes it a breeze to keep up to date with regular security fixes.
First things first if you do not have composer installed follow the global installation setup found at https://getcomposer.org/doc/00-intro.md#installation-nix.
We will create a directory structure to store our project:
mkdir -p example.com/ cd example.com/
Now in the root of the project create a composer.json, this is the file that lists all that we depend on.
Our first version of the composer.json file looks like this:
{ "name": "geekpad/example.com", "description": "Sample project to show how to use composer and wp-cli to manage wordpress", "require": { "wp-cli/wp-cli": "~0.15.0", "composer/installers": "~1.0" } }
On line 5 we define our first dependency, WP CLI this library will be to install and update the wordpress core and it’s described in detail in the next section.
On line 6 we add a installer for composer which will allow us to specify a target directory for theme files and plugins outisde of the default ./vendor directory.
Those install paths are added in an extra section of the composer.json file, which will look something like this:
{ "name": "geekpad/example.com", "description": "Sample project to show how to use composer and wp-cli to manage wordpress", "require": { "composer/installers": "~1.0", "wp-cli/wp-cli": "~0.15.0" }, "extra": { "installer-paths": { "public/wp-content/themes/{$name}/": ["type:wordpress-theme"], "public/wp-content/plugins/{$name}/": ["type:wordpress-plugin"] } } }
All dependencies by default are located on the packagist.org repository, but for wordpress themes and files we need to add the wpackagist.org repository which will enable us to add depencies from the plugins and themes available on wordpress.org website.
The updated file with wp-packagist (lines 13-15) and our main theme an plugins (lines 7-10) looks like this:
{ "name": "geekpad/example.com", "description": "Sample project to show how to use composer and wp-cli to manage wordpress", "require": { "composer/installers": "~1.0", "wp-cli/wp-cli": "~0.15.0", "wpackagist-theme/devdmbootstrap3":"*", "wpackagist-plugin/google-sitemap-generator": "*", "wpackagist-plugin/wordpress-seo": "*", "wpackagist-plugin/jetpack": "2.9.*" }, "repositories":[ {"type":"composer", "url":"http://wpackagist.org"} ], "extra": { "installer-paths": { "public/wp-content/themes/{$name}/": ["type:wordpress-theme"], "public/wp-content/plugins/{$name}/": ["type:wordpress-plugin"] } } }
Our file is now ready, we first install the dependencies using the following command at the root directory of the project:
composer install
Installing WordPress With WP-CLI
WP-CLI provides a lot of functionality to manage WordPress but for this setup though I only use it install and update the WordPress core files. It’s possible to do this via composer but the problem is that when updating it completely wipes out the folder and re-installs the whole package which is problematic for files that need to stay there.
If you followed the previous step wp-cli should have already installed by composer and is found in the ./vendor/bin folder. So we’re ready to start setting up WordPress:
cd public ../vendor/bin/wp core download cp wp-config-sample.php wp-config.php
Now we proceed to setup the MySQL database:
CREATE DATABASE example_blog; GRANT ALL PRIVILEGES ON example_blog.* TO example@localhost IDENTIFIED BY 'supersecurepassword'; FLUSH PRIVILEGES;
Next we Update the db info and add composer autoload file in public/wp-config.php:
/* dependencies autoload */ require_once('../vendor/autoload.php'); /** The name of the database for WordPress */ define('DB_NAME', 'example_blog'); /** MySQL database username */ define('DB_USER', 'example'); /** MySQL database password */ define('DB_PASSWORD', 'supersecurepassword');
Setting up virtual host config for the webserver, in this case nginx:
cd ../ mkdir -p example.com/configs/nginx/ touch example.com/configs/nginx/example.com
Add this to your NGINX server config created in the previous setp.
server { server_name example.com; root /var/www/example.com/public; access_log /var/log/nginx/example.com.access.log; error_log /var/log/nginx/example.com.error.log; # prevents php files to be executed in uploads folders location ~* /(?:uploads|files)/.*\.php$ { deny all; } index index.php index.html index.htm; location / { try_files $uri $uri/ /index.php?$args; } location ~* \.(jpg|png|gif|jpeg|css|js|mp3|wav|swf|mov|doc|pdf|xls|ppt|docx|pptx|xlsx)$ { access_log off; expires 30d; } # avoid logging some common files location = /robots.txt { log_not_found off; access_log off; } location = /favicon.ico { log_not_found off; access_log off; } location = /apple-touch-icon.png { log_not_found off; access_log off; } location = /apple-touch-icon-precomposed.png { log_not_found off; access_log off; } # Pass PHP scripts to PHP-FPM location ~* \.php$ { try_files $uri =404; fastcgi_index index.php; fastcgi_pass 127.0.0.1:9000; include fastcgi_params; fastcgi_param APPLICATION_ENV PROD; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param SCRIPT_NAME $fastcgi_script_name; } # deny access to .htaccess files, if Apache's document root # concurs with nginx's one location ~ /\. { deny all; } } # redirect www.example.com to example.com server { server_name www.example.com; rewrite ^ http://example.com$request_uri?$args permanent; }
Make sure that file is included in the core nginx config file and restart nginx and add example.com to your host file.
You should now be able to navigate to example.com to complete the setup of your wordpress.
At a later point in time if you want to update the core of wordpress and it’s dependencies you can run the following commands from the root of the project:
composer update cd public ../vendor/bin/wp core update
Version Control Wordpress Wth Git
In the first two sections we setup the basic wordpress along with themes, plugins and other dependencies, those files are the files that will not be modififed because they will be overridden if we do updates. Everything else we want to keep track of in our git repository, it will contain the different customizations that we did to make our site unique. Normally that includes configurations, child or custom themes and maybe plugins coded from scratch.
First lets setup the project folder to act as git repository:
git init git remote add origin git@github.com:geekpad/wp-composer-demo.git git pull origin master
We first will create a .gitignore files to tell git which files we will not want to put in the repository.
My basic file looks like this:
vendor/ public/index.php public/wp-activate.php public/wp-admin/ public/wp-blog-header.php public/wp-comments-post.php public/wp-config-sample.php public/wp-cron.php public/wp-includes/ public/wp-links-opml.php public/wp-load.php public/wp-login.php public/wp-mail.php public/wp-settings.php public/wp-signup.php public/wp-trackback.php public/xmlrpc.php public/wp-content/themes/devdmbootstrap3/ public/wp-content/plugins/ public/wp-content/uploads/
We can now proceed to add the left over files:
git add composer.json configs/nginx/example.com public/wp-config.php .gitignore git commit -m "initial settings and files for example.com" git push origin master
You can then follow your normal git workflow to keep the files up to date. Just remember not to commit files that might be updated by composer or are part of the wordpress core. So this is how in my opinion should manage wordpress using composer and wp-cli.
Hey this works! Thanks for posting this just a couple days before I needed to set up a wordpress site 🙂
The gitignore ignores plugins, but only one of the themes; why’s that?
Hey Benoit, Glad it worked out for you. The .gitignore can be tweeked to your own needs especially in the the theme folder. My advice is to ignore parent themes and only commit child theme files.
Great article. Thanks. We’ve been looking for an elegant, version-controlled workflow for WP development for a while.
We’ll implement yours today and I’ll let you know how we get on.
One question…
We use codeship to deploy to both development and production servers, and codeship can run composer at the beginning of a deploy e.g. http://note.io/1CoKVbC
Does that make the above code unnecessary in wp-config.php?
Thanks again for a great article.
Hi Peter,
Hope you like the workflow. Now about your question, I don’t know codeship but the composer command it runs would still require the line in the wp-config.php. That line allows you to use PSR-0/PSR-4 autoloaders, basically meaning it will include files as you need them in your wordpress without the need to do a require/include first.
If you only use wordpress plugins and themes as your dependencies then the line is probably not needed, but there is no harm in keeping it.