symfonynerds.com

Nerds who love the symfony-project

Archive for December, 2009

Worker classes in Symfony

I’ve been thinking about the fact that the engineers at Google view static classes as terrible things. My own code has an unfortunate number of static classes. Mostly this is because I am not sure where to put some of the code. For instance, on every project I have a few methods that format output. Some of these methods are the same on every project, so I hate to put them in the action code. Where else? Output formatting obviously does not belong in the model classes. What about methods for formatting dates, methods which I use in action code in multiple modules? It is easiest to put this into a class of static methods. Then I can call it from everywhere.

Another approach would be to add methods to the parent class. The parent of all action classes is sfActions. I’ve thought about editing sfActions, but I hate the idea of editing a core class of Symfony. I’ve also thought about creating a child class of sfActions, adding my methods there, and then having all of my action classes inherit from that. This is probably the best way to go, but I admit to a certain laziness. At some point I plan to write my own custom generation classes, which would make this easier. For now I tend to rely on the auto-generate code. Also, there are some good arguments against tall object trees. In his book Effective Java, Joshua Bloch suggests “Favor composition over inheritance.”

Given these concerns, I was interested to read this article about worker classes in Symfony. They list these advantages:

Advantages of worker classes

1. actions could be shrinked huge in size

2. worker classes are more decoupled from the rest of symfony and could be tested better

3. you can replace the content of an worker method without breaking actions

This is a step away from the problems of static classes. It provides a place in the object hierarchy where this code might legitimately belong. The engineers at Google suggested that most static methods should really belong to one of their own parameters. I can imagine worker classes as a way of making this happening, without bloating up the owning class.

A follow-up article looks at the interface changes that worker classes allow. These changes should allow better long-term maintainability and better testing. I especially like the idea of removing all ORM specific code from my action classes.

The action (or template) does not process anymore with a concrete ORM model. With the help of the proxy class, we can change the underlying model layer without breaking anything in the action. By providing a well defined interface, the proxy class manages how a model could be modified and accessed.

At this point, you should understand the reason for all those changes. It may look like a huge overhead for just retrieving some data out of the model. But in our case it was a precondition for our global architecture.

Our models and our main business logic is partly served by and processed in SOA applications (there we use Propel and i hope in some near days also Doctrine). I implemented a mechanism, which allows worker classes to access those SOA applications very easily. They are fetching the serialized data from those applications, hydrate them into proxy classes and provide them to the rest of the project.

…In this way, action code is really short, it is completely decoupled from the logic how the model is originally handled and it works quite well. If we decide to change something in our architecture some day, we only have to change our worker and proxy classes. The rest of the project will not notice those changes, as it should be in a layered architecture.

Sure, this step is nothing for small applications. But when your project grows and grows, the number of requests in a second turns from dozens to hundreds, then you’ll be thankful, if your project provides this approach.

I will probably be adopting some of this technique for my own current project.

Darren Hoyt and I rolled out a new website yesterday. WP Questions is a place for WordPress developers to go when they have questions so urgent that they are willing to pay money to get an answer. We built the site using Symfony, and I’d like to use this site to talk about the situations where I think Symfony makes a great choice as a development platform.

First, understand our goals. We needed to create a site that allowed questions to be posted, money to be taken, answers to be given, and money dispersed to the person who had offered the most useful answer. Darren Hoyt did a good job of summarizing the goals of the site.

Right now we are listening to feedback and fine-tuning the site. Once we feel we have fixed all the bugs and added all the needed features, we plan to roll out an identical site for each of the major technologies: Java, Ruby, Rails, Symfony, Cake, Drupal, .NET Oracle, SQL, Javascript, etc.

So what do we need in our development platform?

1.) We can not guess the future of our data storage so we need database abstraction that keeps SQL out of our code.

2.) We know the structure of our data will evolve, so we need an ORM that can mask the changes.

3.) We know the designs will evolve and multiply even as the data schema evolves, so we need a clean MVC architecture that allows us to correctly observe a separation of concerns.

4.) We know that if we are successful we will eventually need to hire other programmers, so we need a well-documented framework that other programmers know, preferably one that is open source.

5.) We need to maintain the code base even as we scale up to running 100 sites, so we need a framework that allows us to run 100 sites off a single installation.

So what should we use?

A few months ago it was suggested that we use WordPress to develop the site. I think this is clearly a bad idea. WordPress has a fairly rigid database structure. A developer can add new database tables to any WordPress installations, but establishing relationships between the new tables and the core tables is always a bit of a hack. And WordPress engages in some practices that I regard as extremely questionable, such as storing config data in the database and using fields in the database to store formatted text that needs to be drawn out and parsed for data - data storage inside of data storage.

We could have also started with forum software like phpBB and then hacked it to add in the other features that we wanted. But again, it would be an uphill struggle against a database structure that makes too many assumptions about what we need, and which offers us few tools to evolve the structure over time. And, personally, I find their template system a pain to edit.

Last month I complained about the prominence of Drupal. The comments in that thread show that Drupal has some very strong supporters. However, Drupal started life as a CMS, and still shows some traces of that, in terms of assumptions about database structure. Also, its files live within the directory of the server that is made available to the public on the web, something I regard as a security risk. Karl made a good point in a comment in that thread:

Interesting here is that I looked at drupal, but choose not to use it, instead, Symfony was the choice. The two biggest reasons was 3rd party integration with quickbooks and scalability, we were afraid that drupals datamodel would cause us headaches at the end.

I like the way that James Herrmann summed things up at the end:

What are the questions asked that lead to the answer of ‘Symfony’, ‘Cake’, or ‘Solar’? I have a feeling they would entail infinite extensibility, unit testing, bridging with other libraries, optimization and security, to name a few.

Clearly, using a CMS is a bad idea when you know you’ll need a maximum of flexibility in the future. But why use Symfony? Why not use one of the other frameworks? Grails offers a clean architecture, a great plugin system, a highly productive dynamic environment. Groovy, and the JVM, is somewhat faster than PHP, so it offers a speed boost which might be important when we need to scale up. I’ve written enough Groovy code that I could probably write the app in Grails if I wanted to. So why not? My reasons here have to do with the overall eco-systems that surround the 2 languages. There is an abundant supply of PHP programmers. More importantly, the sysadmins I know are comfortable with Linux, Apache and PHP, but not with Tomcat. In the bad old days, the whole world of Java was geared toward Fortune 500 companies with big budgets. The frameworks were as verbose as the underlying language. The idea of dynamic languages and lightweight frameworks is relatively new to the world of the JVM. PHP, by contrast, has been stable for 10 years now, and it has a huge following.

There are, of course, a hundred other technologies I might have considered, but I am limited to the ones I know well.

Symfony is going to allow us to grow and evolve the database structure while keeping the code clean. I’ll give you an example. On our Top Winners page we offer a few ways of sorting. The page shows the users, their name, their image and how much they’ve won. I must draw the data out of these 2 tables:

sf_guard_user_profile:
_attributes: { phpName: sfGuardUserProfile }
id: ~
user_id: { type: integer, foreignTable: sf_guard_user, foreignReference: id, required: true }
status: { type: varchar(255), required: false }
first_name: { type: varchar(255), required: false }
last_name: { type: varchar(255), required: false }
email: { type: varchar(255), required: false }
url: { type: varchar(255), required: false }
biography: { type: longvarchar, required: false }
created_at: ~
updated_at: ~
cash_total: { type: float, required: false }
image: { type: VARCHAR, size:255 }
secret_signup_key: { type: VARCHAR, size:255 }

answer:
id: ~
user_id: { type: integer, foreignTable: sf_guard_user, foreignReference: id, required: false }
question_id: { type: integer, foreignTable: question, foreignReference: id, required: false }
image: { type: varchar(255), required: false }
description: { type: longvarchar, required: false }
status: { type: varchar(255), required: false }
score: { type: integer, required: false }
prize_amount_paid_for_this_answer: { type: float, required: false }
created_at: ~
updated_at: ~

I need to create a JOIN. There is no foreign key relationships between these 2 tables, so the automatically generated Propel classes do not have any JOIN methods. I prefer to handle this by creating a database view,

CREATE VIEW answers_totals_users AS
SELECT answer.*,
COUNT(question_id) as questionTotal,
SUM(prize_amount_paid_for_this_answer) as prizeTotal,
sf_guard_user_profile.first_name, sf_guard_user_profile.last_name,
sf_guard_user_profile.image AS userImage
FROM answer, sf_guard_user_profile
WHERE answer.user_id=sf_guard_user_profile.user_id
GROUP BY answer.user_id

and then I add it to schema.yml as if it was a real database table:

answers_totals_users:
id: ~
user_id: { type: integer, foreignTable: sf_guard_user, foreignReference: id, required: false }
question_id: { type: integer, foreignTable: question, foreignReference: id, required: false }
image: { type: varchar(255), required: false }
description: { type: longvarchar, required: false }
status: { type: varchar(255), required: false }
score: { type: integer, required: false }
prize_amount_paid_for_this_answer: { type: float, required: false }
prizeTotal: { type: float, required: false }
questionTotal: { type: integer, required: false }
created_at: ~
updated_at: ~
first_name: { type: varchar(255), required: false }
last_name: { type: varchar(255), required: false }
userImage: { type: VARCHAR, size:255 }

Then I run the “symfony propel:build-model” command, and now I have a model class that selects against this JOIN as if it was a single table. This leaves my action code looking simple and clean:

$limit = $request->getParameter(’limit’);
if (!$limit) $limit = 25;
$c = new Criteria();
$c->setLimit($limit);
$c->add(AnswersTotalsUsersPeer::PRIZETOTAL, 0, Criteria::GREATER_THAN);
$c->addDescendingOrderByColumn(AnswersTotalsUsersPeer::PRIZETOTAL);
$this->listOfTopWinners = AnswersTotalsUsersPeer::doSelect($c);

I started to think of a site such as this one some years ago, when I first used Experts Exchange. I was frustrated that Experts Exchange took my money, yet none of the money went to the people who were helping me. I wanted a site where I could give money directly to the people answering my questions. My ideas about this site have become more clear over time, and no doubt 6 months from now I’ll have even more clarity about how this site/software can be useful to people. Likewise, as we get more feedback from our users we will continue to add new features, and offer more information. They’ll be a need for more JOINs and sometimes new database tables. A framework like Symfony assumes a changing database structure, it is set up to help a developer deal with those changes, and in this it is different from the CMSs that assume they know what kind of site you are building. Symfony, therefore, offers an attractive set of features for developing our site/software over the long term. It is the ideal development platform for us.

  • 6 Comments
  • Filed under: Symfony MVC
  • 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.

    Are you in New York City?

    I’m gathering together some web and tech and design people to go out for drinks on December 9th. Give me a shout if you are in New York City and would like an invite. Be sure to include your website or Twitter address, which I’ll pass along to all other invitees.

    Contact me at: la @ teamlalala.com

  • 0 Comments
  • Filed under: community