drupal-migrations
Skill71 starsChanged 7 months agoMITView on GitHub
Drupal migration API — D7-to-D11 upgrades, CSV imports, JSON API imports, and custom source/process/destination plugins.
In this repository35 other files it gives its agents.
- ddev-expertSkill
- docker-localSkill
- drupal-accessSkill
- drupal-cachingSkill
- drupal-composerSkill
- drupal-configSkill
- drupal-contrib-mgmtSkill
- drupal-debuggingSkill
- drupal-drushSkill
- drupal-dttSkill
- drupal-entity-apiSkill
- drupal-eventsSkill
- drupal-fieldsSkill
- drupal-form-ajaxSkill
- drupal-form-alterSkill
- drupal-form-apiSkill
- drupal-form-validationSkill
- drupal-hooksSkill
- drupal-htmxSkill
- drupal-javascriptSkill
- drupal-kernelSkill
- drupal-menusSkill
- drupal-paragraphsSkill
- drupal-pluginsSkill
- drupal-queriesSkill
- drupal-renderSkill
- drupal-routesSkill
- drupal-search-apiSkill
- drupal-securitySkill
- drupal-servicesSkill
- drupal-stateSkill
- drupal-taxonomySkill
- drupal-twigSkill
- drupal-unitSkill
- SKILL.mdSkill
The file
Files
Drupal Migrations
Migration YAML Structure
id: my_migration
label: 'My Migration'
migration_group: my_group # group related migrations together
source:
plugin: source_plugin_name
process:
destination_field: source_field
destination:
plugin: 'entity:node'
default_bundle: article
migration_dependencies:
required:
- other_migration # must match actual migration IDsProcess Plugins Quick Reference
| Plugin | Use for |
default_value | Hardcode a value |
static_map | Map old values → new values |
concat | Join fields with a delimiter |
migration_lookup | Reference a previously migrated entity (by old ID) |
entity_lookup | Reference an existing entity by field value |
entity_generate | Create entity if it doesn't exist |
skip_on_empty | Skip field/row if value is empty |
skip_on_value | Skip row if field equals a value |
sub_process | Iterate a multi-value source array |
Entity References
process:
# Reference a previously migrated entity
uid:
plugin: migration_lookup
migration: users
source: author_id
# Reference an existing entity by field value
field_category:
plugin: entity_lookup
source: category_name
entity_type: taxonomy_term
bundle: categories
bundle_key: vid
value_key: name
# Create term if it doesn't exist
field_tags:
plugin: entity_generate
source: tag_name
entity_type: taxonomy_term
bundle: tags
value_key: nameMultiple Values & Conditional
process:
# Iterate a multi-value source array
field_tags:
plugin: sub_process
source: tags
process:
target_id:
plugin: entity_generate
source: name
entity_type: taxonomy_term
bundle: tags
value_key: name
# Skip field processing if source is empty
field_image:
plugin: skip_on_empty
method: process
source: image_url
# Skip entire row based on a value
pseudo_skip:
plugin: skip_on_value
source: status
method: row
value: 'draft'Custom Source Plugin
namespace Drupal\my_module\Plugin\migrate\source;
use Drupal\migrate\Attribute\MigrateSource;
use Drupal\migrate\Plugin\migrate\source\SqlBase;
use Drupal\migrate\Row;
#[MigrateSource(id: 'legacy_products', source_module: 'my_module')]
final class LegacyProducts extends SqlBase {
public function query() {
return $this->select('legacy_products', 'p')
->fields('p', ['id', 'name', 'price'])
->condition('p.status', 'active');
}
public function fields() {
return [
'id' => $this->t('Product ID'),
'name' => $this->t('Name'),
'price' => $this->t('Price'),
];
}
public function getIds() {
return ['id' => ['type' => 'integer', 'alias' => 'p']];
}
public function prepareRow(Row $row) {
$row->setSourceProperty('price_with_tax', $row->getSourceProperty('price') * 1.21);
return parent::prepareRow($row);
}
}Custom Process Plugin
namespace Drupal\my_module\Plugin\migrate\process;
use Drupal\migrate\Attribute\MigrateProcess;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
#[MigrateProcess(id: 'cents_to_decimal')]
final class CentsToDecimal extends ProcessPluginBase {
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
if (empty($value) || !is_numeric($value)) {
return NULL;
}
return number_format((float) $value / 100, 2, '.', '');
}
}Key Drush Commands
drush migrate:status # list all migrations + status
drush migrate:import migration_id # run
drush migrate:import migration_id --update # re-run and update existing records
drush migrate:import migration_id --limit=50 # batch to avoid memory issues
drush migrate:rollback migration_id # undo
drush migrate:reset-status migration_id # fix "migration is busy" state
drush migrate:messages migration_id # show per-row errorsDoes it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

