How to Build Custom Views Argument Plugins in Drupal

30 / May / 2024 by Dharmendra Singh 0 comments

Introduction

Drupal Views has a robust feature for filtering content based on any field using contextual filters. Contextual filters come with various filter types, such as Content ID from URL, query parameter, raw value from URL, and more. Additionally, you can create custom filter types if the existing ones do not meet your requirements perfectly.

Scenario: In most websites, it’s standard practice to display URLs in a clean format, such as “/blog/skys-limit-indias-growing-aviation-industry”, rather than using generic formats like “node/34”. To achieve clean URLs within Drupal, the Pathauto module comes in handy for configuring and generating URLs.

Let’s consider a scenario where we are working with a Headless CMS. When retrieving details of any node from the front end, we can easily pass the node ID (NID) to fetch the data from a view. However, the front end often uses the clean URL alias of the node instead. In such cases, we need to retrieve the node details using the alias.

‘For instance, if our API URL is structured as follows: {base_url}/api/v1/read/blog-detail

where our view page path is ‘/api/v1/read/blog-detail,’ we need to pass the alias in the view to retrieve the node detail.

To achieve this, I created a views argument plugin named ‘Entity ID by Alias‘ and called the path as follows:

{base_url}/api/v1/read/blog-detail?alias=/blog/skys-limit-indias-growing-aviation-industry

new plugin

new plugin type created by code

Assumptions:

  • You should have a good understanding of Drupal Views, particularly with contextual filters.
  • You should be familiar with creating custom modules in Drupal.
  • You should understand annotations and how to write plugins within Drupal.

Step 1: Create a new custom module

Create a custom module named ‘app_rest_api,’ or utilize any other existing custom module.

Step 2: Create a new Plugin

Now, we need to create a new plugin type, @ViewsArgumentDefault. To do this, we’ll create a file named EntityIdByAlias.php in the following directory:

app_rest_api/src/Plugin/views/argument_default/EntityIdByAlias.php

We can use the below annotation to generate the viewsArgumentDefault Plugin

/**
* Default argument plugin to fetch Entity ID by Alias from the URL.
*
* @ViewsArgumentDefault(
* id = "entityid_by_alias",
* title = @Translation("Entity ID by Alias")
* )
*/

So the final code looks like this:

<?php

namespace Drupal\app_rest_api\Plugin\views\argument_default;

use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Default argument plugin to fetch Entity ID by Alias from the URL.
 *
 * @ViewsArgumentDefault(
 *   id = "entityid_by_alias",
 *   title = @Translation("Entity ID by Alias")
 * )
 */
class EntityIdByAlias extends ArgumentDefaultPluginBase implements CacheableDependencyInterface {

  /**
   * The request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected $requestStack;

  /**
   * Constructs a Raw 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 \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, RequestStack $request_stack, RestApiService $aliasResolver) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->requestStack = $request_stack;
    $this->AliasResolver = $aliasResolver;
  }

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

 /**
 * {@inheritdoc}
 */
  protected function defineOptions() {
    $options = parent::defineOptions();
    return $options;
  }

  /**
   * {@inheritdoc}
   */
  public function getArgument() {
    // Get the alias from the URL.
    $alias = $this->requestStack->getCurrentRequest()->query->get('alias');
    // Get Nid from the URL.
    $nid = $this->AliasResolver->getNidByAlias($alias);
    
    if (!empty($nid)) {
      return $nid;
    }
    return FALSE;
  }

  /**
   * Resolves a URL alias to a node ID.
   *
   * @param string $alias
   *   The URL alias to resolve.
   *
   * @return int|null
   *   The node ID corresponding to the alias, or NULL if not found.
   */
  public function getNidByAlias($alias) {
    $path = \Drupal::service('path.alias_manager')->getPathByAlias($alias);
    if (preg_match('/^\/node\/(\d+)$/', $path, $matches)) {
      return (int) $matches[1];
    }
    return NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheMaxAge() {
    return Cache::PERMANENT;
  }

  /**
  * {@inheritdoc}
  */
  public function getCacheContexts() {
    return ['url', 'url.query_args'];
  }
}

Don’t forget to flush the cache after writing a new plugin.

Here, we have created a getArgument function that accepts the current argument from the URL, which in our case is the alias, and converts it to the corresponding node ID (NID).

You should add your logic to convert the alias to the NID in the getNidByAlias function.

Step 3: Implement it in the view

Open the view and navigate to the contextual filter in the advanced settings on the right sidebar. Add a new filter type, such as the ID of the node. Configure the ID and choose “Provide default value.” Select the newly created plugin type, which in our case is “Entity ID by Alias.”

blog contextual filter

 

new plugin

New plugin type to convert the the value from alias to id

Conclusion:

We can create various argument types within the contextual filter to filter the data based on the received argument. Once we’ve added our logic, we can convert this argument into any other form of IDs required by the contextual filter. This approach can resolve many scenarios where we encounter challenges in filtering the data. Additionally, it can facilitate the construction of logic based on the received argument, enabling us to produce the desired output.

FOUND THIS USEFUL? SHARE IT

Leave a Reply

Your email address will not be published. Required fields are marked *