Picture for blog entry A setup script for Symfony 2 projects

A setup script for Symfony 2 projects

Ever get tired of typing all those Symfony commands every time you change something in your entities? How about creating a script to handle some repetitive tasks? This article will show you how.

If you used Linux for some time now, you're probably no stranger to scripting, and you won't learn anything new from this post. If, however, you're a Linux and/or Symfony newbie it might save you considerable amounts of time in the long run. This article assumes the use of the following:

Application development phase

Handling database updates

Let's assume that you changed something in your entity structure, and thus need to update the database structure. What you'd normally do is:

    # drop the database
    php app/console doctrine:database:drop --force

    # recreate the database
	php app/console doctrine:database:create

    # create the database tables for your entities and relations
	php app/console doctrine:schema:create

    # load sample content from fixtures
    php app/console doctrine:fixtures:load --append

    #clear the application cache
    php app/console cache:clear

Now let's say that you have two separate database connections: one for the dev and prod environments and one for the test environment. It's really a good thing to do, because that way you could run the tests and it wouldn't impact your regular dev environment users or test data. On the other hand, having two separate database connections means you'd have to run the above code twice. That usually takes some time. Not much, but it quickly adds up and by the end of the project you'd have spent several hours doing just that. What a waste.

This is where scripting comes in handy. As all the above commands do nothing fancy, and are constant throughout the whole application life cycle, you might put them together in a simple bash script. Put the below code in a single file. I usually put it in the bin directory of the project and name it setup.

    #!/bin/bash
    php app/console doctrine:database:drop --force
	php app/console doctrine:database:create
	php app/console doctrine:schema:create
    php app/console doctrine:fixtures:load --append
    php app/console cache:clear
    php app/console doctrine:database:drop --force --env=test
	php app/console doctrine:database:create --env=test
	php app/console doctrine:schema:create --env=test
    php app/console doctrine:fixtures:load --append --env=test
    php app/console cache:clear --env=test

Add execute permissions to the script and run it with:

    chmod u+x bin/setup     <- you have to run this only once
    ./bin/setup             <- this one runs the script

Done. You have just refreshed your database structure for both the dev/prod and the test environments.

Handling uploaded test files

Often you need a directory to store user uploads. In dev environments this directory tends to become a mess pretty quickly if you don't handle it properly. Each time you clear your database and/or load the fixtures, you should ensure that the contents of this directory reflect the contents of the database. I managed to waste quite some time looking for a non-existent bug, just because I forgot to remove old test entries from this directory.
Let's say that you have two server-writeable directories: one to store the user uploads and one to hold the rss feeds that the site generates. Let's say these are web/uploads and web/feeds respectively.
Add the following to your bin/setup script.

    echo "Check if web/uploads exists"
    if [[ ! -d ./web/uploads ]] ; then
        echo "Creating web/uploads"
        mkdir web/uploads
    else
        echo "web/uploads exists"
        echo "Deleting contents of web/uploads"
        rm -rf web/uploads/*
    fi;

    echo "Adding server write permissions to web/uploads"
    chmod o+w web/uploads

    echo "Check if web/feeds exists"
    if [[ ! -d ./web/feeds ]] ; then
        echo "Creating web/feeds"
        mkdir web/feeds
    else
        echo "web/feeds exists"
        echo "Deleting contents of web/feeds"
        rm -rf web/feeds/*
    fi;

    echo "Adding server write permissions to web/feeds"
    chmod o+w web/feeds

Now, each time you run the setup script the contents of the aforementioned directories will be removed.

Adding some interactive features

Sometimes you want to run only some tasks from these mentioned before. For example: you changed a test scenario and would like to reset the test database and run behat, but you don't want to delete the contents in the dev database for some reason.
At some point I just got fed up with creating tons of small scripts to do simple tasks, and wrote a slightly bigger one to handle most of our daily development needs. One script to rule them all.
See this gist for the full script code. Put the code in bin/setup.
Usage examples:

  • ./bin/setup -h - display available options
  • ./bin/setup - run in interactive mode
  • ./bin/setup -cT - recreate the database, clear the cache and run all tests in verbose mode (output saved to behat_data.html)
  • ./bin/setup -cdT - same as above, but only for test environment
  • ./bin/setup -n - interactive mode without dropping the database (useful when your MySQL user doesn't have DROP privileges on the database)

Production server updates made easy

In the early production phase of the applications life cycle there is still a lot going on. The client often wants some minor changes to be made to the look and feel of the final product. This can be a pain for your front-end team. Each time a button is moved 5 px you have to ssh to the server, find the project root in the servers filesystem, update the code from the remote repository, install all assets and, finally, clear the prod environment cache to make the changes visible. And then the client tells you: "Nah. On second thought, it was better the first time.". It happens a lot and repeating all those steps can be really dull after a while. That's why I always create simple update scripts on our production servers. The sole purpouse of these scripts is to introduce such small changes to the production changes without having to type all those commands again and again.

Let's say that your username is user and you have a Symfony app in the /home/user/exampleApp directory of the example-host.com server. Put the following code in a updateServer file in your home directory:

    #!/bin/bash
    cd /home/user/exampleApp
    echo "[LIVE Server] Update code from git repository"
    git pull origin master > /dev/null 2>&1
    echo "[LIVE Server] Update assets"
    php /home/user/exampleApp/app/console assets:install > /dev/null
    php /home/user/exampleApp/app/console assetic:dump > /dev/null
    echo "[LIVE Server] Clear prod cache"
    php /home/user/exampleApp/app/console cache:clear --env=prod > /dev/null

Add execute permissions to the script by running:

    chmod u+x /home/user/updateServer

Now you can update the server easily, by running the following command on any machine with an ssh client

    ssh user@example-host.com /home/user/updateServer

Some ssh command managers are available for mobile devices. I use ServerAssistant to manage several servers from my Android mobile phone in just a few clicks. It really speeds up the process of updating your production environment.

Previous post Next post