How to kill Symfony’s Forms and live well

I hate the sfFormDoctrine. I don’t make a mystery of it.

If you are reading this article, it’s probably because you also have been searching for information on how to make them work, mostly in contests a bit more articulated, like in the case of embedding.
The good news is that you’ve found the definitive solution.
The bad news is that you have been gasping for months in a glass of water and wasted precious time reading usless chapters of Symfony‘s manual about Forms.

Very simply: the problems that you always had with the sfFormDoctrine don’t depend on you.
It is the very concept of Form to to be wrong.
Now I will show you to get rid of them and live happy.

A dutiful specification

I have a debt of gratitude towards Fabien Pontencier and his team. My business lives because of Symfony. My salary comes from the use of Symfony‘s framework. Out of all the PHP frameworks that I have worked with professionally, Symfony is without a doubt the best. If I have to tell the whole story, I think Symfony is a masterpiece of architecture.

I do not want to give the impression of disregard towards Fabien’s work with what I am about to say. I sincerly admire it.

But from the moment in this article I strongly contest some infrastructural choices of his framework and propose a solution that ruthlessly destroys one of the components of Symfony‘s core, I felt like spitting it out: I might have written very strong and extremely politically incorrect assertions.

Forgive me. This is just my personal opinion.

A heretical point of view on the sfFormDoctrine

Where is the sfFormDoctrine in MVC context?

It seems to me that there are at least 3 fundamental tasks:

  1. to execute HTML rendering of the widgets, of the labels and of the error messages
  2. to invoke the methods of the Model – and if necessary the methods of the Models for the embedded Forms in the correct order – in order to persist data on the database
  3. to collect in a sole point the rules of validation and execute them.

Now, feature 1 is the same as the one normally performed by the components of the Presentation Layer, or the View layer in MVC context.

The Forms allow to call back in the template methods such as:
echo $Form->renderHiddenFields(); and to produce HTML code, precisely like link_to().
It is obvious that Forms, therefore, can be placed, together with Helpers, in the View layer of the MVC.

Feature 2 is superimposed to the tasks of the Controller: it’s Controller layer elements that typically determine the operating flow of the program and invoke in the right order the methods of the Models of the underlying Data layer.
Forms, therefore, perform typical functions of the MVC Controller.

Feature 3 pertains the manipulation and the correctness of the data conserved from the Business Objects.
It is clearly a task that normally comes turn from the Model.
Forms, therefore, develop typical functions of the MVC Model.

A three-headed monster

The sfFormDoctrine objects unite what the MVC paradigm tried to separate.

In the architecture of Symfony, Forms are the sole component whose field of application break the boundaries of the individual layer. The separation of code and functions in M, V and C, one of the cardinal principles of Ruby-On-Rails-like frameworks, comes cheerfully betrayed to breed an allmighty and omniscient monster, that can generate the HTML of the tags, perform the validation of Business Objects and take control of the program execution flow when saving on db. It is the sole object of the whole framework that does not respect MVC principles.

It comes by itself that the typical cleanliness and the exceptional order of softwares written with Symfony are lost as soon as we need to make operations with Forms that are just outside the ordinary. No wonder, in conclusion, if the Embedded Forms are the nightmare of every Symfony developer. And yet, if we think about it, the editing of two tables using just one Form should be a banal operation: or rather, to be really honest, this is the minimum you can ask to a framework.

It is sufficient to follow an ordinary tutorial of CakePHP or Ruby on Rails to realize that this target is really embarassingly very simple to reach with other frameworks. Moreover, both frameworks use exactly the approach described in this article.

If the result is that often the developer has the impression of having almost totally lost the control of his own application, it is clear that there is something really sick in sfFormDoctrines.

My conviction is that the fundamental problem is the existence itself of the abstraction layer for the Form management.

The web is full of articles from with titles like ‘A workaround to make embedded Forms and merged Forms work in almost any cases (very advanced)’.

I hope I have written ‘The definitive suggestion to make the Form matter finally simple’.

Why the situation is not solveable with refactoring or patches

At the end of an umpteenth day of discouragement, working with a dynamically generated embedded Form, after reading the thousandth article on this subject, I have convinced myself to rewrite the entire Form management layer.

Surprisingly, the analysis led me to a rather paradoxical conclusion.

The Business Rules belong to the Business Object!

The first target that I planned was: to transfer the data validation control from the Form to the Model.

The idea that data validation is a Form task always seemed deeply wrong to me. Nowadays I am still convinced that it has been an unforgivable mistake from Symfony architects.
The core mistake is the unreasonable assumption that the data to validate comes unavoidably from a web form.
It is not true and it is very dangerous to assume it.

Let’s see an example.

We take an application that should record data for a bank transaction. The application have a TransactionForm form to manipulate the Transaction model with a widget dedicated to the IBAN insertion.

Obviously it is vital that the IBAN is subject to validation prior to storage on db. It’s a matter of money. For no reason in the world I would like to have a record with a malformed IBAN.

For this reason, the IBAN widget will have it’s trusty validator. According to Symfony, I am covered from any bad surprises.

Nevertheless, if an Action performed:

$TransAction = new TransAction();
$TransAction->setIban("wrong IBAN");
$TransAction->save();

the TransAction Model would cheerfully save the wrong IBAN field without any reports, violating the TransAction object Business Rule.

This possibility is anything but remote: the manipulation of a Model from an Action, without the involvement of a HTML Form, happens for example in the case of webservice.
Besides, it is self-evident: to validate a data means ensuring that the object respects the Business Rules.
And who is responsible for the Business Rules if not the Business Object, that is the Model?

The Business Object is the data. What twisted reasoning could have lead to the delirious decision to deprive the application data core of the sole protection network to move it some meters up, encapsulating inside a component that, among all its tasks, is in charge of printing HTML labels? (Someone, a real purist, could object that the Business Rule should be defined at an even lower level, at the level of RDBMS, for example by means through triggers. How could you say he’s wrong?)

Preserving the code to respect the Business Rules within the Form class is dangerous and philosophically wrong. It sensationally violates the Single Responsibility Principle, because encapsulates the Model‘s responsibility within an object that is just one among many users of the Model and exposes the application to the risk of violating the validation rules every time Models are directly manipulated.

Not only this: it also brings the risk of having different and contradicting valdiation rules in case more than one Form involved the same Model.

The solution, in reality, is very simple: you imperitively need to move the data validation responsibility from users to object to validate.
It’s obvious.

It’s not an accident, in fact, that any ORM that are worthy of respect gives the tools to perform the validation.
Hibernate supplies the Hibernate Validator. And even Doctrine has it’s own good validators. There is even a whole chapter in the manual about this. To ignore it is irresponsible.

For this, I eliminated from my implementation of the sfFormDoctrines every reference to the validation. In my implementation, I wished it was the Model itself to inform the Forms of what Business Rules are violated.

One less task for the sfFormDoctrines, with a great advantage for the solidity of the application.

I hate to double the code in the partial!

Inside the form are listed the widgets that must be used.

In the template that will have to visualize the Form (to understand, in the self-generated partial _Form.php) are listed many

echo $Form ['a_field'];
echo $Form ['an_other_field'];
echo $Form ['a_third_field'];

to obtain the HTML of the input field.

A lot of work can be saved asking the Form to generate itself all HTML inputs, with an

echo $Form->render();

but this, Symfony‘s fans forgive me, is a solution that’s almost ridiculous.

If you leave the control of HTML generation to the Form you need to do deadly leaps to manipulate those features for which the HTML would be the fittest tool.

For example, to move two fields in the HTML you need to intervene in the Form code with

$this->widgetSchema->setPositions(
  array(
   'field_1',
   'field_3',
   'field_2',
   'field_4'
  )
);

Or, if you wanted to use a Form in a second page and decide to hide a field, you would have to implement a new class, adding in its configure ()

unset($this->widgetSchema['field'])

to add a Javascript event or to modify a CSS class of an input you need to perform disreputable tricks.

The most astonishing aspect is that Forms do everything to hinder the developer use of the three most suitable tools for these purposes: HTML, Javascript and CSS.

Bad scene.

In fact, echo $Form->render(); is good for scaffolding or little more and if you don’t want to lose control too much on the HTML you will have templates substatially equivalent to long sequences of

[...]
<tr>
  <th><?php echo $Form['name']->renderLabel() ?></th>
  <td>
    <?php echo $Form['name']->renderError() ?>
    <?php echo $Form['name'] ?>
  </td>
</tr>
<tr>
  <th><?php echo $Form['surname']->renderLabel() ?></th>
  <td>
    <?php echo $Form['surname']->renderError() ?>
    <?php echo $Form['surname'] ?>
  </td>
</tr>
[...]

Now, there would be nothing bad in listing all fields inside the template if it wasn’t for the fact that you already have done the same identical job in the Form class with

$this->setWidgets(array(
  'id' => new sfWidgetFormInputHidden(),
  'name' => new sfWidgetFormInputText(),
  'surname' => new sfWidgetFormInputText(),
));

Don’t Repeat Yourself! Please, I want to write the field list just once!
And, if I could choose, I prefer to do it in the template. Besides, we are writing HTML.

In short, I find that the Form, as a widget container, after all, is not a big help.

I mean, if it was possible to eliminate the widgets from the Form and possible to write them directly in the template with something like:

[...]
<tr>
  <td>
    Name
  </td>
  <td>
    <?php echo new sfWidgetFormInputText();?>
  </td>
</tr>
<tr>
  <td>
    Surname
  </td>
  <td>
    <?php echo new sfWidgetFormInputText();?>
  </td>
</tr>
[...]

we could emancipate the Form from the trivial and totally useless duty of managing HTML in place of the template.

Other frameworks use this approach, supplying the appropriate Helpers.
And even Symfony once supplied a set of Helpers for the tag generation directly in the template.
I got convinced that the template was really the best place where to manage HTML and layout, and that the Form as a generator of HTML was, after all, just an obstacle.

Only one problem remained: to visualize inside the HTML the validation error messages.
But it is soon said: once the Business Rules are transferred inside the Model, the template can query it directly to know which fields are not valid and visualize the respective error messages.
Oh yes, Doctrine can already very well manage by itself a stack of error messages. Is it an accident?

Another useless Form burden that we can get rid of.

No intermediary between sfWebRequest and Business Object!

Let’s look at what happens in the typical method executeUpdate() of a Form based Action:

public function executeUpdate(sfWebRequest $request) {
  $this->forward404Unless($request->isMethod(sfRequest::POST) || $request->isMethod(sfRequest::PUT));
  $this->forward404Unless($object = Doctrine_Core::getTable('Table')->find(array($request->getParameter('id'))),
    sprintf('Object does not exist (%s).', $request->getParameter('id')));
  $this->Form = new object Form($object);
  $this->processForm($request, $this->Form);

  $this->setTemplate('edit');

protected function processForm(sfWebRequest $request, sfForm $Form) {
  $Form->bind($request->getParameter($Form->getName()), $request->getFiles($Form->getName()));
  if ($Form->isValid())
     $object $Form->save();

Roughly this is the process:

  1. User performs the submit and the executeUpdate() method, defined as Action in the Form, is invoked.

    $request contains all data introduced from the user

  2. Through Doctrine_Core::getTable('Table')->find($request->getParameter('id')) the object to update is recovered
  3. The object sfDoctrineForm is instantiated and the object instance is passed to it.
  4. sfDoctrineForm is processed. The Form executes the binding between $request and the object, so that the properties of the Model get the values defined in the input fields of the web Form.
  5. The Form is saved. With this operation the object is saved.

The question is: hmmmmm, for what purpose object Form is instantiated?

For two purposes:

  1. To perform the binding between $request and the object
  2. To save the object

It’s like firing a cannon at a fly: there is no real need of instantiate an entire class to perform these few operations. You just need to ask the object directly to populate itself according to the data present in $request and subsequently to save itself:

$object->fromArray($request->getParameter('something');
$object->save();

The involvement of the Form, in the standard approach of Symfony, is dictated by the necessity to perform the data validation and from the necessity to save eventual embedded Form. These are, by the way, two tasks that the Doctrine Model is already able to perform extremely well:

  • Model can easily validate itself
  • Model can save recursively the other Models eventually connected to it. Doctrine was invented for this purpose

Let’s admit it: in the communication between the sfWebRequest and the Model, the Form is performing some totally useless operations.

But then, what’s your purpose?

To sum it up:

  • Form as a HTML generator doubles the code and is not of help
  • It should not be used as a validation tool. May plague attack you if you only think about it.
  • It’s no use at all for the communication between Form HTML and Business Object

What functionalities were left to implement, in my new version of the sfDoctrineForm object?

None.

I began to think that this story of sfFormDoctrines was a large useless bubble. Lots of smoke and no real advantage. Just weeks of headaches to do the easiest operations.

For what benefit, then, to carry this pachyderm?
What would happen if we just simply killed it?
I tried.
It works.
Better.

The Form is dead, long life to Symfony

I tried to pretend I never read the chapter on sfFormDoctrine. And I discovered that everything becomes magically simple.

Let’s get to the point. I want a Form (complete with the scary embedding feature) that allows me to save data of a cat: Cat(id, name, owner_id) and of his owner: Owner(id, name).

I used my courage and I killed the directory lib/form.

Goodbye.

After that I wrote a Model, with its good validator (I’m using a banal example: i could use the great standard Symfony validators inherited from sfValidatorBase; we’ll see how later)

class Cat extends BaseCat {
  protected function validate() {
    // Name's length must be at least 5
    if (strlen($this->getName()) < 5) {
      $errorStack = $this->getErrorStack();
      $errorStack->add('nome', 'Name must be at least 5 char');
    }
  }
}

validate() is executed when we invoke isValid() to directly query the Model on the vaidity of its own data. The good thing about validate() is that is invoked automatically before saving the record: Doctrine, in other words, inhibits the saving of the records that violate the Business Rules. It’s exactly what we were looking for.

Here’s the HTML template to print the Form:

<form method="post" Action="<?php echo url_for("cats/update");?>" >
  <table>
    <tr>
      <td>
        <?php
           $w = new sfWidgetFormInputHidden();
           $w->render("cat[id]", $cat->id);
         ?>
       </td>
       <td>
         <?php
            $w = new sfWidgetFormInputText();
           $w->render("cat[name]", $cat->name);
         ?>
       </td>
     </tr>
     <tr>
       <td>
         <?php
           $w = new sfWidgetFormInputHidden();
           $w->render("owner[id]", $cat->getOwner()->id);
         ?>
       </td>
       <td>
         <?php
           $w = new sfWidgetFormInputText();
           $w->render("owner[name]", $cat->getOwner()->name);
         ?>
       </td>
     </tr>
  </table>
  <input type="submit" />
</form>

The names of the widgets have been chosen so that the Action receives an object sfWebRequest in the Form of an array, with the Cat data separated from the Owner ones:

array(
  'cat' => array(
    'id' => 2,
    'name' => "Fritz",
  ),
  'owner' => array(
    'id' => 12,
    'name' => "Marianne"
)
);

The Action Class will look like this:

class CatsActions extends sfActions {
  public function executeNew(sfWebRequest $request) {
    $this->cat = new Cat();
  }

  public function executeUpdate(sfWebRequest $request) {
    $cat = new Cat();
    $cat->fromArray($request->getParameter('cat'));
    $cat->getOwner()->fromArray($request->getParameter('owner'));
    if ($cat->isValid() && $owner->isValid()) {
      $cat->save();
      $this->redirect('cats/index');
    }
    else {
      $this->cat = $cat;
      $this->setTemplate('new');
    }
  }
  public function executeEdit(sfWebRequest $request) {
    $this->forward404Unless($cat = Doctrine_Core::getTable('Cats')->find(array($request->getParameter('id'))), sprintf('Object does not exist (%s).', $request->getParameter('id')));
    $this->cat = $cat;
  }
  }

End of the story, more or less.

Please note, among the other things, there’s no more need of executeCreate(), because it’s by itself capable of managing both the creation and the update of the object.

Don’t try to run this code again, it would not work. For now just verify the logic of this.
To make everything run properly there are a couple of arrangements missing, still. These are some setup operations, to execute only once at the creation of the project.

Some Little Tweaks.

Doctrine’s validation

In Symfony, Doctrine‘s validation is deactivated by default.

To activate it you need to set the attribute Doctrine_Core::ATTR_VALIDATE to the value Doctrine_Core::VALIDATE_ALL.

You can use the callback method configureDoctrine() in the class config/ProjectConfiguration.class.php:

class ProjectConfiguration extends sfProjectConfiguration {
  public function configureDoctrine(Doctrine_Manager $manager) {
    $manager->setAttribute(Doctrine_Core::ATTR_VALIDATE, Doctrine_Core::VALIDATE_ALL);
  }
}

fromArray() is not clever

When I said that in order to execute the binding between sfWebRequest and the Model is sufficient

$cat = new Cat();
$cat->fromArray($request->getParameter('cat'));

I lied a little. Things are a bit more complicated.

Let’s suppose we have record 12 with some values inside table cats.

It would be really nice if, loading an instance of cat with the array

$data = array(
  'id' => 12,
  'name' => "Fritz"
)
$cat = new Cat();
$cat->fromArray($data);

the following execution of $cat->save() would update record 12.

Unfortunately things work differently and the saving would fail with a duplication error of primary key.

$cat = new Cat() creates a new instance of Cat. For Doctrine, this is a new record. Unfortunately, even once we set the primary key value (either manually or through fromArray()), Doctrine will keep on treating the instance as a new record.

To patch this, we need to be careful to execute $cat->assignIdentifier(12) to tell Doctrine to treat the Model like the id 12 record, and then execute an UPDATE instead of an INSERT.
Naturally, I would never like to have to write some glue code every time that I have to save data of a Form.

The idea is to implement this behavior in a class that extends sfDoctrineRecord. Generalizing the assignment of the primary key is not exactly banal, because the name of the primary key is arbitrarily chosen from the developer and because it is possible that he had chosen to use multiple primary keys. A possible (working) implementation could be:

abstract class MyDoctrineRecord extends sfDoctrineRecord {
  public $validatorSchema;

  public function loadFromArray(array $array) {
    $primaryKeys = $this->_table->getIdentifier();
    if(!is_array($primaryKeys))
      $primaryKeys = array($primaryKeys);

    $primaryKeys = array_combine($primaryKeys, $primaryKeys);
    $intersect = array_intersect_key(array_filter($array),$primaryKeys);

    if (count($primaryKeys) == count(array_count_values($intersect))) {
      $this->assignIdentifier(array_intersect_key(($array),$primaryKeys));
    }
    parent::fromArray(array_filter($array));
  }
}

There is nothing left to do than convince all the other Table objects of the application to inherit from MyDoctrineRecord instead of sfDoctrineRecord.

Fortunately, Doctrine allows to define the classes to use as a base for both the Business Objects and the Table Classes.

class ProjectConfiguration extends sfProjectConfiguration {
  public function configureDoctrine(Doctrine_Manager $manager) {
    $manager->setAttribute(Doctrine_Core::ATTR_VALIDATE, Doctrine_Core::VALIDATE_ALL);
    $options = array(
      'baseClassName' => 'myDoctrineRecord',
      //'baseTableClassName' => 'myDoctrineTable'
    );
    sfConfig::set('doctrine_Model_builder_options', $options);
  }
}

We just have to use loadFromArray() instead of fromArray() and game over.

And what if we want to reuse sfValidator* classes?

sfDoctrineRecord classes inherit from Doctrine_Record a set of callback methods very useful for the validation: preValidate(), postValidate() and validate().
However, it would be very useful to be able to use even the many sfValidator* classes provided from Symfony, like sfValidatorEmail for email validation, sfValidatorDoctrineUnique for unique indexes validation etc.
It would be interesting, in short, to enrich the sfDoctrineRecord classes with the ValidatorSchema previously used by sfDoctrineForm.

Luckily, it is not that difficult.
Let’s suppose to define, in Cat Model, one sfValidatorRegex validator associatd to the name of the cat to make sure that the name starts with a capital letter:

class Cat extends BaseCat {
  public function validate() {
    parent::validate();
    if (strlen($this->nome) < 15) {
      $errorStack = $this->getErrorStack();
      $errorStack->add('name', 'Too Short');
    }
  }

  public function setUp() {
    parent::setUp();
    $this->setValidatorSchema(
      new sfValidatorSchema(array(
        'name' => new sfValidatorRegex(array('pattern'=>'/[A-Z].*/'), array('invalid'=>'The name must start with a capital letter')
      )
     ));
  }
}

We want that during the validation, not just the standard method sfDoctrineRecord::validate() is executed but also all the validators defined inside the Validator Schema.

It will be sufficient to enrich our implementation of sfDoctrineRecord as follows:

abstract class MyDoctrineRecord extends sfDoctrineRecord {
  public $validatorSchema = array();
  public function setValidatorSchema(sfValidatorSchema $validatorSchema) {
    $this->validatorSchema = $validatorSchema;
  }
  public function getValidatorSchema() {
    return $this->validatorSchema;
  }
  public function validate() {
    $this->setup();

    try {
      $this->getValidatorSchema()->clean($this->toArray(false));
    }
    catch(sfValidatorErrorSchema $errorSchema) {
      $errorStack = $this->getErrorStack();
      foreach($errorSchema->getErrors() as $key=>$error) {
        /*@var $error sfValidatorError */
        $errorStack->add($key, $error->getMessage());
      }
    }
  }
}

Some Helpers

In sfDoctrineRecord::getErrorStack() the Model supplies an array with the validation errors.

sfDoctrineRecord::getErrorStack()->get($fieldName) supplies an error with the various errors spotted in the field of $fieldName Model.

To get the various messages from the Model and visualize them inside the Template we need a cycle similar to:

foreach($Model->getErrorStack()->get($fieldName) as $error) {
  echo $error;
}

It’s worthy to use a Helper to reduce the code to be typed into the template. A possible prototype could be this one:

class MyRender {
  static function error($Model, $name) {
    if(count($Model->getErrorStack()->get($name))>0)
      return $Model->getErrorStack()->get($name);
    else
      return null;
  }
  static function errorList($Model, $name) {
    $list = self::error($Model, $name);
    if(!is_array($list))
      $list = array($elenco);
    return implode("<br/>", $list);
  }
}

This way the template can be renamed as follows

<tr>
  <td>Name</td>
  <td>
    <?php echo MyRender::errorList($cat, 'name');?>
    <?php
    $w = new sfWidgetFormInputText();
  	echo $w->render('cat[name]', $cat->getName();
    ?>
  </td>
</tr>

Download

Example for the lazy

You can download from here a full project that uses this approach.

And what about the automatic generation of the code?

Ephraim Pepe wrote a Symfony task that is able to generate Actions and templates based on this concept.

His work will be soon published.

What do we miss?

Just the sfForm class in the standard library of Symfony is made of 1340 lines of code. In Symfony 2 we still have a class hierarchy inside Symfony\Component\Form namespace with over 5000 lines of code already developed.

I have doubts, therefore, that in the next days, going deeper inside the subject, I will discover some fundamental functionalities that are not covered from my approach. For example, I know for sure that I still have to work on the implementation of the CSRF protection.

Yet I am persuaded that the direction to follow is the right one: leaving templates in control of the HTML, moving validation to Business Objects, using only the minimum necessary intermediates between sfWebRequest and Model.

In the company that I work for we are progressively abandoning the use of sfDoctrineForms in favor of this approach even for our production softwares.

If you are interested in the subject, stay in touch: we will be sharing code as we go.

16 thoughts on “How to kill Symfony’s Forms and live well

  1. Mark says:

    Brilliant post. I struggled with embedded forms and I always found them delirious.
    This method works. Hope this could be the standard.
    Thanks for sharing.

  2. I’ve some projects with Symfony. I’m not a pro with it, but always hated the Form behaviour.
    While i was reading your post, i’ve found so many phrases i used (like the part when mixing the mvc paradigm) that sounds just like me.

    Thank you for this, excelent post.

  3. Excellent article, come from a background of symfony 1.0, I don’t like Symfony form component found in the subsequent versions either.
    There’s no such thing as form in symfony 1.0, and there shouldn’t be any, to PHP, every request you make is just a URL with a collection of parameters, form is just a way of sending request, it’s simply not in PHP’s domain. So in symfony 1.0, validators are used on the request parameters.

    But there is a problem with your approach, you know, not all the data coming from the request need to be stored in database, or bound with a model, and these parameters are also need to meet some constrains, since you are using model validators to validate data, how do you handle this problem? A two layers of validation maybe?

    • am says:

      Even if previous examples were using a persisted Model inherited from sfDoctrineRecord, there’s no proscription to pass the View an ordinary array or any not-persisted object.

      In fact, when you write

      $request->getParameter(‘cat’)

      in your Controller you’re actually getting $_REQUEST[‘cat’], an ordinary PHP variable which may contain an instance of Cat, a simple array of values or whatever you want.
      Since there’s no Form class that requires your data to be inherited from a particular class, you’re free to use whatever you want in the communication between Controller and View. You may even directly use $_REQUEST.

      For example, you can store some data in $this->cat = array(‘name’ => ‘whatever’) and use in your view

      $w = new sfWidgetFormInputText();
      $w->render(“cat[name]”, $cat[‘name’]);

      or you could use an object $cat which doesn’t inherit from sfDoctrineRecord.

      If you want to validate your (not persisted) data, since validation code is not embedded into any Form class, you can use a custom method in the Controller class or a method of (not persisted) model itself. You’re not forced to use sfDoctrineRecord’s ErrorStack.

      Yet I think that validations should be accomplished by data itself: in other words, I think you should encapsulate your data in a class with some validation stack.

  4. No way do to describe the relief I am experiencing reading this post. I thought this is me so stupid to understand this sfForm complications. Thanks again. It worth it to rewrite some part of the project i develope @ the moment.

  5. hh says:

    Well done. Things seem improved somewhat in Symfony2; just wished I hadn’t wasted so much time in the old forms framework

  6. I don’t like the idea to write full html code in template in order to render a form. The html code of most forms in one application is identical and formatted with css, so I don’t see need to repeat myself every time. As form’s render method is not suitable for my needs, I wrote my own render_form method, which goes approximately like that:
    echo render_form($form, $url, array(
    ‘field1’,
    ‘field2’ => array(‘class’ => ‘special_field’)
    ))
    This will print out standard html structured code, which is formatted in unified way with prewritten css, and have option to set custom classes on field level, so one can bring specifics to that concrete form.

    • am says:

      Good point.
      I like DRY principle as well, and I think your approach can (and should) be applied as much as possible.
      I also think it’s absolutely compatible with my proposal.

      You wrote a Helper class (or a Helper function) able to render a form, and that’s very clever.
      But, since my approach does not contemplate any “Form object”, I would adapt your snippet as follow:

      echo render_form_for_model($model, $url, array(
         'field1',
         'field2' => array('class' => 'special_field')
      ))
      

      I mean, I would not use any sfDoctrineForm object.

      Differently said: if $form is useful for “rendering form” I would accept it, since it could be very useful. But, applying MVC thought I would also conclude: “forms are in V in MVC”.
      For this reason, since I absolutely want to decouple views, helpers, partials and every other element in V from M, I could not accept a form able to save the model.

      Your approach is nice, because it lives in V world: your method receive something and renders it. That’s ok. It only does things related to rendering.

      All I say is: your method could receive a model, instead of a form.
      I believe your smart suggestion is still valid in my context.

  7. japes says:

    So I’m a little late to the party on this one, and while I think your logic is sound there’s at least one important reason for abstracting the idea of the ‘form’ beyond a simple array passed directly to the model for validation. For example:

    I have an object which some users can edit and other users can process. Let’s say a comment posted to a blog by a user, which an admin-user can approve or deny. The user can add a ‘comment’ and a ‘name’ and the admin can edit both of these, along with a ‘status’ field, which might detail whether the submission is pending, approved or denied.

    While the fields presented to the user may be limited, passing the request array to the object could allow a malicious user to approve their own submissions, by adding a post[status] field to the submission. It’s still a valid submission as far as the model knows, but there should be some logic in the controller to prevent this type of over-reach.

    Similarly, on a single field, you may want to allow a certain type of submission for one class of user but not from the other (silly example: only allow employees to submit email addresses ‘*@thecompany.com’). Having some level of validation which is not in the model layer allows this type of restricted interface to be employed.

    Essentially, having a model able to validate itself makes sense. However, there’s a further level of interface to such objects which requires its own validation and a selective availability of fields above the model layer.

    Along with your mentioned csrf function, these functions suggest some kind of Form interface object in the controller layer. When you then consider how to combine validation fails from both the model and the form it does begin to seem logical that they should be encapsulated and served from a controller form object whose purpose is to:

    – limit fields
    – apply controller-level validation (eg. anything to do with users)
    – defer to model model-level validation (ie. business logic)
    – provide a consistent interface for accessing errors to report to the user
    – returning a save-able object to the action

    While the manner in which this is achieved in Symfony 1.4 is excessive at best, and sould-crushing at worst, I think there’s a risk of oversimplifying by kicking it out altogether.

  8. Hello there! This is my first visit to your blog!
    We are a group of volunteers and starting a
    new project in a community in the same niche. Your blog
    provided us valuable information to work on. You
    have done a marvellous job!

  9. I comment each time I like a article on a website or if I have something to valuable to contribute to the conversation.

    Usually it’s caused by the fire displayed in the article I looked at. And after this article How to kill Symfony’s Forms and live well | Arialdo Martini. I was actually excited enough to drop a leave a responsea response😉 I actually do have some questions for you if it’s okay.
    Could it be only me or do some of the remarks appear as if they are coming from brain dead people?😛 And, if you are writing on other online sites, I’d like to keep up with you. Would you list the complete urls of your social pages like your twitter feed, Facebook page or linkedin profile?

  10. The main problem lies in where the fungus is living: under the nail.
    The mother will nurse them for 3-4 months until they are able to swim on their own. My mother and my grandmother are
    very calm people who seem to handle stress very well.

  11. Remo says:

    I’m quite late to the party. I just tasked to work on legacy enterprise app which used symfony and sometimes have to kill this monster, thank you for the insight

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s