This issue is the 1st step towards migrating Field API towards Plugins.
Attached patch starts with widgets. Next in line, in separate patches:
- Formatters - basically ready in (build on the widgets patch), but needs more performance testing
- Fields types
- Field storage backends - or not, depending on how goes
"Field widget" Plugin type
The patch introduces the "Field widget" plugin type, managed by WidgetPluginManager. It uses annotations discovery, and a custom WidgetFactory factory (as advised by EclipseGc rather than the ReflectionFactory, for performance reasons).
The WidgetPluginManager object is accessed through field_get_plugin_manager('widget'), which acts as a static factory for now. This will move to the DIC at some point (afteris solved).
WidgetInterface / WidgetBase
Actual widget classes implement WidgetInterface, and will most probably want to subclass WidgetBase.
WidgetBase methods hold the code that currently exists in field_default_*():
WidgetInterface also includes methods replacing the old hook_field_widget_*() hooks:
|(complex tricks with FAPI #callbacks)||(new) Widgetinterface::massageFormValues()|
Those methods are usually not called directly, but rather called by the methods above.
To make it clearer for widget developpers which methods need to be implemented vs. which are probably better off inherhited from WidgetBase,
the interface has actually been split between WidgetBaseInterface (wrapper methods) and WidgetInterface (methods you actually want to implement - extends WidgetBaseInterface). Credits @EclipseGc for the suggestion.
Widget plugins are created from an $instance structure (since the $instance holds the definition of the widget). They are instanciated on demand (when a widget for that $instance is needed on the page) and are persisted through the request within the $instance itself. There is no real place other than the $instance where the Widget could be persisted, since a widget is "per instance".
This implies turning the $instance structure into a classed object.
- This is something we want to do long-term (move away from dumb-array-of-hell $field and $instance structs)
- This is something that we'll *need* to do anyway as part of ($field and $instance as ConfigEntities)
- This very much makes sense for our case here.
Yet, to limitate the impact on the external facing API (switching $instance['property'] to $instance->property or $instance->getProperty() across all of core would be an unreviewable 500k+ patch), we're just turning $instance to a
FieldInstance implements ArrayAccess class here. Credits @sun for the idea and implementation.
FieldInstance::getWidget() calls the plugin system to instantiate the Widget plugin, then persists it for the rest of the request.
Similarly, there will be a FieldInstance::getFormatter($view_mode) when we do "formatters as plugins"
This also means that we cannot support hook_field_widget_properties_alter() changing the widget type on the fly on a per-entity basis. The hook still works, but does not receive an $entity in its $context parameter. We discussed this with @EclipseGc and @chx (who filed the original use case for "entity by entity" in D7), and this was deemed a reasonable regression - see for details.
Additionally, _field_info_prepare_instance_widget() is gone. Widget properties are not pre-massaged on all existing instances as part of _field_info_collate_field() anymore, but only when actually creating a widget opbject. Same will happen to _field_info_prepare_instance_display() when we do "formatters as plugins".
In order to:
- keep the patch size reasonable
- focus reviews on a limited amount of implementations
- crowdsource the rest of the conversions as potential "novice" followups, getting more people familiar with the API
this patch only converts a couple core widgets to the new API : text widgets, number widget, test widgets.
The other widgets (option widgets, image & file upload, taxo autocomplete, email) are kept fully functionnal by a LegacyWidget implementation, that takes care of proxying method calls to the old-style hooks. WidgetPluginManager discovers legacy widgets through a custom LegacyDiscoveryDecorator, that uses HookDiscovery to append the widget definitions still exposed through hook_field_widget_info(). Credits @EclipseGc for the idea.
- hook_field_widget_info() is replaced by annotations. Some keys are renamed (white spaces replaced by underscores).
Note : we currently have no standard location to document the expected format for the annotations - i.e. the documentation currently contained in the phpdoc for hook_field_widget_info().
- hook_field_widget_settings_form(), hook_field_widget_form(), hook_field_widget_error() : replaced by WidgetInterface methods (see above)
- field_default_form(), field_default_form_errors(), field_default_extract_form_values(), field_default_submit() : replaced by WidgetInterface methods (see above)
- hook_field_widget_properties_alter(), hook_field_widget_properties_ENTITY_TYPE_alter() : the 'entity' and 'default' entries in $context are gone.
Patch depends on:
- our use of ArrayAccess requires PHP 5.3.4.
Testbots run 5.3.4 now, but the actual core requirement bump hasn't been committed yet
Patch currently includes:
- to get rid of notices in tests
(a self contained version, that will be used within Field API plugins even if that issue goes nowhere)
Patch includes a workaround for:
- denoted by a @todo
The rest of the widget conversions will need:
- option widgets need hook_field_widget_info_alter()
Benchmarks will be tracked at.
Code-wise: @yched, @Stalski, @zuuperman, @sun, @tstoeckler - with xtra special thanks to @Stalski for awesome help on various subtasks.
Many thanks @EclipseGc for numerous feedback & suggestions on Plugin API.
PASSED: [[SimpleTest]]: [MySQL] 41,477 pass(es).
[ View ]
PASSED: [[SimpleTest]]: [MySQL] 41,310 pass(es).
[ View ]