Philipp Hoffmann

my octopress blog.

Upgrading Symfony 1.4 to Symfony 2 - My Migration Experiences

| Comments

I finally decided to upgrade from symfony 1.4 to Symfony 2. As I started migrating I figured that Symfony 2 is very different from symfony 1.4. Since I’m probably not the only one migrating to Symfony 2, I’m going to share my migration experiences with you in this blogpost. This is going to be a living post which means that I update it as soon as I think there is anything valueable to share. Please feel free to post any feedback or suggestions in the comments. A lot of stuff is already covered in the Symfony 2 book. I suggest to read at least the first few chapters before starting to migrate. I’m going to assume that you have an Apache webserver running and you are putting stuff in a directory called my_project in your webservers document root.

Installation

Everything (and even more) in this chapter is already covered in the Symfony 2 book. This is just the way I installed Symfony 2 which worked fine for me. Installing Symfony 2 is really easy (thanks to composer). Get the latest copy of composer and install the Symfony 2 framework standard edition:

1
php composer.phar create-project symfony/framework-standard-edition my_project/ 2.1.2

This will install Symfony 2.1.2 (which is the current stable version at the writing of this post) including all its dependencies. You don’t have to worry too much about the version as you can update this later easily. After the installation completed, setup write permissions for the webserver to the cache and log directories.

1
2
chmod -R 777 app/cache
chmod -R 777 app/logs

That’s it, you can check your installation by visiting the two preconfigured application controllers in your browser:

1
2
http://localhost/my_project/web/app_dev.php
http://localhost/my_project/web/app.php

Oh wait, the last one should throw a 404-error. That’s okay, because this is the controller for the production environment and no routes have been setup for production yet.

As in symfony 1.4 there is a console application for easy access to useful commands. In Symfony 2 it’s located at ./app/console. You should make this executable so you can use it from the command line:

1
chmod u+x app/console

Symfony 2 comes with a demo project (called Acme) pre installed. You can delete it to start off with a clean setup. Delete the following files and directories:

1
2
rm -Rf src/Acme
rm -Rf web/bundle/acmedemo

Then unregister the Acme demo bundle from the application kernel (more on this later) by removing the line

1
$bundles[] = new Acme\DemoBundle\AcmeDemoBundle();

from app/AppKernel.php

Then remove the following routes from the development environment routing configuration in app/config/routing_dev.yml: _Welcome, _demo_secured, _demo.

Great, so if you now visit the development frontend controller

1
http://localhost/my_project/web/app_dev.php

Symfony should throw a 404 (NotFoundHttpException) because there are no configured routes. Now we have a clean setup and you can start migrating your project.

It’s time for your inital commit (assuming that you’re using git to version control this project).

1
2
3
git init
git add .
git commit -m "inital commit"

There is already a .gitignore file included so no further steps are necessary to exclude any directories.

A few words on Composer

You should make yourself familiar with Composer as it makes dependency management in your project a lot easier. I just want to mention a few tasks you are probably going to use more or less regularly.

The vendor directory of your project is used by composer to install all packages your project depends on. Go check it out. You will see that the Symfony framework itself is just a composer package. Depedencies are defined in composer.json in the root directory of your project. You can add packages from Packagist in this file and have composer install them by running

1
../composer.phar update

from your project root. Composer will then get the matching versions of each package and install them in the vendor directory. This will also update all other packages defined in composer.json. Composer will save the current package setup in composer.lock. This is important for deployment. Installed packages are not included in the git repository (but the composer.lock file is). When deploying your project, you check out the repository on the production system and run

1
../composer.phar install

to tell composer to install the packages defined in composer.lock. You end up with the same package setup as the development system. Great, this is exactly what we want.

Please note that this is just a minimal overview. You probably have more deployment stages between development and production and a more complex deployment process going on. However, you just need to customize the above to your needs.

Next up:

(Not necessarily in this order)

  • Bundling
  • Layout and Tempate organization
  • Assets
  • Translations
  • Static partials
  • Controller partials
  • Using databases
  • Using models
  • Backend for CRUD-operations
  • Fixtures
  • Forms
  • File uploads

Symfony 2 - Get the CSRF-Token of a Form in a Twig Template

| Comments

You probably already know that you can get the value of each widget of a form using

1
form.vars.value.{widget_name}

in your Twig template (where {widget_name} is the name of the widget you are trying to get the value of). However, when trying to get the CSRF-Token this way, you will get an error that the variable _token does not exist. To access the CSRF-Token of a Symfony 2 form in Twig you have to use

1
form._token.vars.value

This way you can use it in JavaScripts for example (I actually used it to dynamically build post-delete-buttons).

Using Java Static Nested Helper Classes

| Comments

Helper classes are classes that summarize methods you need throughout your project that don’t actually provide any business logic. Since these methods usually don’t depend on any object they are implemented as static methods like this:

1
2
3
4
5
public class ProjectHelpers {
  public static String hashPassword(String password) {
    // do some kind of hashing
  }
}

This static helper method can be used anywhere in your project by a static call like this:

1
String myHashedPassword = ProjectHelpers.hashPassword(myPassword);

This is pretty simple but also very useful. However, sometimes your “helper-logic” is a bit more complex and you would wrap it into a separate class. You can then still use the idea of helpers and nest a separate class into your helper class like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ProjectHelpers {
  public static class StringAnalyzer {
    private analyzeString;

    public StringAnalyzer(String analyzeString) {
      this.analyzeString = analyzeString;
    }
    public int countVocals() {
      // count vocals
    }
    public int countConsonants {
      // count consonants
    }
  }
}

The class StringAnalyzer is called a “static nested class” or “static inner class”. To use this class, just use it like any other class but remember that it is a static member, so you have to use it like this:

1
ProjectHelpers.StringAnalyzer myAnalyzer = new ProjectHelpers.StringAnalyzer();

This is a great way to separate small helper methods and classes from your business logic into helper classes. Declaring the members being static, no instantiation of the helper class is necessary, it just acts like a repository for all the little helpers you need throughout your project. Simple, useful and clean.

A Bulletproof Pattern for Creating Doctrine Subqueries of Any Complexity

| Comments

Doctrine subqueries can be very frustrating. They sometimes work but as soon as you reach a certain complexity level, Doctrine just can’t handle things anymore. I will show you how to write subqueries in Doctrine which you can nest in as many levels as you want without Doctrine complaining about it and still using the DQL (Doctrine Query Language).

Notice that this post refers to Doctrine Version 1.2. Version 2 of Doctrine is already released but many developers still use symfony 1.3/1.4 which ships with Doctrine 1.2.

A lot of people suggest embedding subqueries into the where() function of Doctrine but this will only work until a certain level. At some point Doctrine gets confused parsing the subqueries. This results in queries that are not completely parsed and thus throw errors when executed. The solution is to explicitly tell Doctrine about the subquery. I am going to use an example query which is short enough to understand the solution pattern. Lets say I have two tables, one table for movies and and one for movies I already saw. In my query I want to check if the movie “Prometheus” (in a Movie-Table) is one of the movies I already saw (by checking if the name exists in a SeenMovie-Table). Lets have a look at the sample query:

1
2
3
4
5
6
7
// nest subquery into the where condition
$q = Doctrine_Query::create()
  ->from('Movie m')
  ->where('name = ?', 'Prometheus')
  ->where('EXISTS (SELECT * FROM SeenMovie sm WHERE m.name=sm.name)')
  ->execute()
;

We have a simple select with a nested subselect in the where condition. I know that this is not very complex and Doctrine can surely handle this. But it is easier to understand the principle using this simple example (and I like the movie “Prometheus” :-P). You can then easily use the pattern on more complex queries. So here is how to tell Doctrine about the nested subquery:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// build root query
$query = Doctrine_Query::create()
  ->from('Movie m')
  ->where('name = ?', 'Prometheus')
;

// build subquery
$subquery = $query->createSubquery()
  ->from('SeenMovie sm')
  ->where('m.name = sm.name')
;

// nest subquery and execute
$query->where('EXISTS (' . $subquery->getDql() . ')')->execute();

What I did is, I created the subquery separately from the main query by using $query->createSubquery(). A new subquery is created and linked to the main query. This replaces Doctrine_Query::create() when creating regular queries. Everything after $query->createSubquery() is exactly the same as if you were writing a regular query. So you can write your subquery just like any other query. You can even nest another subquery by continuing the pattern. Then you call $subquery->getDql() to let Doctrine “render” the subquery. After that you can embed it into the main query and execute it. The result is the same as before but you excplicitely told Doctrine about the subquery. This helped me in a much more complex situation and I’m sure it will help you too :-)

Browsers Automatically Submit Single Input Field Forms on Enter (and How to Fix That)

| Comments

Normally browsers will not submit forms when the enter key is pressed. You have to implement this kind of feature via JavaScript (and you probably did already at some point). However if your form only has ONE single input field most browsers will submit the form when the enter key is pressed. You don’t even need a submit button for this to happen. This is deeply rooted in the HTML 2.0 specification (you can read about this here). I guess it is supposed to be some kind of feature. I found it very frustrating because I didn’t want my form to be submitted when the enter key is press and I tried everything to prevent this from happening, from unbinding the key events to blocking the event from bubbling up. Nothing helped. It took me quiet a while until I figured out that this is because my form only has one input field. Apparently, there is only one way to solve this problem. Right, you guessed it, add another input field and hide it via CSS:

1
2
3
4
<form action="/search.php" method="get">
  <input type="text" name="keyword" />
  <input type="text" name="formsubmitfix" style="display: none;" />
</form>

This will make it work. But you end up with a messy url when submitting the form: /search.php?keyword=test&formsubmitfix;=. This is not a very satisfying solution. Making the input a hidden field will not work either:

1
2
3
4
<form action="/search.php" method="get">
  <input type="text" name="keyword" />
  <input type="hidden" name="formsubmitfix" />
</form>

Despite the fact that the resulting URL is still messy, it also doesnt prevent the form from being submitted when the enter key is pressed. What does help is the following:

1
2
3
4
<form action="/search.php" method="get">
  <input type="text" name="keyword" />
  <input type="text" style="display: none;" />
</form>

Hide the input field via CSS and don’t give it a name. This way the browser recognizes multiple input fields but the resulting URL does not contain the input field we added to fix the problem, voilá: /search.php?keyword=test Exactly what we wanted :-)

How to Discard the Query String in a RewriteRule (Apache, Mod_rewrite)

| Comments

Removing the query string in a rewrite rule of Apache’s module mod_rewrite is a bit tricky. Let’s say you want to redirect the url http://www.example.com?query=test to http://www.example.com/noqueries in Apache with mod_rewrite enabled. Rewriting the URL is no problem but Apache always appends the original query string (?query=test) to the resulting URL which we don’t want in this example. If you’re using Apache 2.4 or later you can use the QSD option (qsdiscard) to remove the query string like this:

1
2
RewriteCond %{QUERY_STRING} ^query=test$
RewriteRule ^/$ /noqueries [QSD,R=301,L]

However if you’re using an earlier version of Apache (like 2.2) this solution won’t work. We have to use a little trick to make this happen:

1
2
RewriteCond %{QUERY_STRING} ^query=test$
RewriteRule ^/$ /noqueries? [R=301,L]

Notice that instead of setting the QSD option we added the question mark to the result URL. This generates the URL we wished for (http://www.example.com/noqueries) adding a new (empty) query string. The original query string is completely discarded. Have fun url-rewriting ;-)

Flexible Domain Mapping to Symfony Apps

| Comments

Here is a quick tip for your symfony 1.3/1.4 project (this probably works with other versions as well) hosted on an Apache webserver: So you fleshed out your symfony project and you ended up with several apps, all being available under their controller name frontend.php, anotherfrontend.php etc. For your routing (and maybe SEO purposes) its a bit unsatisfying that only one app in the project can be available without adressing its front controller (using the no_script_name setting in settings.yml). However, if you do have multiple domains, you can map each domain to a symfony app in your project very flexibly by modifying .htaccess in your symfony web folder. The stock file should look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Options +FollowSymLinks +ExecCGI

<IfModule mod_rewrite.c>
RewriteEngine On

# uncomment the following line, if you are having trouble
# getting no_script_name to work
#RewriteBase /

# we skip all files with .something
#RewriteCond %{REQUEST_URI} \..+$
#RewriteCond %{REQUEST_URI} !\.html$
#RewriteRule .* - [L]

# we check if the .html version is here (caching)
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f

# no, so we redirect to our front web controller
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>

For each domain you want to redirect to another app you then insert the following lines into the rewrite rules:

1
2
RewriteCond %{HTTP_HOST} ^(www\.)?example.com$
RewriteRule ^$ exampleapp.php [L]

This will redirect the domain www.anotherdomain.com to the app controller exampleapp.php. Of course these are just examples. You need to modify this for your issues. You could, for instance use regular expressions to cover multiple domains or you could redirect the domain to a certain route instead to the front controller. You can also use this pattern for further domain routings by just inserting more of these rules. Your final result look something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Options +FollowSymLinks +ExecCGI

<IfModule mod_rewrite.c>
RewriteEngine On

# uncomment the following line, if you are having trouble
# getting no_script_name to work
#RewriteBase /

RewriteCond %{HTTP_HOST} ^(www\.)?example.com$
RewriteRule ^$ exampleapp.php [L]

# we skip all files with .something
#RewriteCond %{REQUEST_URI} \..+$
#RewriteCond %{REQUEST_URI} !\.html$
#RewriteRule .* - [L]

# we check if the .html version is here (caching)
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f

# no, so we redirect to our front web controller
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>

This is my solution for routing multiple domains to symfony apps, I hope you like it :-)

How to View Any Git Repository File Without Checking It Out

| Comments

Here is how to have a quick look into any file in your git repository without checking it out or overwriting your working copy.

1
git show master:path/to/file/file.xml

This will show file.xml of the master branch. Git will echo the contents of the file to the standard output. So to actually save a temporary copy of the file for inspection you can just redirect the output into a new file:

1
git show test:path/to/file/file.xml > temp_file.xml

or pipe it into your favorite editor like this

1
git show test:path/to/file/file.xml | vim -

You can even look into files in the origin repository:

1
git show origin:path/to/file/file.xml

There is a lot more you can do with git show like showing diffs. Check out git help show for more information.