Drupal 8 - Add redirect destination in action button

Use case

By default, whenever you add some content, Drupal redirects you to that content page. But sometimes you might want to go back to the previous page after adding the content, instead of the content page.

Consider a page where you are listing all songs of an album, with an action button to add a new song as well. And after adding a new song, you want to redirect back to this listing page.

Solution

As stated in documentation, you can customize local action with your own class that must extend from Drupal\Core\Menu\LocalActionDefault. That means you can override methods like getOptions and provide your own dynamic options for local action.

music.song_add:
  route_name: music.song_add
  title: 'Add song'
  route_parameters:
    node_type: 'song'
  class: \Drupal\music\Plugin\Menu\LocalAction\SongAdd
  appears_on:
    - music.song_list

Drupal uses \Drupal\Core\Routing\RedirectDestination to provide redirect destination functionality. in LocalActionDefault, RedirectDestination is not available by default, so we need to inject it somehow.

Following is the one way to do it.

namespace Drupal\music\Plugin\Menu\LocalAction;

use Drupal\Core\Menu\LocalActionDefault;
use Drupal\Core\Routing\RedirectDestinationInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class SongAdd extends LocalActionDefault {
  protected $redirectDestination;
  
  public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, RedirectDestinationInterface $redirect_destination) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider);

    $this->redirectDestination = $redirect_destination;
  }

  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('router.route_provider'),
      $container->get('redirect.destination')
    );
  }

}

Once RedirectDestination is available, we can set destination query parameter in getOptions method like following.

namespace Drupal\music\Plugin\Menu\LocalAction;

use Drupal\Core\Menu\LocalActionDefault;
use Drupal\Core\Routing\RedirectDestinationInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Modifies the 'Add link' local action to add a destination.
 */
class SongAdd extends LocalActionDefault {

  /**
   * The redirect destination.
   *
   * @var \Drupal\Core\Routing\RedirectDestinationInterface
   */
  protected $redirectDestination;

  /**
   * Constructs a object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
   *   The route provider to load routes by name.
   * @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
   *   The redirect destination.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, RedirectDestinationInterface $redirect_destination) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider);

    $this->redirectDestination = $redirect_destination;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('router.route_provider'),
      $container->get('redirect.destination')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getOptions(RouteMatchInterface $route_match) {
    $options = parent::getOptions($route_match);
    // Append the current path as destination to the query string.
    $options['query']['destination'] = $this->redirectDestination->get();
    return $options;
  }

}

  PHP, Drupal 8