Warming up this topic.
I know that i always pushed against solving the inheritance problems in the
form component, but the new approach - while being more clean in separation -
has lots of other problems:
First, an organizational question: Why are all the current changes merged into
the branch? Its impossible to check which commits belong to this new branch,
and which belong to the master.
1. FormFactory::getInstance() scares me. Why is there a while() loop, what is a
$hierachy? What are all the config objects that are being created?
2. There is no way to build a form programatically anymore, everything has to
go through the FormFactory. This makes using forms MUCH more complicated.
3. Tons of new object instances are needed, renderers, configs, plugins. None
are loaded lazily. What are the performance implications for medium/large forms?
4. Why is the FormFactory not a builder object around the lightweight objects
that existed before? Now a FormFactory has to be injected into every form. This
feels wrong. It seems the factory is only used inside Form:add(). Wouldn't it
be better to add a builder like:
class FormBuilder extends Form
{
public function add($field, $identifier = null);
}
Then remove the FormFactory dependnecy from Form and move it into FormBuilder.
So that people can use the Form again without having to use that factory thing.
Also this way the ugly code from Form:add using func_get_args could be removed.
5. How can i create field elements outside of the config/factory stuff? Instead
of the FormFactory a builder object would be very useful.
6. What is a field plugin? Why does every field need one, isnt the config
already there to split separation of field and configuration?
7. Why is the EventManager used inside Field? Why is there one EventManager and
one Filterchain per Field? This kind of object inflation is what makes Zend
Form so slow. How about creating them lazily in addEventListener() and
addFilter() and checking for this in bind() and submit()?
8. The $form->getRenderer() seems weird to me. Can't it be hidden inside the
twig and php template helpers?
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
>
--
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