If you read the post written by Andru, „Web Development using the Virtual Machine (VM): why and how“ you know that here at Innobyte we use Virtual Machines for the development of any project along with some other cool tools / services like GitHub.
At the same time we have a staging environment for almost every project, so the clients can see the progress, suggest changes and so on.
This works great for us, especially for new projects.
But when we have projects that are already live and we just need to develop new features/ modules its a bit tricky.
At such a project we are working right now, a very big project with over 100 custom Magento modules.
Beside the new modules, we also develop various tools to automate stuff.
One of the biggest challenges is making sure that every tiny thing we develop would work out of the box on our VM, the staging environment and of course when we deploy it on the live servers.
Lets take for example one of the latest tools we had to develop: a little tool (php script run from CLI ) that would create 16 new categories and for each of those categories we have to copy products from 3 to 5 categories already in the shop.
The part in which we create the categories is easy. The main problem is matching categories IDs on our VMs, stage website and of course live one.
So … here comes the need for a development environment.
Out of the box Magento doesnt support this, like other software or frameworks ( Code Igniter, Symfony, etc).
You guessed it, we build our own.
Well… we need a multi-dimensional array with IDs from which we need to copy the products for each environment (Virtual Machine, Stage, Live)
After much debating with the Innobyte team, we decided to write a custom Magento module so we can use it in more modules. We also decided to hack the index.php a little so we can do some checks (if needed) before everything is loaded-up.
Part 1 – Hacking
In index.php we have the following code :
[php]/* START DEVELOPMENT ENVIRONMENT */
$allowEnvs = array( 'live', 'stage', 'dev' );
$defaultEnv = 'live';
if ( isset($_SERVER['DEV_ENV']) && in_array( trim($_SERVER['DEV_ENV']), $allowEnvs) ) {
$env = trim($_SERVER['DEV_ENV']);
}
else {
if ( PHP_SAPI == 'cli' ) {
$env = 'cli';
}
else {
$search = array(
'dev' => array( 'dev.', 'devel.', 'test.'),
'stage' => array( 'stage.', 'staging.'),
'live' => array( 'www.' )
);
$found = false;
$host = $_SERVER['HTTP_HOST'];
$parts = explode('.', $host);
foreach ( $parts as $part ) {
foreach ( $search as $dev => $bits ) {
if ( in_array($part.'.', $bits) ) {
$found = true;
$env = $dev;
}
}
}
if ( !$found ) {
$env = $defaultEnv;
}
}
}
define('DEV_ENV', $env );
/* END DEVELOPMENT ENVIRONMENT */
[/php]
As you can see, this is done before any other thing being loaded.
It first checks if there is a SetEnv directive in .htaccess. If there is any and it is in the allowed array (prevent typos) it skips to the section where we define our constant.
If not, the code checks the HTTP_HOST and sets the variable based on it.
If no match is found, it sets the default environment (live, in our case).
Part 2 – the Magento Module
We created a new module in the Innobyte namespace, called Base.
We are going to place the code here so we can reuse it everywhere we need to by simply calling a Helper.
Just create the file Innobyte_Base.xml in the app/etc/modules dir with the following contents:
[php]<?xml version="1.0"?>
<config>
<modules>
<Innobyte_Base>
<active>true</active>
<codePool>local</codePool>
</Innobyte_Base>
</modules>
</config>
This tells Magento to load our module.
Next, create the following directory structure:
app/code/local/Innobyte
app/code/local/Innobyte/Base
app/code/local/Innobyte/Base/Helper
app/code/local/Innobyte/Base/etc
Now lets create the configuration file for our module.
Fire-up your favourite IDE or text editor and create a new file in App/code/local/Innobyte/Base/etc called config.xml and paste the following :
[php] <?xml version="1.0"?><config>
<modules>
<Innobyte_Base>
<version>0.0.1</version>
</Innobyte_Base>
</modules>
<global>
<helpers>
<base>
<class>Innobyte_Base_Helper</class>
</base>
</helpers>
</global>
</config>
[/php]
Now that we have this set-up, its time to write our helper class.
We just have one method in it and it .
We send it an array with all values for all environments and it returns the one for the current environment. Pretty simple.
Just create a new file called Data.php in the app/code/local/Innobyte/Base dir and paste the code:
[php]<?php
class Innobyte_Base_Helper_Data extends Mage_Core_Helper_Data {
public function getValues ( $input ) {
$allowEnvs = array( 'live', 'stage', 'dev', 'cli' );
if ( PHP_SAPI === 'cli' ) {
define('DEV_ENV', 'cli');
}
if ( !defined( 'DEV_ENV' ) || !in_array( DEV_ENV, $allowEnvs ) ) {
return false;
}
else {
if ( !array_key_exists( DEV_ENV, $input ) ) {
return false;
}
return $input[ DEV_ENV ];
}
}
}
[/php]
After that it checks if the constant is defined it is one of the one that we need ( $allowEnvs array ). It then returns as the array we need based on the keys of the input array.
An input array would look like:
[php] $input = array (‘dev’ => array ( 1, 2, 3 ) // array of ids for dev env
‘stage’ => array( 1, 2, 5) // values for stage
‘live’ => array( 1, 7, 14) // values for live
);
[/php]
Assuming that we would use this on our dev environment, calling:
[php]print_r(Mage::helper('base')->getValues($input))[/php]would print out the first array ( 1, 2, 3 );
Its a very simple piece of code but it helps us a lot in our development process.
If we didnt use this we will have to modify our files every time before we deploy them on the stage or live environment , add them to git, commit , etc .
If there were any problems, we would have to modify that file again with the first values for our VM and do the whole process all over again.
For any of you reading this that dont want to hack your index.php file, you can take out that code and put it in a __construct() method of the helper class .