Distributing your project with symfony embedded

I was a bit surprised earlier that freezing your project was not really advised anymore, it was the way I was used to working with my symfony 1.0 projects. However, with symfony 1.1 and some pointers I have found the new way of doing things to be very pleasant. So let me share it with you.

There are several reasons for embedding symfony inside your project. On your development environment, a reason can be to easily enable symfony 1.0 and 1.1 projects to be developed side by side. On your production server, it can be because you have no control over centrally installed libraries. But moreover because the right setup can ensure you always have the latest version. Let me elaborate on how I do this.

First of all, my central installation of symfony. It is a complete checkout of the symfony svn repository, which means I have the symfony 1.0, 1.1 and even 1.2 branch locally checked out for usage. However, I hardly use these. I only use them to create a new project. After that, it's all up to Subversion.

Creating a new project

When I create a new project, I use the centrally installed symfony checkout:

/path/to/symfony/repo/branches/1.1/bin/symfony generate:project newproject

This will create my new project in the current directory. This is just the project skeleton, and it is still using the symfony that can be found in /path/to/symfony/repo/branches/1.1

But first... Subversion

My initial new project needs to be put into Subversion first though. I import my new project and create a fresh checkout to work with. This means I can start using some nice advanced features of Subversion for embedding symfony into my project.

The vendor directory

The location for our symfony installation will eventually be lib/vendor/symfony in our project. For this to happen, the vendor/ directory needs to be created and added to Subversion. So I do:

mkdir lib/vendor
svn add lib/vendor
svn commit lib/vendor -m "added vendor directory"

Now, my vendor directory is ready for adding symfony.


It is of course possible now to put a copy of symfony in the lib/vendor/symfony directory. And this works fine if you want it. The downside of this approach is that any symfony upgrade needs to be done manually, which can be a pain. 

There are also strategies which keep a copy of the libraries inside the repository by using "vendor branches". This is an option as well, yet it still keeps the library code locally where there should be no need for such a thing.

Instead, in my setup, I use an svn:externals property to link the lib/vendor/symfony directory directly to the symfony repository. The advantage with this is that every time I do an svn up, I also get updates from the symfony repository, which may contain fixes for bugs that I could encounter. So, let's set up this external link in our project repository. 

svn propedit svn:externals lib/vendor

This opens a new editor window of my favorite editor for such tasks (in my case vim, could be different for you). In this editor, I add a line:

symfony http://svn.symfony-project.com/branches/1.1

What this line says is that a directory named symfony [inside the lib/vendor directory, as specified in the svn propedit command above] needs to contain the contents of the 1.1 branch out of the symfony svn repository. In my case, I am using the branch, but if you want more stability, you could link to a release tag, for instance the current 1.1.4 release tag.This will give you the stability of a release, but not the fixes of recent commits. However, when a new release comes out, you can always simply use the propedit command again to switch to the next tag.

Once I've created the svn:externals link to the external repository, I can simply do:

svn up lib/vendor

To get the latest version of symfony in my project. Do not forget to commit lib/vendor to get your svn:externals change propagated in your subversion repository.

Switch to your new checkout

This is all nice, but symfony is at this point still using the symfony installation I used to create the project. I need to point my project to it's internal symfony checkout. Luckily, with symfony 1.1, this is very easy as it requires only a single change. This change needs to be made in our project configuration, which can be found in config/ProjectConfiguration.class.php. In this file, there is a require line that currently contains the hardcoded path to the location of the symfony installation that you used to create this project. We will need to change this to a relative location inside our project:

require_once dirname(__FILE__) . '/../lib/vendor/symfony/lib/autoload/sfCoreAutoload.class.php';

I am using a relative path to the ProjectConfiguration.class.php file here. I could hardcode this path to our project installation of symfony, however, there is a good chance that the server path to your project will not be the same on your test, staging or production machines. Therefore, making the path relative will ensure that your project runs on any of these machines without altering the configuration file.

The default styles and javascripts

Now there is one more thing left. Your default assets. So far I have not created my virtualhost configuration yet, but I need to ensure that this works. I could create a symlink in the web/ directory to lib/vendor/symfony/data/web/sf but those don't work well with Subversion (or other platforms). Best is to use the Alias option in your virtualhost configuration. Instead of having this point to my centralized symfony installation (as I used to do), I can now simply point it to the symfony in my project:

Alias /sf /var/www/mynewproject/lib/vendor/symfony/data/web/sf

And we're done. The only thing you need to do when you deploy this to another server (and this should only happen once per server) is that the last step, the virtualhost configuration, is done for that specific server setup. Once that is done, you're set.

As with many of symfony's features, this process is aimed at not duplicating the tasks you need to execute in your project. You only set this up once (in your development environment), and once that is done, you can deploy to as many systems as you want without having to do the same steps all over again.