Drupal's Entity API allows developers to expose custom entities to the Views module, and takes care of many common features. It can handle exposing the fields associated with fieldable entity types for use as fields, filters, sorts, and arguments. It provides a way to customize any aspect of the Views integration, and makes it possible to create explicit relationships between 2 different entity types.
In this tutorial we'll:
- Expose a custom Subscriber content entity type and all of its data to Views.
- Create an explicit relationship between the custom content entity and Drupal core User entities.
- Build a view of Subscriber entities.
By the end of this tutorial you should know how to expose custom entities to Views.
Filter plugins provide the logic for determining what items to include in a view. They are the equivalent of a WHERE
clause in an SQL statement. Filters can be exposed to the end user, allowing the viewer to enter a value, or select from a list of options, that are used to filter a list. For example, the options at the top of the Content list at /admin/content allow you to limit the list by content type.
Drupal's Views module provides filter plugins that can handle filtering for all core defined field types, as well as generic data types like numbers and strings. In situations where these filters don't meet your functional requirements, or you need a filter for a custom field, you may want to define a custom Views filter plugin. A common real-world scenario is defining a new filter plugin that extends an existing one and adds new possible values or options. For example, providing a relative date filter that allows a user to choose options like Last week or Last month.
In this tutorial we'll:
- Learn how to create a Views filter plugin.
- Associate our new custom filter with data exposed to Views, so it can be used.
- Learn how to use it in a view as an exposed filter.
By the end of this tutorial you should know how to create a custom filter plugin and use it in Views.
Sort plugins are responsible for determining how the data in a list created with Views is sorted. Sort plugins are applied to fields. For example, a field might contain numbers and the sort plugin can handle putting the values into ascending or descending order. Typically, sort plugins work by adding to the ORDER BY
clause of an SQL query.
The Views module provides sort plugins for all core field types. This includes handling for most primitive data types like strings and numbers. A typical use case for creating a custom sort plugin is extending an existing sort plugin with new conditions or custom sorting logic. For example, you might need to create an alphabetical title sort that excludes articles like a, an, and the. Or a sort based on combining the values in multiple fields.
In this tutorial we'll:
- Learn how to create a custom Views sort plugin
- Associate our custom sort plugin with a field exposed to Views
- Use the sort plugin in a view to sort a list of Article nodes
By the end of this tutorial you should know how to create a custom sort plugin for the title field.
Views field handler plugins handle retrieving and displaying data within a view. The Drupal core Views module provides field plugins for all the core field types, and these work well for many situations where you need to describe custom data sets to Views. For situations where an existing plugin doesn't match your requirements, you can define a custom field handler plugin and alter both the query that gets executed and the rendered output of the data.
This is common for contributed modules that define new field types, or any module using hook_views_data()
to define a dataset to Views where the data contained in a database column may need special handling in order to be displayed as part of a view.
In this tutorial we'll:
- Implement a custom Views field handler plugin
- Extend the default date field plugin and modify the way that it displays a timestamp so that instead of a specific date we get a relative value
By the end of this tutorial you should know how to define a custom field handler plugin and use it in a view.
The Drupal core Views module architecture is built on top of the Drupal Plugin API. This allows site administrators to pick and choose from a list of options (plugins) to handle all the different parts of a view -- including what to display, what style to display it in, how to order the results, who has access, and more. In the Views UI, any time you're presented with the option to choose something from a list of options you're likely dealing with some type of plugin.
The advantage of a plugin-based design is that all functional parts of the Views module are provided in an extensible object-oriented way. The Views module defines the basic framework, and provides interfaces for other modules to implement, extend, and customize via plugins. Customizations to filters, fields, area handlers, sorts, and relationship handlers start with plugins.
In this tutorial we'll:
- Learn about Views plugins and the role they play
- Explore the different plugin types and their underlying classes
- Get a high level overview of the steps for creating a custom Views plugin
By the end of this tutorial you'll have a solid understanding of the different Views plugin types, what they are used for, and where to start if you'd like to define your own.
By default, the Views module can display data contained in any field attached to an entity that is exposed in Views, and the content of any database column exposed to Views via an implementation of hook_views_data()
. It's also possible to create pseudo fields. These appear in the Views UI like any other field, but don't map directly to the data stored in a database column and instead allow the data to be preprocessed. This could be performing a calculation, combining multiple fields into one, and much more.
A common example in core is the fields that allow you to perform edit or delete operations on a node. These don't correspond to a specific database column. And they can't be hard-coded because they require dynamic content specific to the node in question. Instead, they are the result of taking the entity ID and combining it with knowledge about the appropriate route for someone to edit the entity and outputting that as a link.
Another example: Imagine a cooking website where you collect cook time and preparation time (prep time) for recipes and want to also display the full time to prepare. In this case cook time and prep time could be fields on the recipe content type and total time could be handled as a calculated output of both fields, added together and converted into hours and minutes. To achieve this, you can create a custom Views pseudo field and specify the calculation and processing logic in the render function.
In this tutorial we'll:
- Learn how to define a custom Views field plugin for a pseudo field
- Attach the created field to node entities, and expose it to display in a view
By the end of this tutorial you should know how to define a Views pseudo field plugin, attach it to the node entity type via hook_views_data_alter()
, and display it in a view.
Style plugins are responsible for determining how to output a set of rows. Individual rows are rendered by row plugins. Drupal core provides style plugins that include grid, HTML list, table, and unordered list styles. If you need to render the results in a different way, for example as tabs or accordions, or have special markup based on your project requirements, you may want to write a custom style plugin for Views. The advantage of this approach versus overriding the templates in a theme is that you may reuse this plugin in different views throughout the site, or even on different sites.
In this tutorial we'll:
- Learn how to create a custom Views style plugin and render output in the form of an accordion using the HTML5
<details>
element. - Demonstrate how to use a custom style plugin when building a view.
By the end of this tutorial you should know how to declare custom Views style plugins.
Identifing the fields in your data model that contain presentation data is an important part of documenting and planning your API. Examining your existing data model will help put you in the correct mindset when creating or changing it. It is important to reflect on the implications of each field for each hypothetical consumer (even consumers your API is not serving). This process will improve your content model and your API.
In this tutorial we will:
- Go through the article content type's data model
- Locate any fields that contain presentation data
The article model is created out of the box by Drupal when using the standard installation profile.
By the end of this tutorial you'll know how to do these checks, and identify presentation data in your content model, on any content type in your project.
In a monolithic architecture (non-decoupled) there is an implicit proof that the user in the front-end is the same one in the back-end. This empowers the front-end to offload all the authentication and authorization to the back-end, typically using a session cookie. In a decoupled architecture there will be multiple consumers, and some of them will not support using cookies. There are several alternatives to session cookies to authenticate our requests in a decoupled project.
In this tutorial we will:
- Learn about authorization versus authentication
- The impact on a decoupled project of having logged in users
- Learn about the available options for authentication when using a Drupal back-end.
By the end of this tutorial you should be able to explain the difference between authentication and authorization and know how to get started implementing both in a Drupal-backed web services API.
It is important to have good up-to-date documentation about your web services. Doing so will boost developer productivity in a decoupled project. Drupal offers several tools that help maintain your API documentation with minimal effort.
In this tutorial we'll:
- Learn about the JSON schema format for describing data structures
- Generate schemas for our fake articles REST resource
- Visualize those schemas in a human-readable format
By the end of this tutorial you should be able to generate documentation for your REST resources that is kept up-to-date automatically by Drupal.
Traditional Drupal development using the render pipeline allows you to ignore the pitfalls of mixing content and presentation logic in your data model. In a scenario with multiple distribution channels, this separation becomes of critical importance.
In this tutorial we will learn how to:
- Understand the importance of presentation and content separation (delivering clean content, not caring about how to show that content)
- Develop strategies to avoid these presentational problems with minimal damage to the content API
By the end of this tutorial you'll have a better understanding of why keeping presentation data out of your content model is important, and some tips for doing so.
JavaScript applications are the most common type of consumers. They are commonly used to create a website that runs in a web browser. Running decoupled applications in the browser will involve Cross-Origin Resource Sharing (CORS), which requires some setup on the Drupal side in order to work.
In this tutorial we'll:
- Learn about what CORS is and when/why we need to care about it
- Configure Drupal to return an appropriate CORS header, enabling browser-based consumers access to our API
By the end of this tutorial you will have a better understanding of CORS, and how to configure Drupal to serve an API that works with CORS.
The routing system can get dynamic values from the URL and pass them as arguments to the controller. This means a single route with a path of /node/{node}
can be used to display any node entity. Route parameters can be validated, and upcast to complex data types via parameter conversion. If you ever want to pass arguments to the controller for a route, you'll use parameters to do so.
In this tutorial we'll:
- Define what parameters (slugs, placeholders) are and what they are used for in a route definition.
- Explain how URL parameters are passed to a controller.
- Define parameter upcasting.
By the end of this tutorial, you should be able to explain how to define a route that uses parameters to pass dynamic values to the route controller, and explain how parameter upcasting works.
Every route should define its access control parameters. When you define routes in a module, you can limit who has access to those routes via different access control options. Route-level access control applies to the path. If your route defines a path like /journey/example
, the access control configuration will determine whether to show the current user the page at the path defined by the route, or to have Drupal serve an "HTTP 403 Access Denied" message instead.
In this tutorial we'll look at different ways of adding access control to a route including:
- Access based on the current user's roles and permissions
- Access based on custom logic in a callback method
- Logic in an access checker service
By the end of this tutorial, you should be able to add access control logic to your custom routes that will meet any requirement.
When defining a route and subsequently displaying the page, often we need to calculate the page title based on route parameters or other logic. In these cases, we can't just hard code the value into the _title
configuration of the route. To set a dynamic title for a page, we'll use the route's _title_callback
option, and point to a PHP callback that contains the logic that computes the title of the page.
In this tutorial we'll:
- Learn how to use the
_title_callback
route configuration option to dynamically set a page title - Explain how arguments are provided to the title callback method
- Update the route and controller from a previous tutorial to use a dynamic title callback
By the end of this tutorial, you should be able to configure a route so that its title can be set dynamically using route parameters, instead of hard-coding the title with a static string of text.
Let's write some code that will allow us to see route parameters in action. We'll define a new route with a path like /journey/42/full but where 42
can be any node ID, and full
can be any view mode. When a user accesses the path we'll pass the dynamic parameters from the URL to the controller. The controller will then load the corresponding node and render it using the provided view mode, and return that to display on the page.
By the end of this tutorial you should be able to:
- Use dynamic slugs in a route to pass parameters to the route controller.
- See how Drupal will upcast a value like the node ID, 42, to a
Node
object automatically. - Explain what happens when you visit a URL that matches a route but the parameters don't pass validation.
By the end of this tutorial, you should be able to pass dynamic values from the URL to a route's controller.
Every web framework, including Drupal, has basically the same job: provide a way for developers to map URLs to the code that builds the corresponding pages. Drupal uses Symfony's HTTPKernel component. Kernel events are dispatched to coordinate the following tasks:
- Process the incoming request
- Figure out what to put on the page
- Create a response
- Deliver that response to the user's browser
Knowing a bit more about how Drupal handles the request-to-response workflow will help you better understand how to use routes and controllers to create your own custom pages or deal with authentication, access checking, and error handling in a Drupal module.
In this tutorial we'll:
- Walk through the process that Drupal uses to convert an incoming request into HTML that a browser can read
- See how the Symfony
HTTPKernel
helps orchestrate this process - Learn about how the output from a custom controller gets incorporated into the final page
By the end of this tutorial, you should be able to describe the process that Drupal goes through to convert an incoming request for a URL into an HTML response displayed by the browser.
Sometimes we need the response returned from a controller to be something other than HTML content wrapped with the rest of a Drupal theme. Maybe we need to return plain text, or JSON structured data for an application to consume. Perhaps we need greater control over the HTTP headers sent in the response. This is possible by building on the fact that controllers can return generic Response
objects instead of renderable arrays, allowing you to gain complete control over what is sent to the requesting agent.
In this tutorial we'll:
- Look at how to return a plain text response, and JSON data
- Show how to make your responses cacheable by adding cacheability metadata
- Learn about how to use a generic Symfony
Response
to gain greater control over what gets returned
By the end of this tutorial, you should be able to return responses from a controller in a Drupal module that are not HTML content wrapped in a Drupal theme.
By default, individual forms in Drupal are not output using Twig template files. It's possible to associate a form with a Twig template file by creating a new theme hook, and then referencing that theme hook from the $form
array that defines the form. Doing so allows theme developers to customize the layout of the elements in the form using HTML and CSS.
This is useful when you want to change the layout of the entire form. For example, putting the elements into 2 columns. If you want to change individual elements in the form, you can often do so by overriding element specific Twig template files.
In this tutorial, we'll:
- Learn how to create a new theme hook that can be used to theme an element in a render array.
- Associate the
$form
we want to theme with the new theme hook we created. - Create a Twig template file for the theme hook that will allow us to lay out the form elements using custom HTML.
By the end of this tutorial, you should be able to associate a Twig template file with any form in Drupal, so that you can customize its layout using HTML and CSS.
Tools like PHP_Codesniffer (phpcs) can be used to help ensure your code adheres to Drupal's coding standards. As a module developer, you should use phpcs and its Drupal-specific rule sets on all custom module code.
In this tutorial, we'll:
- Learn about PHP_Codesniffer (phpcs).
- Install PHP_Codesniffer and the Drupal-specific rules.
- Use phpcs to lint our custom code.
By the end of this tutorial, you should be able to use PHP_Codesniffer to help automate the process of adhering to Drupal's coding standards.