{"id":61670,"date":"2024-05-30T13:09:59","date_gmt":"2024-05-30T07:39:59","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=61670"},"modified":"2024-05-30T14:25:14","modified_gmt":"2024-05-30T08:55:14","slug":"how-build-custom-views-argument-plugins-drupal","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/how-build-custom-views-argument-plugins-drupal\/","title":{"rendered":"How to Build Custom Views Argument Plugins in Drupal"},"content":{"rendered":"<h2><span style=\"color: #000000;\">Introduction<\/span><\/h2>\n<p><span style=\"color: #000000;\">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.<\/span><\/p>\n<p><span style=\"color: #000000;\"><strong>Scenario:<\/strong> In most websites, it&#8217;s standard practice to display URLs in a clean format, such as &#8220;\/blog\/skys-limit-indias-growing-aviation-industry&#8221;, rather than using generic formats like &#8220;node\/34&#8221;. To achieve clean URLs within Drupal, the Pathauto module comes in handy for configuring and generating URLs.<\/span><\/p>\n<p><span style=\"color: #000000;\">Let&#8217;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.<\/span><\/p>\n<p><span style=\"color: #000000;\">&#8216;For instance, if our API URL is structured as follows: <em>{base_url}\/api\/v1\/read\/blog-detail<\/em><\/span><\/p>\n<p><span style=\"color: #000000;\">where our view page path is &#8216;<em>\/api\/v1\/read\/blog-detail<\/em>,&#8217; we need to pass the alias in the view to retrieve the node detail.<\/span><\/p>\n<p><span style=\"color: #000000;\">To achieve this, I created a views argument plugin named &#8216;<strong>Entity ID by Alias<\/strong>&#8216; and called the path as follows:<\/span><\/p>\n<p><span style=\"color: #000000;\"><em>{base_url}\/api\/v1\/read\/blog-detail?alias=\/blog\/skys-limit-indias-growing-aviation-industry<\/em><\/span><\/p>\n<div id=\"attachment_61687\" style=\"width: 1727px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-61687\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-61687 size-full\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal.png\" alt=\"new plugin\" width=\"1717\" height=\"802\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal.png 1717w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal-300x140.png 300w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal-1024x478.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal-768x359.png 768w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal-1536x717.png 1536w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal-624x291.png 624w\" sizes=\"(max-width: 1717px) 100vw, 1717px\" \/><p id=\"caption-attachment-61687\" class=\"wp-caption-text\"><\/span> <span style=\"color: #000000;\">new plugin type created by code<\/span><\/p><\/div>\n<p><span style=\"color: #000000;\"><strong>Assumptions:<\/strong><\/span><\/p>\n<ul>\n<li style=\"text-align: left;\"><span style=\"color: #000000;\">You should have a good understanding of Drupal Views, particularly with contextual filters.<\/span><\/li>\n<li style=\"text-align: left;\"><span style=\"color: #000000;\">You should be familiar with creating custom modules in Drupal.<\/span><\/li>\n<li style=\"text-align: left;\"><span style=\"color: #000000;\">You should understand annotations and how to write plugins within Drupal.<\/span><\/li>\n<\/ul>\n<h2><span style=\"color: #000000;\">Step 1: Create a new custom module<\/span><\/h2>\n<p><span style=\"color: #000000;\">Create a custom module named &#8216;app_rest_api,&#8217; or utilize any other existing custom module.<\/span><\/p>\n<h2><span style=\"color: #000000;\">Step 2: Create a new Plugin<\/span><\/h2>\n<p><span style=\"color: #000000;\">Now, we need to create a new plugin type, @ViewsArgumentDefault. To do this, we&#8217;ll create a file named EntityIdByAlias.php in the following directory:<\/span><\/p>\n<p><span style=\"color: #000000;\">app_rest_api\/src\/Plugin\/views\/argument_default\/EntityIdByAlias.php<\/span><\/p>\n<p><span style=\"color: #000000;\">We can use the below annotation to generate the viewsArgumentDefault Plugin<\/span><\/p>\n<pre><span style=\"color: #000000;\">\/**\r\n* Default argument plugin to fetch Entity ID by Alias from the URL.\r\n*\r\n* @ViewsArgumentDefault(\r\n* id = \"entityid_by_alias\",\r\n* title = @Translation(\"Entity ID by Alias\")\r\n* )\r\n*\/<\/span><\/pre>\n<p><span style=\"color: #000000;\">So the final code looks like this:<\/span><\/p>\n<pre><span style=\"color: #000000;\">&lt;?php\r\n\r\nnamespace Drupal\\app_rest_api\\Plugin\\views\\argument_default;\r\n\r\nuse Drupal\\Core\\Cache\\Cache;\r\nuse Drupal\\Core\\Cache\\CacheableDependencyInterface;\r\nuse Drupal\\Core\\Routing\\RouteMatchInterface;\r\nuse Drupal\\views\\Plugin\\views\\argument_default\\ArgumentDefaultPluginBase;\r\nuse Symfony\\Component\\DependencyInjection\\ContainerInterface;\r\nuse Symfony\\Component\\HttpFoundation\\RequestStack;\r\n\r\n\/**\r\n * Default argument plugin to fetch Entity ID by Alias from the URL.\r\n *\r\n * @ViewsArgumentDefault(\r\n *   id = \"entityid_by_alias\",\r\n *   title = @Translation(\"Entity ID by Alias\")\r\n * )\r\n *\/\r\nclass EntityIdByAlias extends ArgumentDefaultPluginBase implements CacheableDependencyInterface {\r\n\r\n  \/**\r\n   * The request stack.\r\n   *\r\n   * @var \\Symfony\\Component\\HttpFoundation\\RequestStack\r\n   *\/\r\n  protected $requestStack;\r\n\r\n  \/**\r\n   * Constructs a Raw object.\r\n   *\r\n   * @param array $configuration\r\n   *   A configuration array containing information about the plugin instance.\r\n   * @param string $plugin_id\r\n   *   The plugin_id for the plugin instance.\r\n   * @param mixed $plugin_definition\r\n   *   The plugin implementation definition.\r\n   * @param \\Symfony\\Component\\HttpFoundation\\RequestStack $request_stack\r\n   *   The request stack.\r\n   *\/\r\n  public function __construct(array $configuration, $plugin_id, $plugin_definition, RequestStack $request_stack, RestApiService $aliasResolver) {\r\n    parent::__construct($configuration, $plugin_id, $plugin_definition);\r\n    $this-&gt;requestStack = $request_stack;\r\n    $this-&gt;AliasResolver = $aliasResolver;\r\n  }\r\n\r\n\/**\r\n   * {@inheritdoc}\r\n   *\/\r\n  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {\r\n    return new static(\r\n      $configuration,\r\n      $plugin_id,\r\n      $plugin_definition,\r\n      $container-&gt;get('request_stack'),\r\n      $container-&gt;get('demoapp_rest_api.alias_manager')\r\n    );\r\n  }\r\n\r\n \/**\r\n * {@inheritdoc}\r\n *\/\r\n  protected function defineOptions() {\r\n    $options = parent::defineOptions();\r\n    return $options;\r\n  }\r\n\r\n  \/**\r\n   * {@inheritdoc}\r\n   *\/\r\n  public function getArgument() {\r\n    \/\/ Get the alias from the URL.\r\n    $alias = $this-&gt;requestStack-&gt;getCurrentRequest()-&gt;query-&gt;get('alias');\r\n    \/\/ Get Nid from the URL.\r\n    $nid = $this-&gt;AliasResolver-&gt;getNidByAlias($alias);\r\n    \r\n    if (!empty($nid)) {\r\n      return $nid;\r\n    }\r\n    return FALSE;\r\n  }\r\n\r\n  \/**\r\n   * Resolves a URL alias to a node ID.\r\n   *\r\n   * @param string $alias\r\n   *   The URL alias to resolve.\r\n   *\r\n   * @return int|null\r\n   *   The node ID corresponding to the alias, or NULL if not found.\r\n   *\/\r\n  public function getNidByAlias($alias) {\r\n    $path = \\Drupal::service('path.alias_manager')-&gt;getPathByAlias($alias);\r\n    if (preg_match('\/^\\\/node\\\/(\\d+)$\/', $path, $matches)) {\r\n      return (int) $matches[1];\r\n    }\r\n    return NULL;\r\n  }\r\n\r\n  \/**\r\n   * {@inheritdoc}\r\n   *\/\r\n  public function getCacheMaxAge() {\r\n    return Cache::PERMANENT;\r\n  }\r\n\r\n  \/**\r\n  * {@inheritdoc}\r\n  *\/\r\n  public function getCacheContexts() {\r\n    return ['url', 'url.query_args'];\r\n  }\r\n}<\/span><\/pre>\n<p><span style=\"color: #000000;\">Don&#8217;t forget to flush the cache after writing a new plugin.<\/span><\/p>\n<p><span style=\"color: #000000;\">Here, we have created a <code>getArgument<\/code> 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).<\/span><\/p>\n<p><span style=\"color: #000000;\">You should add your logic to convert the alias to the NID in the <code>getNidByAlias<\/code> function.<\/span><\/p>\n<h2><span style=\"color: #000000;\">Step 3: Implement it in the view<\/span><\/h2>\n<p><span style=\"color: #000000;\">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 &#8220;Provide default value.&#8221; Select the newly created plugin type, which in our case is &#8220;Entity ID by Alias.&#8221;<\/span><\/p>\n<p><span style=\"color: #000000;\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-61704 size-large\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2024\/05\/blog-contextual-filter-1024x435.png\" alt=\"blog contextual filter\" width=\"625\" height=\"266\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2024\/05\/blog-contextual-filter-1024x435.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/blog-contextual-filter-300x128.png 300w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/blog-contextual-filter-768x327.png 768w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/blog-contextual-filter-624x265.png 624w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/blog-contextual-filter.png 1190w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><\/span><\/p>\n<p>&nbsp;<\/p>\n<div id=\"attachment_61687\" style=\"width: 635px\" class=\"wp-caption aligncenter\"><img aria-describedby=\"caption-attachment-61687\" decoding=\"async\" loading=\"lazy\" class=\"wp-image-61687 size-large\" src=\"https:\/\/www.tothenew.com\/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal-1024x478.png\" alt=\"new plugin\" width=\"625\" height=\"292\" srcset=\"\/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal-1024x478.png 1024w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal-300x140.png 300w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal-768x359.png 768w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal-1536x717.png 1536w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal-624x291.png 624w, \/blog\/wp-ttn-blog\/uploads\/2024\/05\/Blog-Detail-API-Content-Demo-Application-Drupal.png 1717w\" sizes=\"(max-width: 625px) 100vw, 625px\" \/><p id=\"caption-attachment-61687\" class=\"wp-caption-text\"><\/span> <span style=\"color: #000000;\">New plugin type to convert the the value from alias to id<\/span><\/p><\/div>\n<h2><\/h2>\n<h2><span style=\"color: #000000;\">Conclusion:<\/span><\/h2>\n<p><span style=\"color: #000000;\">We can create various argument types within the contextual filter to filter the data based on the received argument. Once we&#8217;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.<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>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. [&hellip;]<\/p>\n","protected":false},"author":1509,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":252},"categories":[3602],"tags":[4862,5253,5152,2162],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/61670"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/1509"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=61670"}],"version-history":[{"count":23,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/61670\/revisions"}],"predecessor-version":[{"id":62084,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/61670\/revisions\/62084"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=61670"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=61670"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=61670"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}