In the previous article, we ended up having our application and other libraries installed in the same directory. This type of setup isn’t much use in any situation. Also, the code distribution isn’t very readable for development.
Hence, we want to keep the application itself in the main directory, and the other packages in a place that’s meant for the vendor software. The specific catalogue structure actually depends on the application itself. However, in many cases it will work if we assume that your application is located in /app/docroot/ and vendors in /app/vendor.
Having said that, by the end of this article you should have the knowledge and the ability to deploy your application to the following directory structure:
app
- build
- composer.json
- docroot
- vendor
Composer Plugins
Composer seems to be using quite a straightforward installation flow, but that’s not exactly true.
Composer and its functionalities can be extended using plugins. A more detailed description of the possibilities can be found on the Composer homepage. Our main interest this time is how to change the target path for installing the packages. Well, we can use a custom Composer plugin.
When talking about Composer plugins, it’s worth mentioning the Composer Installers plugin. This plugin can be found on GitHub (https://github.com/composer/installers). It contains installers that recognise many commonly used packages and allows us to customise the install path for them. It’s distributed under a free license, so you can download it and customise it to fit your needs. If you’re using a self-developed application, you’ll need a custom installer such as this one.
The easiest way to approach it is to modify the existing plugin, like Composer Installers, and insert your own logic (which is actually very residual). Of course, you could always build your own plugin from scratch.
Before we go into examples, one distinction must be made. By using a single Composer installer plugin, we may decide on a target path based on the package’s name or package type.
The first case is easily handled by the Composer Installers plugin mentioned above. To utilise it, simply install this package with your Composer, and then place the following code in your main composer.json file:
"extra": {
"installer-paths": {
"../docroot/": ["vendor/app"]
}
}
As you can see – the path goes first, and then the exact package that should be downloaded into it. The path included should be relative to the main composer.json file, which in this case is located in your_app/build/. If you’re managing an application that isn’t taking advantage of any add-ons, this part will work just fine for you. The other case is when your project can incorporate further packages.
To help you visualise a situation like this, consider installing and maintaining your instance with an open source application (e.g. Piwik or WordPress). There really are a lot of applications like these ones that contain tons of ready-to-use plugins that can empower your application.
It is worth mentioning that many of them are already handled by Composer Installers, so a quick look at its documentation may give you the information that you are looking for.
However, still in many cases you may not want to go public by contributing your specific case to Installers.
That is the case when you want to introduce path building based on the package type. This is the second phase mechanism in Composer. After attempting to match it based on its name, Composer Installers will look for an installer that matches the package type. To do so with Installers, we must introduce the next assumption. Plugin package types have a strict naming convention. Most frequently it’s
{main_application_name}-{sub_application_type}
Therefore, in the composer.json file of some add-on packages, we may see a package type like “cakephp-plugin”, “wordpress-plugin” or others like that. A plugin is not obligatory of course, it can be a theme, module, component or whatever is needed.
The first part of the package type on the other hand, gives you the possibility to distinguish the package’s master application at first glance. Like I said above – this convention is only forced by default logic in Composer Installers. It can also be altered to match your specific needs.
Now that we have introduced the Installers, we can start customising them to suit our needs. The following list of instructions is only a shortcut. Its purpose is to provide you with quick start instructions so that you can start reaping the rewards from Composer as soon as possible. Of course, it’s always encouraged to work out something by yourself.
Follow these points to customise the original Installers plugin:
- Download the original Installers plugin (https://github.com/composer/installers),
- Open in your editor file under installers/src/Composer/Installers/Installer.php,
- In line 15 you can find the $supportedTypes array. This is the place where you should inject your new, custom type of Composer package. The key should be the package name, and the value should be the name of the specific installer class, so your new entry should look like this:‘myComposerType’ => ‘myInstallerClass’,
- The interesting thing about registering your own classes to specific types is that you can actually map the custom installer to standard Composer types, like ‘application’,
- Create an empty class for your new installer in, for example –installers/src/Composer/Installers/MyInstaller.php,
- An example of the content of such a file can be found at https://github.com/mgazdzik/installers,
- In $locations you can define the different install paths for each sub-type (plugin, theme, extension etc.),
- That’s it! Your very own Composer Installers customisation is done.
Now that you have prepared the Installers package, you’ll now need to put it in a place so that it can be read by Composer then downloaded and installed. It can be a local repository or any other VCS (if needed – please refer to the previous part of this article at: http://clearcode.cc/2014/05/manage-application-setup-composer/.
After placing it somewhere, it can be used by simply putting the below line into the require section:
“require”:{
"composer/installers": "dev-master",
}.
This entry should be the first thing in your ‘require’ section so that Composer can download and use those installers before any other vendors are being processed.
Now you can try using your newly created installers by trying to deploy your application or its component to the defined path – it should work like a charm.
Great, now you can install all the parts you want, wherever you want. That’s something, but it still has some flaws.
The first and major one is that after running the Composer update command, Composer will be purging the directory of the vendor that is being updated. How can this affect you? Let’s consider the case when you’ve got one master application with a couple of plugins installed within them.
What will happen after you update the master app? Its directory will be deleted and a fresh clone will be made.
Unfortunately, Composer will not notice that other vendors have been removed from its original location and will not try to download it again, leaving you with the master app being stripped of plugins. The opposite scenario (update the plugin inside the master application) will work fine on the other hand. This could be, or could not be a problem in your particular case. You have to judge it by yourself whether this will cause you any problems during the application’s life cycle.
There are some different approaches that can be taken on this matter to avoid problems, but it will be covered in another part (usage of PostScripts). For now, you should just be aware of this mechanism as this can cause you some confusion when you find that all the plugins are gone.
This is also the time when it’s worth remembering a very important factor – be consistent with your versioning.
As you have probably noticed, there are more and more things that need to be downloaded as dependencies for Composer, and there will be more of them. Therefore, it will definitely help you to keep good control over your current version of each package, so that you don’t get lost and become confused by all the commits that have been made.
Another significant perk of a well-maintained versioning scheme is the two-way verification that Composer offers.
Basically, in each composer.json file of each package you have, you can define the package’s own dependencies. A very useful example is to keep a minimal version of the master application in the plugin package. This way, when you change your code in the plugin and are aware of changing requirements towards the master application, you can store this information.
The next time users try to use a certain version of the plugin with an incompatible version of the master application, they will see an error message and the update or the install will not proceed. This combined with a sensible versioning scheme will prevent you from setting up an application with incompatible add-ons.
Of course, there are many dependencies that can be described, so it’s not only the application-plugin relationship that needs to be stored, but also the plugin-plugin relationship, and even php version that is required.
The main message of this paragraph is that keeping the well-maintained versions and the dependencies’ definition will help you in creating unbreakable constraints between certain versions of your code.
However, this really requires deep thinking over the topic in order not to go too hard into one direction, which could possibly make the development and maintenance stage a real nightmare.
Summary
The main purpose of this part was to introduce the customisation for paths where packages are installed and to encourage you to constantly maintain your repositories with composer packages, as it can give you a great boost. Combined with the previous article, you now know:
- How to make your application a Composer dependency
- To decide where to install specific packages with regard to their designated location (plugin app, theme, etc.)
- Why you should keep proper versions and dependency constraints up to date
Altogether this should be enough to deploy a full codebase according to your desire.
The next article will be devoted to using the pre-* and post-* scripts, which will help you get the most out of Composer and allow you to prepare and deploy your application to any environment with minimal effort – so stay tuned!