Our client, the League of Women Voters, has many local Leagues across the United States and it was our goal to provide an easy way for users to find their nearest local league by zip code. There were three outcomes we needed to account for:
1) If no zip code was being used, then display a summary list that shows the state name and the number of local leagues within that state. Users could click on a state and find a list of local leagues within that state.
2) If a user entered a zip code, then display the local league with that matching zip code and display the state league.
3) If a user enters a zip code but there is no matching local league, then display all of the local leagues that exist in the same state as the zip code and display the the state league.
Achieving Outcome #1
To start off, we created a view page display that pulled in our local league content type. This content type has a zip code field that allows multiple values. We added this field as an exposed filter in our view.
Then, we added a has taxonomy term id contextual filter. On our local league content type, we have an entity reference field to the state taxonomy vocabulary, so it would be the state term id that would be passed in as a filter value. We set it to display a summary when the filter value is not in the url. This means it will display the state name. We also set it to display a record count, which means it will display the number of local leagues within each state.
After just setting up the view page display, we were able to achieve outcome 1. If we navigate to the url of the view page, which does not include a filter value in the url, it will display a summary list of all the states and the number of local leagues within each state.
If you click on one of the states, it will take you to a url that includes the state term id as a filter value, and will therefore display a filtered list of all of the local leagues within that state.
Also, as you can see, the state league is displaying in the footer region. To achieve this, we created a view block display. In terms of fields and filter criteria, this is similar to the view page display except league type is set equal to state league instead of local league. We also added state as a contextual filter and set the default value equal to has taxonomy term id from url. Then on the page display, we added the state league view block to the footer region using a global view area. So now when a user goes to a page that includes the state term id as a filter value in the url, it will also display the state league in the footer region.
Achieving Outcome #2
As you can see, from just setting up the view, we not only achieved outcome 1 but also achieved a significant portion of outcome 2. The zip code exposed filter allows the user to search by zip code and find a matching local league. The only thing missing is the state league. The reason is because when the user searches by zip code, it takes the user to a url that does not include the state term id as a filter value. So in order to display the state league, we need to set the url to include the state term id as a filter value. To do this, we can use the HOOK_views_pre_view, which allows us to change things before the view is executed.
As the code above suggests, we grab the exposed input value, which is the zip code that the user entered. We take that value and pass it to a function called _get_state_by_zip(). This is a custom function that accepts a zip code value and returns the state term id of the state where the zip code resides in. We put the state term id into the url by setting it equal to args. So now when a user searches by zip code, he/she is taken to a url that includes the state term id, so not only will a matching local league display if one exists, but so will the state league.
Achieving Outcome #3
For outcome 3, when a user enters a zip code that doesn’t have a matching local league, we need to display all of the local leagues within the same state that the zip code resides in. If you recall from outcome 1, we displayed a summary list and when a user clicked on a state name it took them to a url with the state term id as a filter value. The page the user was taken to displayed all of the local leagues within that state and the state league. So essentially, we need to take the user back to that page when their zip code search returns no results.
To do this, we can use a HOOK_views_post_execute, which is used to alter results after the view is executed but before it is displayed. The reason this hook fits our needs is because we need the view to execute so we can know if any results are returned. If no results are returned, then we know we need to proceed with getting the user to a url with the state term id as a filter value.
As the code suggests, we first test to see if the view returns any results. If it doesn’t, then we grab the value of the zip code entered by the user. We pass the zip code to the _get_state_by_zip() function which will return the state term id. We take this state term id and put it in the url to be used as a filter value. Now the user will be taken to a page that shows them all of the local leagues that exist in the same state as the zip code they searched for, as well as the state league. This is important because we are providing the user with much more useful information than the standard no results text. We are giving them more options so that they may still find a local league that they can join.
The _get_state_by_zip() function has played a critical role in helping us achieve outcome 2 and 3. This is a custom function that we created that allows us to pass it a zip code value and it will return the state term id, which we need to use as a filter value.
In this function, we make a call to the Google Maps Geocoding API with zip code. We decode the JSON response into an object and retrieve the state information from adminstrative_area_level_1. In the last third chunk of the code, we use the state name obtained from the response and locate the taxonomy term that matches it. Then we grab the term id. This last part is crucial because without the term id we would not have had a filter value that allowed us to filter the view results as needed for our various outcomes.
The View modules, out of the box, provided a lot functionality to help users find their nearest local league by zip code. With just views configuration, we were able to achieve outcome 1 and most of outcome 2. Wanting to make sure that users could easily obtain all of the information they needed to join a local league, and take part in the mission of the League of Women Voters, we used Views hooks to extend the functionality of our views lists so that in any outcome a user has a positive journey.