In GenericEventSubscriber::onRulesEvent() we have the following @todo:

  // @todo Add support for the getter method.
  // @see https://www.drupal.org/project/rules/issues/2762517

Currently, Rules can make use any type of Symfony Event, provided that event has public visibility properties. Defining metadata in a MODULE.rules.events.yml exposes these events to Rules and declares the event properties that Rules may access as context variables.

Rules can also use Symfony Events with no properties (e.g. KernelEvents::REQUEST), or use events that have protected/private properties as long as Rules does not need to add those protected/private properties as context variables.

Rules can also make use of Symfony GenericEvents using the methods on GenericEvent intended for accessing properties.

What Rules cannot do currently is to use Symfony Events where the properties are protected or private and need to be accessed as context variables.

The work in this issue before comment #13 attempted to extend the current Rules capability, and make it so Rules could access some protected and private properties - the approach taken was to search for getter methods for these protected/private properties, and to use these methods (if found) to access the protected/private properties. The problem with this approach is that it relies on naming conventions to locate the getters. So for example if the event had a property name 'element', the code would look for a 'getElement' method to access that property. While this will work in some cases, when code follows the "expected" naming conventions, it won't work in all other cases (e.g. when a property is snake_cased and the getter is camelCased). And while this is better than nothing, it is not a general solution and will only marginally improve the current situation.

The proposal in #13, and the patches in the comments since then, take a more general and useful approach. This new approach allows Rules to access any property with any visibility in any Event, AND allows for modules to override the Rules default. The way this works is to let the getter method be declared explicitly in the metadata MODULE.rules.events.yml, if desired. Because the getter method may be (but does not need to be) defined in the metadata, this getter method may be added or altered by any module, by implementing the existing hook_rules_event_info_alter() hook. Likewise, if no getter method is specified, Rules will still be able to access any public/protected/private property on the event as long as the context variable name in the metadata matches the property name declared in the Event class.

This means, with the current patch (currently in #24), Rules will be able to use ANY event dispatched by Symfony, Drupal core, or any Drupal contributed module. This opens up a whole new level of Rules integration that wasn't previously available. For example, the core Migrate module defines 11 different events that were not usable by Rules because their properties have protected visibility. But with this patch, all these events are now completely usable (and useful!) - all that needs to be done is to write a few lines of metadata in a MODULE.rules.events.yml to expose them for use by Rules.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

asgorobets created an issue. See original summary.

asgorobets’s picture

Title: Allow context definition to extract via Event getters » Allow GenericEventSubscriber::onRulesEvent to extract contexts via Event getters
Status: Active » Needs review
FileSize
1.28 KB
daria.a’s picture

I had the same problem. This patch solved it.

daria.a’s picture

Status: Needs review » Reviewed & tested by the community
dragos-dumi’s picture

Hi,
The patch worked for me but with a small modification. The getter method it's not called as a method, but as property. Shouldn't be like in the interdiff attached?

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 5: interdiff.diff, failed testing.

asgorobets’s picture

You are correct dragos.dumitrescu! Thanks for pointing this out, not sure how I missed it. I'll update the patch

asgorobets’s picture

Attaching the updated patch

asgorobets’s picture

Status: Needs work » Needs review

Attaching the updated patch

asgorobets’s picture

fago’s picture

Status: Needs review » Needs work

Thanks, the patch looks good. A remark though:

+++ b/src/EventSubscriber/GenericEventSubscriber.php
@@ -112,15 +112,23 @@ class GenericEventSubscriber implements EventSubscriberInterface {
+      elseif (isset($event->{lcfirst(ucwords($context_name, '_'))})) {

I do not think we should do magic conversion of the context name for variables. Either it fits, or you have to use the method.

TommyChris’s picture

I created a less complex patch.

dbiscalchin’s picture

Hi,

My suggestion is to allow a "getter" property to be provided in the YAML like so:

my_module.example:
  label: 'After something happens'
  category: 'Example'
  context:
    user:
      type: 'entity:user'
      label: 'User'
      getter: 'loadUser'

We could still check if a method with the standard name exists when this property is missing, but I think explicitly providing the method makes it much more flexible - one wouldn't have to wrap a third-party event just because it did not follow naming conventions.

It also has the advantage of decoupling the name exposed to data selection ("user" in this example) from the Event's class implementation (the method name). This would be useful in cases the getter has a generic name like "getEntity" as in the example below:

my_module.other_example:
  label: 'After something else happens'
  category: 'Example'
  context:
    article:
      type: 'entity:node'
      label: 'Article'
      getter: 'getEntity'
TR’s picture

Priority: Normal » Major
Status: Needs work » Active

@dbiscalchin: I think that sounds like a good idea. IMO it has advantages over what has been proposed earlier in this thread, and like you said it affords much more flexibility in how event classes work. Additionally, because the means of accessing a context variable would be part of the plugin definition, it could be altered using the plugin manager's hook_rules_event_alter().

Can you create a patch to do this?

I think this is very important for integration with contributed modules, where event properties tend to be protected so we currently can't just add the .yml and make use of those events.

TR’s picture

This patch implements the idea from #13.

Additionally, this patch allows access to Event properties when those properties are declared as protected or private - this has always been a problem that prevented Rules from making use of Events defined by other modules, because most Event objects don't use public visibility for their properties.

And of course, tests are included to ensure this works.

Event properties are accessed as follows:

  1. Rules will first use the getter method if it is defined in the MODULE.rules.events.yml file. This allows overriding the default behavior.
  2. If there is no getter, Rules will check the Event class to see if it is a subclass of GenericEvent. If so, Rules can access the property values through the getArguments() method.
  3. Finally, if there is no getter method and the Event is not a GenericEvent, Rules will peek into the class and access the properties by name, regardless of whether they are public, protected, or private.

This should finally allow us to use any third-party event directly from Rules, simply by adding the necessary metadata into a MODULE.rules.events.yml file.

Status: Needs review » Needs work

The last submitted patch, 15: 2762517-15-getters.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

TR’s picture

Status: Needs work » Needs review
FileSize
19.74 KB

Remove commented out code and clean up coding standards.

Status: Needs review » Needs work

The last submitted patch, 17: 2762517-17-getters.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

TR’s picture

Status: Needs work » Needs review
FileSize
19.84 KB

Some more coding standards, fix plugin discovery in Unit tests (hopefully).

TR’s picture

TR’s picture

Component: Rules Core » Events
TR’s picture

Added a bunch of comments.

Also re-wrote the issue summary.

TR’s picture

Issue summary: View changes
TR’s picture

The patch in #22 adds to the 8 branch failures in the D10 tests (see #3259445: Symfony\Component\EventDispatcher\Event is deprecated in drupal:9.1.0 for the cause of these branch failures). Let's try to avoid making the situation worse.

Because this is just a test, we can afford to remove the explicit Event type hint in order to make the code run in both D9 and D10.

Note: This patch contains a test module named 'rules_test_event', which it primarily intended for use in test cases. But you may also enable this test module using drush if you first add the line $settings['extension_discovery_scan_tests'] = TRUE; somewhere in your settings.php. Once you enable this module, you will have access to some test events (in the 'Tests' category) and you may use these test events to trigger any Rules you like. There is also a test page defined at /dispatch-form that allows you to manually dispatch these test events from the browser. This is very useful for manual testing!

TR’s picture

#24 fixes the two D10 failures caused by the code in this patch. It does not fix two failures that come from the same problem as in #3259445: Symfony\Component\EventDispatcher\Event is deprecated in drupal:9.1.0 - those can't be avoided in this patch.

In summary, I think #24 is ready to commit (the two coding standards warnings will be fixed on commit, to keep from cluttering this issue with yet another patch).

TR’s picture

Issue tags: +beta blocker

  • TR committed e26bc68 on 8.x-3.x
    Issue #2762517 by TR, dbiscalchin: Allow GenericEventSubscriber::...
TR’s picture

Category: Task » Feature request
Status: Needs review » Fixed

Committed #24. This patch adds new capability without changing the existing capability, so I don't have any BC concerns. There are plenty of new tests added to ensure the new capability works properly.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.