Drupal 8 Views: Entity Reference Exposed Filter as a Select List
I have two content types: Art and Artist. The first content type, Art, has an entity reference field to the second content type, Artist. I have a view named “Art” which shows all Art content with an exposed “Artist” filter that lets a user pare down their results. For example, a user might use the “Artist” filter to only show art by Cleon Peterson. By default this exposed filter will be rendered by Views as an empty text input, which is pretty much entirely useless! Users may not know of Cleon Peterson and wouldn’t know to search for him.
A much better solution would be to show the options available for this filter as a select list.
This is exactly the problem I was faced with while working on The Octopus Initiative, a soon-to-launch Drupal 8 project by the Museum of Contemporary Art Denver that allows citizens of Denver the opportunity to take art from the museum home with them.
The solution
Let’s jump into the code. You’ll need to either create a new module or add the following code to the .module
file of an existing one. I created a new module called “MCA Artwork” and placed it in my project’s modules/custom directory. My file structure looks like this:
- mca_artwork
- mca_artwork.info.yml
- mca_artwork.module
Here’s my mca_artwork.info.yml
:
name: MCA Artwork
type: module
description: Customizes Artwork Display
core: 8.x
package: Custom
And here’s the mca_artwork.module
file, where the magic happens:
<?php
/**
* @file
* Contains mca_artwork.module.
*/
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_form_FORM_ID_alter().
*
* Alters the artist options on artwork pages.
*/
function mca_artwork_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// If not the view we are looking, move on
if ($form['#id'] != 'views-exposed-form-the-art-block-1') {
return FALSE;
}
// Query nodes
$storage = Drupal::getContainer()->get('entity_type.manager')->getStorage('node');
$nids = $storage->getQuery();
// Gather published artist nodes and sort by title
$nids = $nids->condition('type', 'artist')
->condition('status', 1)
->sort('title')
->execute();
// If there are no nodes, move on
if (!$nids) {
return FALSE;
}
// Start building out the options for our select list
$options = [];
$nodes = $storage->loadMultiple($nids);
// Push titles into select list
foreach ($nodes as $node) {
$options[$node->id()] = $node->getTitle();
}
// Start building out our new form element
$artist_field = 'artist';
$form[$artist_field]['#type'] = 'select';
$form[$artist_field]['#multiple'] = FALSE;
// Specify the empty option for our select list
$form[$artist_field]['#empty_option'] = t('Artist');
// Add the $options from above to our select list
$form[$artist_field]['#options'] = $options;
unset($form[$artist_field]['#size']);
}
If you read through the comments in the above code, you’ll see we are essentially doing the following things:
- We load all published artist nodes, sorted by name
- We create an array of Artist names keyed by node id. These will be our select list options.
- We change the existing artist form input to a select list and populate it with our new options array.
It turns out this is a common UX need in Drupal 8 Views. My coworker, John Ferris, also ran across this problem for a recently-launched Drupal 8 project he worked on for the Center for Court Innovation, a non-profit seeking to to create positive reforms in the criminal justice system. The code snippet for The Octopus Initiative was largely adapted from his work on the Center for Court Innovation.
For the Center for Court Innovation site, the Chosen JS library was added to provide an interface for searching through a larger list of items.
In summary, the module I created for The Octopus Initiative provides a useful UX over what Drupal Views offers out-of-the-box. If you have a larger number of items in your select list, then you may consider adding something like Chosen JS to make it easier to sort through, as was done for the Center for Court innovation. Whatever you do, don't leave your users stranded with an empty text element!