My Joomla Component development workflow
My first and currently only Joomla component development was for the SWA UK website. The component manages memberships, events, tickets, results and more and is a rewrite of a previous component for Joomla 1.
But in this post lets ignore what the component does and instead concentrate the development workflow that is used. After lots of research at the beginning of development I decided to ignore other methods of developing a component and this is what I ended up coming up with.
The basics
All previous PHP development that I have been involved in has involved GIT so for me GIT is an obvious choice (although there is no reason this method would not work with ever other source control system).
Joomla components are different to components that I have previously worked on. generally they are installed using a ZIP file with one file structure, and installing shifts certain parts of this file structure around. This mainly refers to the split between the regular site directory and the administrator directory. This is discussed in the Joomla docs as the difference between using an install package and manually placing the files.
My GIT repository layout can be seen below:
.gitignore .joomlaRoot README.md make.php copyIn.php copyOut.php src administrator site foo.xml tests administrator site
All Joomla related code is held in the src directory. Space is allowed for tests in the tests directory. And the root of the repository contains the basics along with 3 useful scripts.
make.php
As mentioned earlier components are generally installed using ZIP files. So to make that easy, I decided to make a tiny script to make the zips for me. This would allow me to quickly zip up my code and deploy it to the production site. The script looks like this:
$rootDir = __DIR__ . DIRECTORY_SEPARATOR; @unlink( $rootDir . 'com_swa.zip' ); zipRecurive( $rootDir . 'src', $rootDir . 'com_swa.zip' );
where the zipRecursive method looks like this:
function zipRecurive( $source, $destination ) { $source = realpath( $source ); if ( !extension_loaded( 'zip' ) || !file_exists( $source ) ) { return false; } $zip = new ZipArchive(); if ( !$zip->open( $destination, ZIPARCHIVE::CREATE ) ) { return false; } if ( is_dir( $source ) === true ) { $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $source ), RecursiveIteratorIterator::SELF_FIRST ); foreach ( $files as $file ) { $file = str_replace( '\\', '/', $file ); // Ignore "." and ".." folders if ( in_array( substr( $file, strrpos( $file, '/' ) + 1 ), array( '.', '..' ) ) ) { continue; } $file = realpath( $file ); if ( is_dir( $file ) === true ) { $name = rtrim( str_replace( $source . DIRECTORY_SEPARATOR, '', $file . DIRECTORY_SEPARATOR ), '\\' ); $zip->addEmptyDir( $name ); } else if ( is_file( $file ) === true ) { $name = str_replace( $source . DIRECTORY_SEPARATOR, '', $file ); $zip->addFromString( $name, file_get_contents( $file ) ); } } } else if ( is_file( $source ) === true ) { $zip->addFromString( basename( $source ), file_get_contents( $source ) ); } return $zip->close(); }
copyIn/Out.php
These scripts copy the source from the git repository to a local Joomla installation and vice versa. This allows development of the code in place inside a Joomla setup and easy committing to GIT as well as easily updating a test instance with new code from GIT without having to upload a ZIP file.
copyIn.php can be seen below:
$joomlaRoot = trim(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . '.joomlaRoot')); if ($joomlaRoot === false) { die('Please create a .joomlaRoot file.'); } recurse_copy($joomlaRoot . '/components/com_swa', __DIR__ . '/src/site'); recurse_copy($joomlaRoot . '/administrator/components/com_swa', __DIR__ . '/src/administrator'); #This file also needs to be moved rename(__DIR__ . '/src/administrator/swa.xml', __DIR__ . '/src/swa.xml');
and copyOut.php can be seen below:
$joomlaRoot = trim(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . '.joomlaRoot')); if ($joomlaRoot === false) { die('Please create a .joomlaRoot file.'); } recurse_copy( __DIR__ . '/src/site', $joomlaRoot . '/components/com_swa'); recurse_copy(__DIR__ . '/src/administrator', $joomlaRoot . '/administrator/components/com_swa'); #This file needs to go too! copy(__DIR__ . '/src/swa.xml', $joomlaRoot . '/administrator/components/com_swa/swa.xml');
where the recurse_copy function is as below:
/** * Taken from the doc page of copy * http://php.net/manual/en/function.copy.php#91010 */ function recurse_copy($src, $dst) { $dir = opendir($src); @mkdir($dst); while (false !== ( $file = readdir($dir))) { if (( $file != '.' ) && ( $file != '..' )) { if (is_dir($src . '/' . $file)) { recurse_copy($src . '/' . $file, $dst . '/' . $file); } else { copy($src . '/' . $file, $dst . '/' . $file); } } } closedir($dir); }
and the .joomlaRoot file simply contains a string to the root of your local Joomla installation.
Notes
Personally I love these small files and they make my life working with this Joomla component much simpler. Perhaps there is a generally preferred solution out there but with all of my searching nothing has caught my eye yet!
Of course if you make schema changes simply copying the files into Joomla will not update the schema. In these cases you’ll either have to make the schema changes yourself or make a ZIP and upload your component to be fully installed.
Also if you ever delete files, this will not be reflected in the copies. (This is probably something that could easily be fixed…)
The .joomlaRoot file of course means that this solution can be used by multiple people using the same git repository. .joomlaRoot is listed in the .gitignore file thus is never committed and each developer can specify their own setting.