Displaying Sort Links with a Views Area Plugin
Views is a powerful and highly popular feature in most Drupal sites. For today’s blog post, we're going to look at how to create a Views Area plugin that will display sort links at the top of the page.
by
Pasan Gamage
/ 14 March 2019
So what is an Area plugin ?
As per the documentation on Drupal.org, they are plugins governing areas of views, such as header, footer, and empty text.
Task at hand
For our example, we will be using a search view. Let’s say that this view is created to accept full text search of a content type, sort by relevance by default, and also can be able to sort by created date in descending order.
If we stick to the default Drupal behaviour and use it’s exposed forms we will get a form with buttons as shown below.
However, for this task we want to alter the default behaviour and make the form show links instead. The end result of the sort links should look like image below:
In order to achieve this we build this form using an Area plugin.
Let us call this view search, and add these settings via UI:
- Add a field Configure filter criterion: Search: Full text search
Give a path to the view page
Now to add the sort criteria. For this instance we:
- set relevance to be the default sort, and created date to be the optional sort.
And now for the fun part!
The header section is where we need to add the area plugin. It will add logic to render these two sort options and how each link should work on a user click.
In order for us to add the plugin to the header as shown below, let’s jump into start coding.
The Code
Following the rules of Drupal annotation-based plugins, the plugin should reside under the right directory and namespace for an Area plugin. E.g. app/modules/custom/my_search/src/Plugin/views/area
There we can create a PHP class which we will name as SortBy.php
As for any plugin to work area plugin needs the three main ingredients as well.
-
Namespace needs to follow PSR-4 standards and reside in the Drupal\my_module\Plugin\views\area namespace
-
Must use the @ViewsArea annotation
-
Must implement a particular interface or extend the a base class, in this instance the base class is AreaPluginBase
Namespace needs to follow PSR-4 standards
<em class="code">namespace Drupal\my_search\Plugin\views\area;</em>
use Drupal\Core\Url;
use Drupal\views\Plugin\views\area\AreaPluginBase;
Annotation
/**
* Defines an area plugin to display a header sort by option.
*
* @ingroup views_area_handlers
*
* @ViewsArea("my_search_sort_by")
*/Extend the class from AreaPluginBase
class SortBy extends AreaPluginBase {}
To make things a bit easier I will add a few constants in to the play.
// Query parameter of search form.
const KEYWORD_PARAM_NAME = 'search';
// Query parameter created by view for created date field.
const CREATED_DATE_PARAM = 'created_date';
// Query parameter created by view for relevance field.
const RELEVANCE_PARAM = 'search_api_relevance';
// Search view's route name.
const SEARCH_PAGE_ROUTE = 'view.search.page_1';
For this particular example we only need to override the render() method.
/**
* Render the area.
*
* @param bool $empty
* (optional) Indicator if view result is empty or not. Defaults to FALSE.
*
* @return array
* In any case we need a valid Drupal render array to return.
*/
public function render($empty = FALSE) {
// Sort criteria array.
// This will be our render array that will be used to generate the
// desired html.
$sort_links = [];
// Drupal request query.
$request_query = \Drupal::request()->query;
// Default query options for date sort criteria.
$date_options = [
'query' => [
'sort_by' => self::CREATED_DATE_PARAM,
'sort_order' => 'DESC',
'search' => $request_query->get(self::KEYWORD_PARAM_NAME),
],
];
So, now we already have a known route name of the search view which is view.search.page_1 all we need to do is to pass in the $route_parameters, $options values into Url::fromRoute()
// Default query options for relevance sort criteria.
$relevance_options = [
'query' => [
'sort_by' => self::RELEVANCE_PARAM,
'sort_order' => 'DESC',
'search' => $request_query->get(self::KEYWORD_PARAM_NAME),
],
];
These query parameters will be later displayed as below when a search is made from the form and sorted by created date.
http://mysite/search?sort_by=created_date&sort_order=DESC&search=Thomas
// Determine which criteria is currently active.
// Default is set to relevance.
$active_link = self::RELEVANCE_PARAM;
// On search page load we need check if a GET query is passed in
// having key sort_by
if ($request_query->has('sort_by') && $request_query->get('sort_by') === self::CREATED_DATE_PARAM) {
$active_link = self::CREATED_DATE_PARAM;
}
$sort_links = [
[
'title' => 'Relevance',
'link' => Url::fromRoute(self::SEARCH_PAGE_ROUTE, [], $relevance_options),
'active' => $active_link === self::RELEVANCE_PARAM,
],
[
'title' => 'Date',
'link' => Url::fromRoute(self::SEARCH_PAGE_ROUTE, [], $date_options),
'active' => $active_link === self::CREATED_DATE_PARAM,
],
];
// Finally we return our render array
return [
'#theme' => 'cdu_sort_by_links',
'#sort_links' => $sort_links,
// Tell Drupal this varies by url.
'#cache' => [
'contexts' => ['url'],
],
];
// End of render function
}
Important thing to note is the use of Url::fromRoute() to generate the link rather than using a hardcoded /search?some_stuff
This is because of one good reason: if someone goes into the view and decides to change page url from /search to /content/search - the Url:: code will keep working but the hard-coded href="/search..." will not.
At the moment we're hardcoding the view/route name, because this is a one off plugin. If you were building an area plugin that worked for all you could use
$this->displayHandler and $this->view to dynamically derive the route name and support any view.
Now our plugin is ready, but it is not discoverable by Drupal. So we need to add:
app/modules/custom/my_search/my_search.views.inc file and add below code under hook_views_data_alter()
function my_search_views_data_alter(array &$data) {
$data['views']['my_search'] = [
'title' => t('Sort by'),
'help' => t('Provides sort by option.'),
'area' => [
'id' => 'my_search_sort_by',
],
];
return $data;
}
We need to add this because views area plugins are a bit odd. There is a core issue to try and make them behave like normal plugins.
After adding this, we are all set.
Now we can add Sort by plugin under view header.
Final outcome
On the search page you should see the sort links according to the applied theme
When you click on relevance; the url should change into
http://mysite/search?sort_by=relevance&sort_order=DESC
And on sort by date click;
http://mysite/search?sort_by=created_date&sort_order=DESC
References
- https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Url.php/function/Url%3A%3AfromRoute/8.5.x
- https://api.drupal.org/api/drupal/core%21modules%21views%21src%21Plugin%21views%21area%21AreaPluginBase.php/group/views_area_handlers/8.5.x
Tagged