0

in my custom module I have an event subscriber service for flag module. I used the tutorial here as a base for learning: Link to Tutorial

Here is my code:

namespace Drupal\MYMODULE\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\flag\Event\FlagEvents;
use Drupal\flag\Event\FlaggingEvent;
use Drupal\flag\Event\UnflaggingEvent;
use Drupal\user\UserInterface;
use Drupal\user\Entity\User;

class MYMODULEFlagSubscriber implements EventSubscriberInterface {

  /**
   * Do the logic when the upgrade flag is triggered to flag.
   */
  public function onFlag(FlaggingEvent $event) {
    $flagging = $event->getFlagging();
    $flag = $flagging->getFlag();
    $flag_machine_name = $flag->id();
    if ($flag_machine_name === 'my_awesome_flag') {
      $entity_nid = $flagging->getFlaggable()->id();
      $user = User::load($entity_nid);
      $user_is_active = $user->isActive();
      $field_1 = $user->get('field_1')->value;
      $field_2 = $user->get('field_2')->value;
      $break_with_error = FALSE;
      $upgrade_message = '';
      $roles_to_ignore = [
        'administrator_junior',
        'team_sales',
        'administrator',
      ];
      if (!$user->hasRole('authenticated_level_2')) {
        if (($user_is_active == TRUE) && ($field_1 == 1) && ($field_2 == 1)) {
          foreach ($roles_to_ignore as $role_to_ignore) {
            if ($user->hasRole($role_to_ignore)) {
              $break_with_error = TRUE;
              $upgrade_message = 'The User has a company role already and can not being upgraded as a Level 2 user.';
              break;
            }
          }
        }
        else {
          $break_with_error = TRUE;
          $upgrade_message = 'The User can not being upgraded as a Level 2 User. Reasons: 2. The account is not activated yet, 3. The user has not yet checked field 2';
        }
      }
      else {
        $break_with_error = TRUE;
        $upgrade_message = 'The User was upgraded as a Level 2 User already.';
      }

      switch ($break_with_error) {
        case TRUE:
          \Drupal::messenger()->addError($upgrade_message);
          $flagging->unflag($flag_machine_name, $entity_nid);
          return;

        case FALSE:
          $user->addRole('authenticated_level_2');
          $user->save();
          \Drupal::messenger()->addStatus('The User was upgraded as a Level 2 User sucessfully.');
          return;
      }

    }
  }

My Problem is

How can I unflag the flag again, when my logic checks settings and my variable $break_with_error becomes true?

$flagging->unflag($flag_machine_name, $entity_nid);

throws me an PHP error.

Update:

My intention was the following:

I use the flag here as a substitute for a link/button to trigger the "business logic" checks and to give the user a additional role with more privileges if the checks are passed.

In my opinion it's a easy way to quickly have a link/button in multiple variants (e.g. in a view or as a field in user account). Another advantage is, that other people (in my case sales team or other admins) see the business logic checks have performed already when the user entity is flagged. But in case the logic checks are failing, the user entity should unflagged again.

For other opportunities to have a "business logic trigger" I'm open and thankful of course. 🙂

3
  • 1
    What PHP error is thrown? – sonfd Jan 16 at 3:03
  • Does this answer your question? How can I flag content programmatically? – sonfd Jan 16 at 3:35
  • @Patrick Kenny is right, this is the wrong way to solve the problem. You should make it impossible for the flag to be set if your conditions aren't met, rather than undo things after they already happen. – sonfd Jan 16 at 3:40
3

I'm guessing the problem is that in the onFlag event, the flag is still being created, so it can't be unflagged because it hasn't been fully saved yet.

But I think the bigger problem is that your code has a logic error-- validation should happen before the action, not after the action.

It looks like you are trying to do this:

  1. When a user is flagged,
  2. check if the flag meets certain "unflag" conditions,
  3. if the conditions are met, unflag the user.

However, validation in Drupal usually is applied before something happens, not after. Validation is not supposed to be a kill switch to stop a problem; you should stop the problem before it happens.

One reason to stop the problem before it happens is that you later may add code that does something when a node is flagged (for example, in hook_entity_insert()); in such a case, you would also have to undo the code in your flag event subscriber.

So, what you probably want to do is apply the validation login in hook_form_alter() so you can stop the flagging before it happens.

2
  • Thanks for your reply. In my case the flag is more a trigger to perform my business logic checks. I updated my question to be more clear with my intention – dercheffe Jan 16 at 10:25
  • @dercheffe IMO using the flag as a trigger to perform business logic checks more than once is the wrong way to use flag. The reason is because once an entity is flagged, it's more work to unflag it (as you are learning). Drupal core already provides "Actions" to run business rules; instead of using flags, you can define a custom action and then use VBO (Views Bulk Operations) to trigger it: drupal.org/docs/8/core/modules/action/overview – Patrick Kenny Jan 16 at 10:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.