This is part of
#2015147: [meta] Improve the DX of drupal_render(), renderable arrays and the Render API in general →
Currently it is pretty confusing and requires some research to figure out when #theme and #type should be used to render a given HTML element with the Render API.
Since #type can set a default #theme through element_info() it makes sense that #type should actually be the "de facto" starting point for building any renderable array and #theme can just be used to set overrides when the default #theme is not sufficient.
To achieve this we need a few things:
- Every #theme currently set by core in hook_theme() needs a corresponding #type set in hook_element() if it doesn't have one already.
- Every #theme with a corresponding #type already needs to actually be compatible, eg. taking the same parameters, doing the same sanitisation, not using #markup as the default render for the #type, etc..
- Renderable arrays used by core should always be built starting with #type so that core leads by example - this has to be done last and will probably end up being its own meta issue.
There has been a general movement towards this for a while now, but I'm unaware of any existing meta issue bringing them all together and formalising the process so I opened this one.
Introducing new element types for existing "base" theme hooks
For the most basic additions all we need is something like the following for #theme 'foo' and module 'bar':
/**
* Implements hook_element_info().
*/
function bar_element_info() {
$types = array();
$types['foo'] = array(
'#theme' => 'foo',
);
return $types;
}
For these most basic conversions, if
#2005970: In renderable arrays, #type should provide a #theme suggestion, similar to how drupal_prepare_form() works →
ever lands it will remove the need to explicitly set all these #theme defaults but it's unlikely the patch in that issue will land if we don't have a comprehensive set of #types that are compatible with matching #themes - It's kind of chicken or the egg, so we have to start somewhere.
Not everything in this list is this simple however, some are looking to introduce more advanced functionality in the form of new #pre_render callbacks.
It is important to realise that theme hooks may be theming forms based on a form ID, which is set as a theme suggestion in drupal_prepare_form() - this is an example of render defaults being merged into a render array outside the "element type" system. In these cases, defining a #type is useless but I don't know of any easy way to test whether a hook is implementing a form ID based suggestion or not.
There are plenty of other theme hooks that are already overriding a default #theme for a given #type for a specific use-case or are implementing a suggestion - These do not need their own #type. Figuring out what makes sense as an element #type and what only exists as a #theme override/suggestion can take a little research and discussion on a case-by-case basis.
#theme hooks that are only intended to be used with #theme_wrappers do not need a matching #type element.
The concept of a "base" theme hooks are those that are used in core with no #type currently set and that aren't implementing a form ID suggestion.
Categories
A - super simple
If #theme works, the theme hook uses variables instead of 'render element', the theme hook doesn't render #children (we're not using #theme_wrappers anywhere) then all we need to do for this is take:
$foo = array('#theme' => 'foo');
define a type in hook_element_info() that sets the #theme:
bar_element_info() {
....
$types['foo'] = array(
'#theme' => 'foo',
);
Then we can replace the #type with #theme in the original array wherever it appears:
$foo = array('#type' => 'foo');
Issues
-
#1595614: [meta] Remove all the theme functions and templates in core that simply output a link. Replace with #type 'link' render arrays →
-
#2086619: #theme 'maintenance_page' should be based on #type 'page' →
Deprecated list, see comment #7 (clean me out, find the wheat in all this chaff please):
Ensuring #type/#theme compatibility between hooks/elements with the same name
There's a few things that can happen here.
All things equal, it would be ideal if when a #type declares a default #theme hook they both have the same name. This won't always be possible, an obvious example is when multiple #types share the same #theme hook, but we should try to consolidate things where we can.
Any #type that declares a #theme of the same name that also declares #pre_render or #post_render callbacks must use the same parameter names that the #theme hook is expecting, or at the very least not conflict/preclude the #theme hook from operating in the desired way when #type is used.
No #theme hook may set a variable named 'type' as this immediately makes it impossible to ever implement a #type for this theme hook.
Migrating existing #theme arrays to #type arrays