Over 8 years have passed since there was a DrupalCamp in tropical Nicaragua. With the help of a diverse group of volunteers, sponsors, and university faculty staff, we held our second one. DrupalCamp Lagos y Volcanes ("Lakes & Volcanoes") was a great success with over 100 people attending in 2 days. It was a big undertaking so we followed giants' footsteps to prepare for our event. Lots of the ideas were taken from some of the organizers' experience while attending Drupal events. Others came from local free software communities who have organized events before us. Let me share what we did, how we did it, and what the results were.
Sign up to be notified when Agaric gives a migration training:
Most companies are run hierarchically. Small companies might have a boss while larger companies usually have a CEO and board of directors or other form of corporate leadership.
We believe work can be done differently- organized democratically and transparently as a worker-owned cooperative. There are 7 principles that cooperatives follow:
This means that each employee is also an owner and we make decisions democratically, fostering a culture of honesty and accountability. Our focus on community ensures that quality work comes first, always contributing back to the software commons when possible. Finally, in a world of unprecedented wealth disparity, we hope to serve as a model for what a business should be.
Vestibulum iaculis consectetur convallis. Donec sit amet congue massa. Quisque nec eros eu dolor posuere fermentum et vel nulla. Donec mollis leo sit amet egestas vulputate. Etiam eget ante ante. Proin dignissim justo vel nibh tincidunt, a porta dui tincidunt. Ut varius, metus ac luctus molestie, turpis magna ultricies lectus, at ornare tellus diam sit amet risus. In hac habitasse platea dictumst. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus neque dui, placerat ac massa nec, rutrum rhoncus ex. Sed ut posuere urna.
In the previous article we provided a reference of available configuration options for migrate source plugins. In today’s article we are doing something similar for destination plugins. We will present a reference of available configuration options for migrate destination plugins provided by Drupal core and some contributed modules. Knowing which options are available might require some Drupal development knowledge. By providing this reference it should make the process of writing migrations easier.

For each migrate destination plugin we will present: the module that provides it, the class that defines it, the class that the plugin extends, and any inherited options from the class hierarchy. For each plugin configuration option we will list its name, type, a description, and a note if it is optional.
Module: Migrate (Drupal Core)
Class: Drupal\migrate\Plugin\migrate\destination\DestinationBase
Extends: Drupal\Core\Plugin\PluginBase
This abstract class is extended by many migrate destination plugins. This means that the provided configuration keys apply to any destination plugin extending it.
List of configuration keys:
Module: Migrate (Drupal Core) Plugin ID: entity:$entity_type See below.
Class: Drupal\migrate\Plugin\migrate\destination\Entity
Extends: Drupal\migrate\Plugin\migrate\destination\DestinationBase
Inherited configuration options: destination_module.
Related article: Drupal migrations reference: List of properties per content entity
This abstract class is extended by migrate destination plugins that want to import entities. It uses the MigrateEntity derivative to handle both content and configuration entities. The derivative sets the plugin ID to entity:$entity_type where $entity_type is the machine name of the entity. For example, entity:node and entity:node_type.
By default, content entities are handled by the EntityContentBase class while configuration entities use EntityConfigBase. Some entities like user (content) and node type (configuration) use specific classes for the import operation. The DefaultPluginManager::getDefinitions method triggers the search for classes that will override the default for a particular plugin ID. The override ultimately happens in the DerivativeDiscoveryDecorator::getDerivatives method.
In addition to the keys provided in the parent class chain, this abstract class provides the following configuration keys, which will be available to its derivatives:
Module: Migrate (Drupal Core) Plugin ID: entity:$entity_type See below.
Class: Drupal\migrate\Plugin\migrate\destination\EntityContentBase
Extends: Drupal\migrate\Plugin\migrate\destination\Entity
Inherited configuration options: destination_module and default_bundle.
This class is used to handle import operations for all content entities, unless a specific class exists for the plugin ID being used. For example, nodes are handled by this class, but users leverage the EntityUser class. The MigrateEntity derivative sets the plugin ID to entity:$entity_type where $entity_type is the machine name of the content entity. For example, entity:node, entity:user, entity:taxonomy_term, entity:file, entity:media, entity:comment, entity:block_content, and entity:contact_message.
In addition to the keys provided in the parent class chain, this class provides the following configuration keys:
Module: User (Drupal Core) Plugin ID: entity:user See below.
Class: Drupal\user\Plugin\migrate\destination\EntityUser
Extends: Drupal\migrate\Plugin\migrate\destination\EntityContentBase
Inherited configuration options: destination_module, default_bundle, translations, overwrite_properties, and validate.
This class provides a destination plugin for migrating user entities. It performs extra checks like preventing that the password for user 1 is overridden by the migration.
In addition to the keys provided in the parent class chain, this abstract class provides the following configuration keys:
Module: Migrate (Drupal Core) Plugin ID: entity_revision:$entity_type See below.
Class: Drupal\migrate\Plugin\migrate\destination\EntityRevision
Extends: Drupal\migrate\Plugin\migrate\destination\EntityContentBase
Inherited configuration options: destination_module, default_bundle, translations, overwrite_properties, and validate.
This class provides an entity revision destination plugin. Only revisionable entities, those that define a revision entity key, can use this destination plugin. It uses the MigrateEntityRevision derivative which sets the plugin ID to entity_revision:$entity_type where $entity_type is the machine name of the entity. For example, entity_revision:node whose revision key is vid and entity_revision:block_content whose revision key is revision_id.
Entity revisions can only be migrated after the entity to which they belong has been migrated. For example, revisions of a given node (entity_revision:node destination migration) can be migrated only after the current version of that node (entity:node destination migration) has been imported.
Module: Entity Reference Revisions module Plugin ID: entity_reference_revisions:$entity_type See below.
Class: Drupal\entity_reference_revisions\Plugin\migrate\destination\EntityReferenceRevisions
Extends: Drupal\migrate\Plugin\migrate\destination\EntityRevision
Inherited configuration options: destination_module, default_bundle, translations, overwrite_properties, and validate.
Related article: Introduction to paragraphs migrations in Drupal
This class provides an entity revision revision destination plugin. It uses the MigrateEntityReferenceRevisions derivative which sets the plugin ID to entity_reference_revisions:$entity_type where $entity_type is the machine name of the entity. For example, entity_reference_revisions:node and entity_reference_revisions:paragraph. For example, entity_reference_revisions:node whose revision key is vid and entity_reference_revisions:paragraph whose revision key is revision_id.
This is the destination plugin used for migrating Paragraphs. Entity reference fields are no longer supported to reference Paragraphs. Instead, entity entity reference revisions must be used. Therefore, this class is used for paragraphs migrations with the entity_reference_revisions:paragraph plugin ID. See this article for an example on how to migrate paragraphs.
In addition to the keys provided in the parent class chain, this abstract class provides the following configuration keys:
Module: Migrate (Drupal Core) Plugin ID: entity:$entity_id See below.
Class: Drupal\migrate\Plugin\migrate\destination\EntityConfigBase
Extends: Drupal\migrate\Plugin\migrate\destination\Entity
Inherited configuration options: destination_module and default_bundle.
This class is used to handle import operations for all configuration entities, unless a specific class exists for the plugin ID being used. For example, taxonomy vocabularies are handled by this class, but node types leverage the EntityNodeType class. The MigrateEntity derivative sets the plugin ID to entity:$entity_type where $entity_type is the machine name of the content entity. For example, entity:node_type, entity:user_role, entity:taxonomy_vocabulary, entity:block, entity:comment_type, entity:block_content_type, entity:contact_form, entity:date_format.
In addition to the keys provided in the parent class chain, this abstract class provides the following configuration keys:
Module: Node (Drupal Core) Plugin ID: entity:node_type
Class: Drupal\node\Plugin\migrate\destination\EntityNodeType
Extends: Drupal\migrate\Plugin\migrate\destination\EntityConfigBase
Inherited configuration options: destination_module, default_bundle, and translations.
This class is used to import node types. It does not take extra configuration options. The plugin overrides the import method to attach the body field to the imported content type. This depends on the presence of certain destination properties in the imported row. That is, the following properties needs to be mapped in the process section of the migration:
Module: Migrate (Drupal Core) Plugin ID: config
Class: Drupal\migrate\Plugin\migrate\destination\Config
Extends: Drupal\migrate\Plugin\migrate\destination\DestinationBase
Inherited configuration options: destination_module.
This class persists data to the configuration management system. In addition to the keys provided in the parent class chain, this class provides the following configuration keys:
Additionally, the plugin expects certain destination properties in the imported row. That is, the following properties needs to be mapped in the process section of the migration:
Module: Migrate Plus Plugin ID: table
Class: Drupal\migrate_plus\Plugin\migrate\destination\Table
Extends: Drupal\migrate\Plugin\migrate\destination\DestinationBase
Inherited configuration options: destination_module.
This class allows you to write directly to a table not registered with Drupal Schema API. See this test for an example on how to use this plugin.
In addition to the keys provided in the parent class chain, this class provides the following configuration keys:
In Drupal core itself there are around 50 migrate destination plugins. And many more are made available by contributed modules. It would be impractical to document them all here. To get a list by yourself, load the plugin.manager.migrate.destination service and call its getDefinitions() method. This will return all migrate destination plugins provided by the modules that are currently enabled on the site. This Drush command would get the list:
# List of migrate destination plugin definitions. $ drush php:eval "print_r(\Drupal::service('plugin.manager.migrate.destination')->getDefinitions());" # List of migrate destination plugin ids. $ drush php:eval "print_r(array_keys(\Drupal::service('plugin.manager.migrate.destination')->getDefinitions()));"
To find out which configuration options are available for any destination plugin consider the following:
What did you learn in today’s article? Did you know that migrate destination plugins can inherit configuration keys from their class hierarchy? Were you aware that there are so many destination plugins? Other than the ones listed here, which destination plugins have you used? Please share your answers in the comments. Also, we would be grateful if you shared this article with your friends and colleagues.
I'm back! Because I knew this blog post wasn't quite long enough already...certainly not because it occurred to me that I left off another alternative I've used in a pinch.
If you want to run a command using a different version of php, rather than rely on the shebang, you can always just call the desired script as an argument to php itself. When you run, e.g. drush status, your shell sees the #! on the first line, and uses that as the default interpreter for the script. In essence, your command is translated into /usr/bin/env php drush status.
Rather than accept this default, you can specify the specific version on your command line, such as php8.1 drush status. In this case the #! is just ignored. Unfortunately, this method can only get you so far - particularly with drush. Drush often figures out what your command requires and in turn will spawn other php scripts to do the dirty work. These spawned scripts are unaware of the interpreter that drush itself was started with.
Thanks to the updated CoLab sites, NICHQ Staff are now collaborating more effectively. By including users from the beginning with surveys and interviews we were able to update their software with exactly the features they need. Doing so on a distribution has empowered their team to deploy new sites and improve existing ones as CoLabs grow.
Ben spoke most recently on this topic at DrupalCon Seattle and Drupal Camp Twin Cities, both in 2019, about how Drupal as a Service can save our livelihoods and our lives. Here are some resources related to the talk and topic.

The Earth is not dying, it is being killed, and those who are killing it have names and addresses.
Utah Phillips
As far as i can tell, the Internet hasn't sourced that quotation, but no one challenges that it was Phillips— and i thought i heard him say about the same thing on the radio, in a broadcast where he also slammed NPR, boosting community radio and, if needed, the do-it-yourself be-the-media ethos. Maybe i'm part of a collective delusion, in which case, i'm sure Utah Phillips wouldn't care one way or another so long as we got on with working for the liberation of humanity and preserving life on planet Earth as part of that.
Crowd-sourcing this on Mastodon (itself interestingly compatible with LibreSaaS with instances like social.coop working on governance models, if not yet having the revenue to cover hosting, let alone contribute back to development:
Hello fediverse, what online tools do you know about that are LibreSaaS— you can pay to use them, but the full stack is free/libre open source software so you or others can totally host the entire thing yourself if you want?
I added whichever answers i didn't already have to the LibreSaaS list pad.
We'll keep looking for ways that cooperation can save us all (and technology can help) so sign up to get (extremely occasional, less than once a year) updates.