ahDoctrineEasyEmbeddedRelationsPlugin and Composite Primary Keys

We’ve been working on a project in which a bunch of the tables had composite primary keys.  Often we wanted to embed these tables in other forms.  ahDoctrineEasyEmbeddedRelationsPlugin is a great plugin for managing embedded forms with Doctrine and Symfony.  It lets you easily let users add multiple new relations and delete previous ones.   However, it didn’t really support composite keys.   We also wanted to be able to expose the primary keys rather than unsetting them from the form(so that the relation is automatically declared).  This was a problem as we needed to be able to select at least one of the primary keys.  Here is an example where we wanted to let the user pick one of the primary keys:

User:
  columns:
     first_name:
        type: string(16)
Product:
   columns:
      name:
         type: string(32)
UserProduct:
   columns:
      user_id:
         type: integer
      product_id:
         type: integer
        price:
          type: decimal
    relations:
       User:
          local: user_id
          foreign: id
       Product:
         local: product_id
         foreign: id

In this case there are Products and Users. A Users can have a products and specify how much each costs. When you are adding products to a user, you would want to be able to select which product you are adding in the embedded form. This is where our problem was, with the plugin as of version 1.4.4 you couldn’t tell it not to unset the primary keys when it embedded the form. We did a checkout of the plugin from SVN and modified it a bit. It now has and additional parameter: newFormUnsetPrimaryKeys . This will make it so the plugin will not unset the primary keys on a new form if you set it to true (it defaults to false).

We also found that the plugin had hard coded a couple of places findOneById which required the primary key to be called `id`. We’ve updated this to use the Doctrine method getIdentifierColumnNames() to get the primary keys.

We only applied to be developers on this script, so are currently waiting on it to be packaged and released, however if you want our updated just do a svn checkout of the plugin and you will be all set!

Let us know if you have any questions on it!

Doctrine Multiple Connections with Symfony Web Debug Toolbar

In April I wrote about using Doctrine with multiple connections for specific table models.  This worked really well, except Symfony’s web debug toolbar would not show any SQL queries that were not defined in the databases.yml file.  This of course made it quite difficult to debug many queries, as the query logs show the queries before the parameters are inserted, for example:

SELECT a.id, a.title FROM posts a WHERE id = ?

The web debug toolbar however shows them with the parameters in place. This makes it a ton quicker to debug as you can see the parameters in place, as well as copy and paste the query straight into the SQL client to see the raw results. After a few months and a project becoming much more complex it was necessary to see the queries. I looked up how sfWebDebugPanelDoctrine gets the queries and found:

So the sfDatabaseManager would manage all the connections and return which ones to pull queries off of. I looked at it a bit and saw that it has setDatabase which sets the databases it has registered. Since it requires you give it a sfDatabase as a parameter I had to update the way we connect the databases a bit so we could pass them to it. The new version is below:

Now you will now see the queries in the web debug toolbar.

Retrieve session timeout in Symfony

We were recently working on an application that required users to enter a significant amount of complex data that often meant that they had to look things up in between saves. Users kept running into the problem that their sfGuard sessions would timeout before they were able to click “Save” on the form which in turn caused them to loose all of their hard work. Obviously, this is lame so we decided to add a popup warning users that their session had expired and prompting them to login again before saving their data.

We decided to implement this by using setTimeout in Javascript to pop up a window once the user’s session had expired.

Setting the session length for a Symfony user is easy enough, open up app/config/factories.yml and add the following:

all:
  user:
    class: myUser
    param:
      timeout: 1800 # this is the default but you can change it at will (its in seconds)

As it turns out, the tricky part is how do you access this value inside the application? Un-characteristically, I couldn’t find anything in the Symfony documentation about how to access these variables. For whatever reason, sfConfig::get() doesn’t provide access to the variables in factories.yml.

In order to get that timeout value I used (inside a template):

  $userOptions = $sf_user->getOptions(); 
  $timeout = $userOptions["timeout"];

Anyway, once I figured that out the rest is pretty straightforward.

After $timeout a Javascript function opens a jQuery UI Dialog box informing the user that their session has expired and presents the standard sfGuard sign in form. I override the onSubmit of this form to perform the request via AJAX in the background (so the user doesn’t loose their data) and then if the credentials are valid the dialog closes and the user can go on their way. If the credentials are invalid, the form re-populates with any errors and the user can correct them to re-login to the app.

Hope everyone had a good Thanksgiving!

FOSS Saturday: sfFbConnectGuardPlugin – sfGuard meets FB Connect

I was slaving over a hot keyboard all Friday!

But at last it is done – FBConnect for sfGuard.

Get it here http://www.symfony-project.org/plugins/sfFbConnectGuardPlugin

A detailed explanation of how to install it and use it is on the Symfony site.

Anyway, the plugin basically just introduces a new table to keep track of Facebook IDs <---> sfGuardUserIds

Here’s a fun nugget. One of the problems with using FB Connect is that you can’t mug a user’s email address from Facebook. Obviously this is a smart move on Facebook’s part but it makes life hard for my Nigerian spammer friends. If you want to snag a user’s email address (or anything else for that matter) while still using Facebook Connect here’s a sketch of how to do it.

Everything is the same except you can’t use Facebook’s FBML to render the FB Connect button. What you want to do instead is trigger the “connect” event by hand. Here is basically how we do it:

  1. The user requests to sign up.
  2. We pop up a Lightbox using Thickbox
  3. We ask the user for their email address and verify that is valid and unique via AJAX in the background.
  4. The validation routing sets an attribute on the user using setAttribute() that contains the entered email address.
  5. We close the Lightbox and initiate a Facebook Connect request with FB.Connect.requireSession
  6. In our createFbUser() method we get the attribute back and save it with the new user

Bam. Got the user’s email address and logged them in via FB Connect.

FOSS Fridays: sfSCMIgnoresTaskPlugin version 1.0.3 released – Doctrine Supported

In the past we have always used Propel as our main ORM for our Symfony projects. Recently with Doctrine becoming the default ORM for Symfony 1.3 we decided we should make sure our plugin supports Doctrine. For those who don’t know about the plugin, it automatically creates ignores for your Source Code Management(SCM) if you use Git or CVS. When you have a large project it gets tiring to create all the different ignores for all the bases, logs, configuration files and such.

Let us know if you find any problems with Doctrine support or have any additional suggestions for the plugin.

More information on the plugin can be found on the Symfony Plugins site: http://www.symfony-project.org/plugins/sfSCMIgnoresTaskPlugin

You can download the most recent pear package manually at: http://plugins.symfony-project.org/get/sfSCMIgnoresTaskPlugin/sfSCMIgnoresTaskPlugin-1.0.3.tgz

or you can install it via:

./symfony plugin:install sfSCMIgnoresTaskPlugin