Hello, i took this afternoon to review the whole patch again and agree with Bernhard that this is an improvement over the old way for several reasons:
1. Passing dependencies into fields was a nightmare before: You had to pass them using $options into the form, from there into the field and with embedded forms this even got more ugly. With the patch all fields are created through the DIC, all dependencies attached, very simple. 2. I first had doubts concerning the need for a class for every form and subform i want to configure. However technically that is what I was doing with my forms anyways, using "new" to build my form. The patch only formalizes the form creation process and building complex forms is much better (i.e. solves the dependency nightmare). 3. Filtering, Normalization and Transformation is solved very cleanly and i doubt you can come up with a field use-case that couldn't be handled by this implementation. 4. There is no inheritance depth anymore that makes you go crazy when debugging forms. 5. Themes seperate template engine of the form from the actually used template engine. That makes this much more reusable, for example i could use this component within Smarty wihtout having to write Smarty helpers (and just using twig or php renderer). 6. I discussed several optimizations with Bernhard that make all the technical doubts i had go away: FieldPlugins can be converted to flyweights, some methods on the factory will be renamed for better, EventManager inside fields will be lazy. That all severly cuts down the overhead of the new implementation. I think this should be merged (after additional work of course), it will make building complex forms much more easy. greetings, Benjamin On Fri, 25 Feb 2011 01:13:04 +0100, Bernhard Schussek <[email protected]> wrote: > Hi dear Symfony2 devs, > > I was pushed by several people to refactor the Form component to be > more decoupled and make better use of DI. Although I wasn't really > convinced of this approach at first, I decided to give it a try - and > am pretty satisfied with the results. Most of the features have been > refactored, but there are still details that need to be worked on. You > can find the current progress here: > > https://github.com/bschussek/symfony/tree/experimental > > Important: This is experimental. It is not decided whether this will > ever be merged into master. I also don't recommend to use this in any > application yet as CSRF protection is not ported yet. > > Thanks to Bulat for helping me. > > This post will outline the problems this refactoring tries to fix and > the benefits that we gain. Also, we need to find out how to deal with > the RC that is planned for next week. > > Fixed problems > ---------------------- > * The option implementation of Fields/Forms was buggy > * Forms could not be put into the DIC and retrieved from there > * Stubbing/mocking of Forms in controllers was impossible, because > they were created with "new" > * Dependencies where hidden inside configure() and could not be replaced > * Dependencies required in a field had to manually be added as options > of all parent forms (e.g. EntityManager) > * Field/Form by design contained lots of code that was relevant for > rendering only (getId(), getName(), getPattern() etc.) > * Because of the monolithic inheritance structure, it wasn't possible > to create fields that inherit the rendering of for example TextField > but the behaviour of another field > * Rendering was unflexible. It wasn't possible to override for example > the "row" template for a specific field type (needed for > RepeatedField, inline forms etc.) > > Additional advantages > -------------------------------- > * Fields and their renderers are now completely separated > * Rendering is more concise and flexible > * All fields of a specific type (e.g. text fields) used in an > application can be overridden/extended > > (probably more that I forgot right now) > > I want to explain how form creation and form rendering work with the > changed approach. Note that not all of the stuff I'm explaining here > is complete yet. > > Form handling > --------------------- > To create a form, you implement a form factory in your bundle. This > factory could look like this: > > https://gist.github.com/842827 > > Binding will look like this: > > https://gist.github.com/843094 > > And rendering something like this: > > https://gist.github.com/843115 > > Rendering improvements > ------------------------------------ > The most interesting part I believe is the rendering. As you can see, > we passed $form->getRenderer() to the template. This renderer encloses > variables and methods that are available there. Example variables are > "id", "name", "value", "class" etc. The methods are "errors", > "widget", "label", "row" and "rest". > > Each field and form has exactly one renderer. Depending on the field > type, this renderer has additional variables set that can be used in > the template. The renderer of a money field, for example, offers the > additional variable "money_pattern". The renderer of a form offers > "fields", "visible_fields" etc. All these variables can either be > statically set on the renderer by calling setVar(), or dynamically > using renderer plugins. They can also be overridden when calling > individual methods of the renderer, like "widget". > > In form themes, these variables are available as global variables in > the blocks of the field: > > {% block money_field %} > {{ money_pattern }} > {% endblock %} > > In normal templates, you can access the variables as properties of the > field renderer: > > {# Assuming the field "price" is a money field #} > {{ form.price.money_pattern }} > > As said above, each renderer also offers rendering methods. With > > {{ field.widget }} > > for example, you can output the widget of a field. You can also > override any of the variables in the call. > > {{ field.wigdet({ 'money_pattern': '...' }) }} > > Unlike before, there is no difference between HTML attributes and > template variables anymore. Each HTML attribute that should be > settable must be available as variable, for example: > > {{ field.wigdet({ 'class': 'myclass' }) }} > > {% block money_field %} > <div class="{{ class }}"> etc. > > Because renderers are simple PHP objects, you can extend them and add > functionality. You could implement this renderer > > https://gist.github.com/843139 > > and create a new form theme with a "help" block. You can then call > > {{ field.help('My help text') }} > > on all fields that have your custom renderer set. It is also possible > to override the renderers of all core fields to add functionality > globally. > > An interesting default rendering method is "rest" (not implemented > yet, if you have better names tell me). Because each field has a > stateful renderer, we can track which field was rendered at least > once. With > > {{ form.rest }} > > you can render all fields that weren't explicitely rendered. This > replaces the "hidden" helper and solves lots of other problems. If > you, for example, forgot to render the hidden fields in a subform, > these will be rendered here. If you added a field in the PHP code, but > not in your template, it will be rendered here. The designer will > notice the problem and manually render the field correctly. > > What about PHP rendering? > ---------------------------------------- > So far I've only talked about Twig. All renderers are connected to a > theme (an instance of ThemeInterface). By default, the component ships > with TwigTheme. By exchanging this object, you can either change the > template(s) used as themes or the whole theming engine. You could > implement a SmartyTheme that renders form fields using Smarty. Note > that the theming engine of the form and the templating engine where > you render the form are independent! > > For example, currently there is only a TwigTheme, but you can render > it in PHP template nevertheless. Just pass the form renderer to the > template and use it like in Twig: > > <?php echo $form->getWidget() ?> > <?php foreach ($form['visible_fields'] as $field): ?> > etc. > > I will cut this post for now. See my implementation if you want to learn > more. > > Conclusion > ---------------- > Because of DI, lots of problems in the form architecture could be > fixed and the rendering could be significantly improved. The reason > for this is that Twig and other dependencies can now be injected. > > Of course, there are problems too: The biggest one is that RC1 is > planned for next week. Most of the existing unit tests are green > again, but there are still lots of smaller things that need to be > fixed/worked on, and this won't be done next week. > > I am very confident though that this refactoring can be finished very > soon. Most of the existing features have been ported, for the > remaining ones (FieldFactoryGuesser, CSRF protection and > CollectionField) I don't see any showstoppers - it just needs to be > done. > > What I need: > ------------------ > * to know whether you want to see this in the first Symfony2 release. > Delivering it in later releases will be difficult because this is a > major BC break > * input. I have considered a lot of feedback in this refactoring that > I have got lately, but more input is always better. > * help. If you have time to help, you are very welcome. > > > Best wishes, > Bernhard > > -- > Software Architect & Engineer > Blog: http://webmozarts.com > Twitter: http://twitter.com/webmozart -- If you want to report a vulnerability issue on symfony, please send it to security at symfony-project.com You received this message because you are subscribed to the Google Groups "symfony developers" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/symfony-devs?hl=en
