In D7, if you want to add any non-field mechanics to entities, you have to implement a number of modules, including hook_form_alter(), and stuff like hook_node_update(), etc.
Within these hooks, you need to examine the given entity and check whether your mechanic is actually enabled for this entity bundle.
This all kind of works, but it is ugly and fragile.
Examples:
- Url aliases and redirects
- Menu items for nodes
- Workflow
(I'm sure there are more examples)
-------------
My proposal:
Entity behaviors:
Modules can attach behavior objects to an entity type. Those behavior objects can subscribe to a number of events related to this entity type.
Especially, they can
- do something if an entity is created, updated, etc.
- do something if a bundle for this entity type is created, updated, etc.
- declare bundle options (see below)
Bundle behaviors:
Modules can attach behavior objects to an entity bundle. Those behavior objects can subscribe to a number of events related to this entity bundle.
Especially, they can
- provide form elements on the entity edit form, which can be rearranged on the "Manage fields" screen for this bundle.
- provide display elements on entity view modes, which can be rearranged on the "Manage display" screen for this bundle.
- do something if the entity is saved, created, deleted, etc.
The cool thing is, the same behavior class can be reused for different entity types.
E.g. there could be a "BundleBehavior\\UrlAlias", which can be attached to taxonomy terms and to nodes, as long as the "EntityBehavior\\UrlAlias" is attached to both nodes and taxonomy terms.
Also, more than one instance of the same behavior could be added to a bundle.
So far this is all code, no configuration via UI.
Any UI configuration stuff either needs to be provided by the module itself, or preferably use the "bundle option".
Bundle options:
Modules, and entity behaviors, can register "bundle options" to an entity type. Bundle options
- provide form elements on the bundle configuration form
- provide a storage for those bundle options.
- can attach behaviors to those bundles, based on the stored option values.
-------------------
Yes, this proposal is still a bit vague, and a lot needs to be ironed out.
However, I think it is generally a reasonable direction to take.
One design considerations:
I intentionally want to split the behavior from its configuration UI. This allows to attach multiple behavior objects and maybe some other stuff, based on only one configuration checkbox. Also, this split will help to keep classes smaller.
-------------------
Related:
#1346214: [meta] Unified Entity Field API
#1803064: Horizontal extensibility of Fields: introduce the concept of behavior plugins
#1818680: Eliminate hook_field_extra_fields() / Redesign field UI architecture
Comments
Comment #1
joachim CreditAttribution: joachim commentedI really like this idea.
I'm not sure I entirely understand this bit:
> I intentionally want to split the behavior from its configuration UI.
As I see it, what a behaviour system needs to do is:
- store that a particular behaviour type is present on entity X bundle Y
- store the options for that instance of the behaviour type
- let the behaviour type add something to the entity form
Which to me looks very much like how field types and field instances work. Just without the actual field database table.
I might see about doing something around this on D7, as I've been pondering how to make http://drupal.org/project/fragment more flexible.
Comment #2
donquixote CreditAttribution: donquixote commented#1:
Example:
You want a behavior that gives "karma" to a user every time he/she views a node.
You want this to be configurable per node type.
Later you want the same behavior to apply to commerce products.
Bundle behavior classes
First, you create a behavior class:
This thing really knows nothing about where the $increment is configured, and why or how it is subscribed to those bundle events.
It does not need to think about "am I really enabled for this bundle?". Because if it wasn't, the subscriber method would not fire.
As if this wasn't good enough, you decide to add an alternative implementation, which has a different formula for the karma:
This behavior does not even have constructor arguments.
Bundle option code
Now, separate from the behavior classes, you create the option code.
I don't know yet whether this should be one class, or a collection of classes, or hooks, or whatever.
The important part is that it should happen outside of the behavior code.
The option code does this:
- radios where you can choose one of the above karma formulas, or disable the karma counter.
- a number field to define the increment.
The configuration form elements will be displayed in the bundle configuration form, for entity types that have this option.
E.g. it tells the system that it wants to store two numbers per bundle: One for the radios, another for the karma increment.
Entity behavior class
This class does the following:
Note how this behavior class is agnostic about the entity type.
Register the entity behavior
Now you need to tell Drupal about the entity type behavior, and which entities it should be available for.
Of course we may decide not to make this a hook, but something else. Too early to decide that.
Our custom module now has
- no hardcoded entity types outside of hook_entity_type_behaviors().
- no hardcoded bundle names anywhere.
- no complex logic to determine if something applies to a given bundle type.
Comment #3
andypostProbably this feature will go to D9
Comment #4
andypostComment #5
catchWe have hook_ENTITY_TYPE_$op() which allows for a single place to hard-code entity types if needed. There are also third party settings on bundle entity config. Between these I think it's possible to do everything from the OP in 8.x, so closing as 'works as designed'.
Comment #6
donquixote CreditAttribution: donquixote as a volunteer commentedI don't know if what we have today really covers what I was proposing in this issue.
But anyway, I think I rather open a new issue when I have a more concrete idea what I want, and a better way to explain it. I don't even want to read my own lengthy post anymore.