3

I'm trying to run custom javascript functions at the same time as people interact with certain flags on my site, I have followed this tutorial on how to respond to both Flag/Unflag actions, and this site for how to setup the custom ajax responses, except it just doesn't appear to be able to combine the two

The code inside my FlagSubscriber.php file (from the first tutorial link) looks like the below

class FlagController implements EventSubscriberInterface {

    public function onFlag(FlaggingEvent $event) {    
      dpm('Test Text');

      $response = new AjaxResponse();
      $response->addCommand(
        new ExampleCommand()
      );
      return $response;
    } 

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents() {
      return [
        FlagEvents::ENTITY_FLAGGED => [['onFlag']]
      ];
    }
  }

It runs the dpm text the next time the page is loaded, but it doesn't seem to be running my custom Ajax response at all, even if I try running a standard Ajax Response from here. Are there some places that you can't run these kind of Ajax Response requests?

EDIT

For those wondering how to do the same thing, look into what the accepted response mentions about altering the route, because you basically need to overwrite how flag does its job and add your code as an extension of it.

So, I have a RouteSubscriber.php file with the following

namespace Drupal\my_module\Routing;

  use Drupal\Core\Routing\RouteSubscriberBase;
  use Symfony\Component\Routing\RouteCollection;

  /**
   * Listens to the dynamic route events.
   */
  class RouteSubscriber extends RouteSubscriberBase {

    /**
     * {@inheritdoc}
     */
    protected function alterRoutes(RouteCollection $collection) {
      // We need to take control of the default methods for the Flag controller so that we can hook into it's actions and add our own
      if ($route = $collection->get('flag.action_link_flag')) {
        $route->setDefault('_controller', 'Drupal\my_module\Controller\FlagController::flag');
      }
      if ($route = $collection->get('flag.action_link_unflag')) {
        $route->setDefault('_controller', 'Drupal\my_module\Controller\FlagController::unflag');
      }
    }
  }

Then I have a FlagController.php with code such as

namespace Drupal\my_module\Controller;

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\flag\FlagInterface;
use Drupal\flag\Controller\ActionLinkController;

/**
 * Controller responses to flag and unflag action links.
 *
 * The response is a set of AJAX commands to update the
 * link in the page.
 */
class FlagController extends ActionLinkController {
  /**
   * Performs a flagging when called via a route.
   *
   * @param FlagInterface $flag
   *   The flag entity.
   * @param int $entity_id
   *   The flaggable entity ID.
   *
   * @return AjaxResponse|null
   *   The response object, only if successful.
   **/
  public function flag(FlagInterface $flag, $entity_id) {
    $response = parent::flag($flag, $entity_id);
    // The line above means your code still does what Flag needs to do, so you need to keep this in, or you'll stop flag working
    
    // Now you can add your own commands based on this link, like below
    https://www.drupal.org/docs/drupal-apis/ajax-api/core-ajax-callback-commands
    $response->addCommand(new EntityAddCommand($foo, $bar, $baz));

    //'EntityAddCommand' is something I wrote in my own javascript function

    return $response;
  }

  /**
   * Performs a unflagging when called via a route.
   *
   * @param FlagInterface $flag
   *   The flag entity.
   * @param int $entity_id
   *   The flaggable entity ID.
   *
   * @return AjaxResponse|null
   *   The response object, only if successful.
   **/
  public function unflag(FlagInterface $flag, $entity_id) {
    $response = parent::unflag($flag, $entity_id);

    //.... etc, do your own commands for unflagging

    return $response;
  }

1 Answer 1

2

The flagging events don't deal with responses, so you won't be able to return one from there. A different approach is to create a route subscriber, and alter the flag.action_link_flag and flag.action_unlink_flag routes, replacing the controller with your own custom one, e.g.

protected function alterRoutes(RouteCollection $collection) {
  if ($route = $collection->get('flag.action_link_flag')) {
    $route->setDefault('_controller', 'Drupal\custom_module\Controller\CustomController::flag');
  }
  if ($route = $collection->get('flag.action_link_flag')) {
    $route->setDefault('_controller', 'Drupal\custom_module\Controller\CustomController::unflag');
  }
}

Your custom controller would extend the original flag controller (Drupal\flag\Controller\ActionLinkController), use its methods to generate the original response, and then just tack your JS command(s) onto the end.

class CustomController extends ActionLinkController {

  public function flag(FlagInterface $flag, $entity_id) {
    $response = parent::flag($flag, $entity_id);
    $response->addCommand(...);
    return $response;
  }

  // Same for unflag()

}

For full coverage, you might also need to do something similar to the flag.action_link_flag_nojs and flag.action_link_unflag_nojs routes. But if your functionality is purely JS and doesn't need to be present for users without it, probably no need to bother.

1
  • Absolutely amazing, I've just setup some new custom AJAX triggers that are firing as soon as I press the button, which is exactly what I was after. Thank you very much :D Jan 6, 2022 at 17:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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