In the process of displaying the content of a view to an end user, every view goes through a common build cycle. As a developer it helps to understand the build and render cycle that a view goes through and how you can use it to alter the final result. It helps to know where things are in the cycle when your code is executed, and how that impacts what your code can and can't do. The Views module exposes hooks -- documented in views.api.php -- that allow developers to influence every step of the cycle. This includes altering the database query that gets executed, changing the configuration of filters, computing things based on raw results, and changing the rendered output of any field.
In this tutorial we'll:
- Learn the steps of the Views build and render cycle.
- Identify the hooks invoked during each step and provide examples of what types of things you might do with each hook.
By the end of this tutorial you should be able to list the hooks invoked by Views, when they get called during the build and render cycle, and explain the impact this has.
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.
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.
When you are implementing an HTTP API for a decoupled project, one of the critical, but often overlooked, aspects is the API documentation. Documenting your API will allow front-end developers (and you six months from now) to learn how to use that particular API.
In Drupal, there are several modules that can read your site configuration and generate documentation for you automatically.
In this tutorial we're going to:
- Learn about the importance of good documentation.
- Decide whether or not to use an existing specification for our API such as JSON:API or GraphQL.
- Review options for automatically generating documentation.
By the end of this tutorial you'll be able to decide whether or not using an existing documentation specification is a good fit for your project, and choose an option based on those available for use with Drupal.
In the last several years REST has been the de facto standard for web services. However, there are several common problems when developing websites and digital experiences with the traditional REST implementations. Luckily, those issues have solutions, either complete or partial.
Modern web service specifications like JSON:API and GraphQL implement those solutions, although they differ slightly in their implementation.
In this tutorial we will learn about:
- The problems of traditional REST implementations
- Common solutions to those problems, and how JSON:API and GraphQL deal with them
By the end of this tutorial you'll be able to explain some of the common pitfalls of REST-based APIs, and how JSON:API and GraphQL address those issues.
The term web services has been around for quite a while. Given that web services is such a broad topic, let's define what web services are and how we are going to refer to them throughout this series so we are all on the same page.
This tutorial is an introduction to web services that will help you:
- Learn what a web service is.
- Understand that this series focuses on HTTP web services, and mostly on REST principles.
- Get some examples of APIs in the wild and what type of consumers they have.
By the end of this tutorial, you'll be able to define what web services are, and how we'll use the term in the context of these tutorials.
When running migrations you can use the highwater_mark source plugin configuration option to influence which rows are considered for import on subsequent migration runs. This allows you to do things like only look at new rows added to a large dataset. Or to reimport records that have changed since the last time the migration was run. The term, highwater mark, comes from water line marks found on structures in areas where water level changes are common. In running migrations, you can think of a highwater mark as a line that denotes how far the migration has progressed, and saying, "from now one, we only care about data created after this line".
Another common use case for highwater marks is when you're importing a large dataset and the system runs out of resources. Usually this will look like a migration failing because it timed out, or the process ran out of memory. A highwater mark should allow you to pickup from where you left off.
In this tutorial we'll:
- Define what a highwater mark is, and how you can use them to limit the rows considered for importing each time a migration is executed.
- Demonstrate how highwater marks can be used to reimport source records that have been modified since the previous time the migration was executed.
- Introduce the
track_changesoption.
By the end of this tutorial you should be able to define what a highwater_mark is and how to use them to speed up the import of large datasets or force the migration to reimport records when the source data is changed.
You should also be aware of the track_changes feature which is a slower, but more dynamic, method of checking for changes in source data and reimporting records when a change is found.
If you need to write a migration that is capable of being executed multiple times and picking up changes to previously imported data, you can use the track_changes configuration option of most source plugins. This will tell the migration to keep a hash of each source row, and on subsequent runs of the same migration it will compare the previous hash to the current one. If they don't match this means the source data has changed, and Drupal will reimport that record and update the existing destination record with new data.
Using track_changes differs from calling drush migrate:import --update in that using --update will force every record to be re-imported regardless of whether the source data has changed or not.
In this tutorial we'll:
- Learn how change tracking works to detect changes in your source data
- Use the
track_changesoption in a migration
Note that progress of a migration can also be tracked using highwater marks if the source data has something like a last_updated timestamp column. Using highwater marks in this case is likely more efficient than using track_changes. It's a good idea to understand how both features work, and then choose the appropriate one for each migration.
Before you can migrate your Drupal 7 site to the latest version of Drupal you'll need to be able to build the features that make up the current site. Part of this is evaluating all the modules you've got installed, figuring out what you're using them for, and if there's a version that's compatible with the latest version of Drupal along with a migration path.
I usually make a spreadsheet for this. But any list of the modules you’re currently using that allows you to keep track of how you plan to update them will work. You also want to keep track of where you are in the process of figuring that all out. Because it’s likely you’ll have some modules for which the path is clear, and others where it’s pretty murky and requires more in-depth research to find a path forward. Having a list means you can break that up into tasks, and ensure you’re not missing something. It'll also help you define when your migration is done as well as any final quality assurance (QA) tasks.
In this tutorial we'll:
- Start a list of the modules that make up our current site.
- Point to some tools that can help speed up the process of evaluating a module's readiness.
- Provide a set of questions that you can ask about each module you're using as part of your planning process.
By the end of this tutorial you should have a list of all the modules you're currently using, and some tools you can use to help you figure out how to move forward with each one.
If you want to be able to say you're done with your Drupal-to-Drupal migration, first you have to be able to define "done". And part of that is doing a content analysis and inventory. Performing a content analysis and inventory will help you ensure that you don't miss any fields or important records. It also gives you an opportunity to spend some time thinking about the overall information architecture of your site. You're already going to be doing a lot of work to migrate your content, so deciding to shuffle things a bit now might not add any significant extra time. Additionally, the latest Drupal (as of Drupal 10) is a different platform than either Drupal 6 or 7, and as such, there are some new best practices and new ways of doing things that might not have been available before.
In this tutorial we'll:
- Provide a set of questions you can ask yourself about the content of your current site to kick-start your analysis.
- Give an example of how we create a content type inventory in a spreadsheet and use that to help define "done" for our projects.
By the end of this tutorial you should be able to get started analyzing the content and content types of your existing site in order to start planning for your Drupal-to-Drupal migration.
Because current versions of Drupal are so much different from older versions like Drupal 7 upgrading to the latest version requires creating a new site using the latest version of Drupal and then migrating your old site's content and configuration into it.
There is no one right way to tackle a Drupal-to-Drupal migration. Instead, it's like walking down a path and coming to a fork in the trail and then choosing a direction over and over and over. Since every site is different, every path to a finished migration will be different, too. I know nobody wants to hear it, but every migration is its own unique adventure. Every successful migration will require its own custom code, weird shell scripts, and detailed lists of the exact order of things. But that doesn't mean there's no plan to follow.
A successful migration charts a course through the maze, while leveraging existing tools and experience to help find the shortest route and the right path at each fork--with minimal backtracking due to wrong choices.
In this tutorial we'll:
- Look at what makes a Drupal-to-Drupal migration (a major version upgrade) so tricky, and how to think about performing one
- Define 3 high-level approaches to performing a Drupal-to-Drupal migration
- Get a better idea of what the migration work entails, so you go into it with a proper mental model.
By the end of this tutorial you should be able to explain the different approaches to performing a Drupal-to-Drupal migration in broad strokes, and have a better picture of the work that will be involved.
The migration_lookup process plugin allows you to populate entity reference fields when the entity that you want to reference is created by another migration, and you don't yet know what the unique ID will be in the Drupal database. Think of this as being able to answer the question: I have a migration that imports new users from a CSV file, and another that imports articles from a different CSV file. The articles' CSV file has a column that indicates which row from the users' CSV file is the author of this file. But in order to populate the author field for the Drupal node I need to know the ID of the imported user which is an auto-incremented ID that I'm not setting. So, what is the correct ID to use?
Another use case is circular dependencies, like hierarchical taxonomies with parent/child relationships. Or a content type with a field that references other nodes of the same type, like articles that reference other articles.
In this tutorial we'll:
- Learn about the
migration_lookupprocess plugin and its configuration - Provide examples of using the plugin to populate entity reference fields during a migration
- Look at alternatives
By the end of this tutorial you should be able to use the migration_lookup plugin to effectively manage entity relationships across multiple individual migrations.