Contextual content with the Flag module and a custom Views "Default Argument Handler"
The Views module is the most installed Drupal module and gives great power to developers for building websites. Because of its rich feature set, Views will be integrated in core as part of Drupal 8. Developers may extend the Views functionality by writing custom plugins using the complex plugin system. This helps implementing functionality for special use cases in Views.
In this blog post I would like to explain how to write a custom "Default Argument Handler" for Views and how to develop a simple context system using the Flag module by providing a sample use case as example.
Hint: The complete code of the module can be accessed for testing purposes at github.
The Use Case
Our client produces organic pet food as a family business and wants to increase sales using e-commerce. Furthermore, the daughter of our client will publish a monthly magazine with tips and tricks for each dogs and cats. The magazine should be advertised on the new website of the customer. Older magazine arcticles will be published on the website later on.
A key feature of the new website will be a context system which delivers products and articles to the user based on his favorite pet. We use the Flag module to flag the pets for each user.
Hint: To keep it simple we flag the pets by hand.
First of all we create a vocabulary "flag context" with some terms which will be flagged later.
Afterwards we create a new flag at admin/structure/flags with the flag type "taxonomy term" choosing "flag context" as bundle.
Extending the content type
By adding an "Entity Reference" field to a content type (i.e. article of the standard profile) we make sure that we can link content to the terms which will be flagged by the user. After adding the field we create at least one node for each term. For this use case we would also need to add this field to our products but this will be sufficient for our example.
Next, we have to flag a term for testing the functionality later. Just visit a term page and flag the term using the link on the page.
The user should see what their favorite pet is. We make this happen by creating a View.
The View filters terms by our created vocabulary "flag context" and has a relationship "Flags: Taxonomy Term flag" on "Flag Context" and "Current User". We use "block" as a display for easy embedding.
In addition to the title we also add a flag link allowing the user to unflag the term in the block.
For testing purposes, we add the new block to the "sidebar first" region.
At last, we create the basics for our final test. We create a page View showing only "article" nodes. We add "Content:has taxonomy term ID" as "Contextual Filter". This filter will be extended later with our context functionality.
Coding the handler
A views handler has to been included in a module to be recognized by the plugin system of Views. It is not a lot of work but despite that there are some lines where we could struggle.
1. The .info file
A common pratice is to put views handlers and plugins in their own subdirectorys. Drupal needs to know where those files are located so we include the path into the info file.
2. The .module file
We need to implement hook_views_api to tell Views that our module is of interest:
Now, Views looks for a file with relevant code. Best practice is to create a modulename.views.inc file.
3. The .views.inc file
By implementing hook_views_plugin we return an array in which we define the kind of plugin we like to create and the place where to find it.
With these 3 steps we prepared our handler. Now it is time to add functionality to the handler.
4. The views_argument_default_flag.inc file
In this file we define the handler as class by inheriting and overriding methods from the views_plugin_argument_default.
First of all, we define the option form of the handler. We want to configure which flags or vocabularys we want to use later. This makes it possible to add other flags or vocabularies very easily. Furthermore, it should be possible to add multiple terms and also to define a fallback if there is no content available.
To achieve this we get all flags from the system and create a checkboxes form item. It is important to implement the "default value" of the options correctly. The options are available in the views object:
The complete function:
As with all forms we also can add a validate and a submit function. We only add a submit function for now.
Now we have to override the get_argument() function. This function will return values to the filter in the view.
We compare the flagged content with the values from the options. We filter all term ids by their vocabularies using a helper function. It is important to return the value as a string just as we would call the argument by hand.
The helper function for the .module file:
This is all for the handler.
Next, we add the handler to the contextual filter we recently created.
After applying the changes there should be the article which matches our favorite pet in the preview.
The Flag modules makes it easy to handle user preferences. Page elements can react to different flags and return relevant content to the user by using the handler we created. With a few tricks we can extend the functionality, i.e. adding automatic flagging with rules reacting on content views.