A setup script for Symfony 2 projectsEver 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:
- Doctrine ORM
- Behat for automated functional testing
- Doctrine Fixtures Bundle for inserting sample content
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.
- ./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 firstname.lastname@example.org /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.