Symfony World blog is not maintained anymore. Check new sys.exit() programming blog.

Default form values for new objects

what for?


Sometimes you may need to get some random values when creating a new object from the symfony generated form. This can happen in lots of situations, some of them are listed below:

  • populating article comments in a community website - a community website looks a lot better when each article has at least several comments. So when an article is created, few comments need to be created right afterwards. Choosing random names is quite easy, of course, but what if your system allows only logged in users to leave comments (which means, comment table row does not have author string value, but author_id integer foreign key)? In such case you have to look up a random user id in the database, which is pretty impossible. Default form values to the rescue!
  • E-shop product attributes - each product has some attributes that measure its quality, just like reliability, aesthetics, performance and so on. As we know, many information in advertisement is pure bull*hit nowadays, e.g. values of product attributes are probably fabricated. Suppose there are many attributes for each product (available inside the form or inside an embedded form, it doesn't matter). Defining each attribute costs time and an E-commerce shop employee will work slower if he needs to input each value separately. Again, default form values comes with assistance!

solution

Put the following code into the setup or configure method of a form class:

if ($this->isNew())
{
  $this->setDefault('name', Tools::getRandomName());
}

Above feature is really easy to implement and you'll find it really useful when generating some partially random data. Of course, you may also set random values after the form is submitted, when a form field is simply disabled - but then you don't have a posibility to change it during object creation.

Advanced SQL expressions in Doctrine

Scene from "Irma la Douce" by Billy Wilder (1963)

missing element in symfony documentation

Symfony and Doctrine book (chapter 6) describes DQL API, but one very important SQL feature is missing: advanced expressions. Particularly, you may often need to use advanced logical expressions in WHERE clause.


example

For example, we run a cron task searching for all active posts which does not meet all SEO requirements - then a warning mail is sent to a particular employee of a company to do something with it. So the query needs to look for Post objects:

Doctrine_Query::create()->from('Post p')
which are active:
->where('p.active = 1')
and have invalid SEO data at the same time, let's assume that SEO is invalid when at least one of all SEO data columns is empty (title, keywords and description):
->orWhere('LENGTH(p.meta_description) = 0')
->orWhere('LENGTH(p.meta_keywords) = 0')
->orWhere('LENGTH(p.meta_title) = 0') 


Now take a look at the above code - is that correct? Of course not! We want to generate the following query:
SELECT *
FROM Post p
WHERE p.active = 1
AND (
  LENGTH(p.meta_description) = 0 OR 
  LENGTH(p.meta_keywords) = 0 OR 
  LENGTH(p.meta_title) = 0)
Three SEO alternatives need to be enclosed in parenthesis. But Doctrine Query API does not provide specific methods doing that. Fortunately, we may use standard query methods (where, orWhere, andWhere, etc.). Unfortunately, we can use them only on the top-level of the query (meaning that all we write stays outside parenthesis). So the top-level of query is created by two arguments linked with AND logical operator - each of those arguments use one DQL API where method. But the second argument (invalid SEO) is internally ivided into an alternative of three subarguments, using OR logical operator. The final query looks like:
$objects = Doctrine_Query::create()
  ->from('Post p')
  ->where('p.active = 1')
  ->andWhere(
  'LENGTH(p.link_rewrite) = 0 OR '.
  'LENGTH(p.meta_description) = 0 OR '.
  'LENGTH(p.meta_keywords) = 0 OR ')
  ->execute();

Anyway, it works. Maybe the Doctrine API will be more friendly in Symfony 2.