symfonynerds.com

Nerds who love the symfony-project

Archive for the ‘controller’ Category

This tip is a quick one for newbies. The Symfony book has some great tips as to when you would want to use a Symfony forward, vs a Symfony redirect. This quick post will show you the difference between the two and give you some guidelines to know when to use redirect or forward.

So what’s the main difference?

Symfony Redirect: This simply does a HTTP header(”location: “)
Symfony Forward:
This is complete custom code that comes with the Symfony framework that forwards you within the Symfony application.

Use Symfony Forward if:

  • If the action needs to forward a call to another action
    This will mean you can keep all the action class’ objects in scope and still have access to them
  • You want to forward the user to an action within the application, and keep it transparent to the user.
    From a users perspective - the displayed URL stays the same.

Use a Symfony Redirect if:

  • You need to go to an external website (http://google.com for example)
  • You don’t want the user to re-submit a form (if you are doing a form POST)
    It is generally good practice to do a redirect after a POST to ensure the user does not re-submit the form.

Conditional Forwarding/redirect
Quite often you need to forward or redirect if a condition has been met. Be sure to checkout the other Symfony functions that help you do this (forwardIf(), forwardUnless(), forward404If(), forward404Unless(), redirectIf(), and redirectUnless())

What about AJAX calls?
You can check if a HTTP request is an AJAX call before deciding what to do with it by calling calling  $request->isXmlHttpRequest() in your actions class. Once you have done this you can choose what you want to do; redirect or forward.

We came across an interesting problem today when setting up an application we’d built earlier in a fresh dev environment to add some features…

Because we’re big fans of MySQL Workbench we made our initial schema changes there, generated the SQL to update the DB, then used a symfony propel:build-schema to generate an updated schema.yml followed by a symfony propel:build-model to update the model…

All good so far, until I began work on the first change and tried to fire off the task I’d just written…

Fatal error: Class 'BaseSMMConfig' not found in /usr/local/projects/smm/lib/model/SMMConfig.php on line 3

After some looking around, I realised my base class was called BaseSmmConfig - spot the problem? Yes, the ‘mm’ was lower case, while my class was looking for ‘MM’ in capitals…

So what caused this?

The application had originally been created by writing the schema.yml file (including the phpName being specified) then doing a symfony propel:build-all to create the DB and the model together. This is fine as the capitalisation used in the hand written schema.yml flowed through to the model. When I had done the reverse, creating a schema.yml from the DB, all it has to go on is the names of the tables, using camel case to identify where an underscore exists in the table name, so the ’smm_config’ table becomes SmmConfig in the schema.yml and model, not SMMConfig as originally typed. Because your extendable model classes don’t get updated (so as not to overwrite your changes) your app then breaks down.

So the lesson:

  • Always observe strict camel case if you specify phpName
  • Don’t specify it, let it be generated
  • Work backwards by creating your DB first - using Workbench makes this an attractive option, saves you drawing up the schema later…

In previous Symfony versions it was quite difficult and painful to link your applications together (eg frontend and backend).

Yesterday the Symfony crew posted a tutorial on how easy it is in Symfony 1.2. In essence, its a simple config change and a change to your sfApplicationConfiguration class.

However, this method does still require an absolute hard-code of your application URL within your class (or you could put it in a config setting in your app.yml and read it off there). Even though it required some hard-coding, it still is much simpler and easier to use than Symfony 1.0 and 1.1 methods.

For more information - check out the Symfony blog post.

Symfony nerds tip: To avoid hard-coding as much as possible, you could in your app.yml define your application URL’s for each environment (eg. DEV, TEST and PROD). Once you have, in your application configuration class (which extends sfApplicationConfiguration), you could read the config variable for your application URL, but automate that via sfConfig::get(’sf_environment’). And depending on what environment you are in, you will select the application URL that corresponds to that environment.

I am working on an interesting project where I have been given the task of creating a web interface that allows an authenticated and authorised user to conduct specific types of search on a huge data set.

The data set begins as flat text logs which are transformed and loaded into a normalised schema - which was designed well before I got involved.  So, I have been very impressed by the way that propel and creole have consistently reverse engineered the schema and built a reliable model.

This is not a post about Propel or Creole but I thought to put this into context since I have been given a development specification that requires me to present data on a browser in a manner different to the way in which it is ordered and layed out in the database schema.

So - consider a table that has the following structure:

id (auto increment unique id)
my_date (of type date)
my_date_as_int (a conversion of date to int eg 2008-08-06 would be 20080806)

The insertion of dates into the above fields is not ordered - so id 1 may have an older date than id 2 and so on…for example:

id my_date_as_int my_date
1 20080602 2008-06-02
2 20080724 2008-07-24
3 20080723 2008-07-23
4 20080411 2008-04-11
5 20080412 2008-04-12

My task was to create a “drop down” select tag that showed the above sorted in an ascending order by the date field.

Step 1.  Create a __toString method to return the date field as opposed to the Id.

public function __toString()
{
return $this->getMyDate();
}


Step 2.  Create a custom method in the relevant Peer class to get a sorted list:

public static function getSortedDate()
{
$c = new Criteria();
$c->addAscendingOrderByColumn(DatePeer::MY_DATE_AS_INT);
$rs = DatePeer::doSelect($c);
return $rs;
}

As you can see, I choose to sort by the integer representation of the date field, but you can use the MY_DATE field in the same manner.

Step 3.  Create a drop down select_tag in your view.

There are two options here.

Option 1.  Standard form name, value pairs being the date and id fields respectivey.

<?php echo object_select_tag('', 'getMyDate', array (
  'related_class' => 'Date',
  'peer_method' => 'getSortedDate',
  'include_blank' => true,
)) ?>


Option 2.  Form name, value pairs being the my_date and the my_date_as_int fields respectivey.

<?php
  echo select_tag('', 
   objects_for_select(DatePeer::getSortedDate(), 'getMyDateAsInt','getMyDate'),
   array('id'=>"this_date",'name'=>"this_date"))
?>

That is it! The view then renders a drop down select that is constructed firstly by a call to the getSortedDate method, which in turn returns an array of sorted items - each of which is shown in the drop down using the __toString method to convert from an id to the date.