luremo / craft-link-migrator
Staged migration plugin for moving Verbb Hyper fields and content to Craft's native Link field with audit, prepare, content, and finalize workflows.
Package info
github.com/LuremoDigital/Link-migration
Type:craft-plugin
pkg:composer/luremo/craft-link-migrator
Requires
- php: ^8.2
- craftcms/cms: ^5.3
Requires (Dev)
- phpunit/phpunit: ^11.0
This package is auto-updated.
Last update: 2026-07-10 14:12:12 UTC
README
Link Migrator
Migrate Verbb Hyper fields and content to Craft CMS native Link fields — safely, in stages, with a guided Control Panel workflow.
Link Migrator gives Craft teams a guided path from Verbb Hyper to Craft's native Link field. Start in the Craft Control Panel, review the audit, prepare parallel native fields, migrate content with backups, review template impact, and finalize the layout cutover when everything is ready.
The original Hyper fields and values remain intact throughout the migration. Control Panel write actions require explicit confirmation, CLI write commands require --force=1, and each migration stage produces reports you can inspect before continuing.
Link Migrator is an independent product and is not affiliated with Verbb. Hyper is a plugin by Verbb.
Features
- Control Panel first: follow the guided Craft CP wizard from audit to finalization.
- Audit before writing: inspect Hyper fields, supported mappings, lossy cases, and template API mismatches.
- Keep source data intact: prepare parallel native Link fields instead of replacing Hyper fields in place.
- Migrate safely: process content in batches, resume interrupted runs, and optionally back up each source value.
- Verify before cutover: re-read migrated content and refuse finalization while non-empty source values remain unverified.
- Review template impact: find common Hyper-only properties and methods that need updating.
- Track every run: write human-readable logs and JSON reports to Craft's runtime storage.
- Automate with the CLI: run the same staged workflow from deployment scripts when needed.
Requirements
- PHP 8.2+
- Craft CMS 5.3+
- Verbb Hyper installed until preparation, content migration, and finalization are complete
- Craft CMS 5.6+ recommended for the full native Link advanced-field set
Installation
Install Link Migrator from the Craft Plugin Store or use Composer:
composer require luremo/craft-link-migrator php craft plugin/install link-migrator
Link Migrator is free to use, with every feature included and no edition split.
Start in the Control Panel
Open Link Migrator in the Craft Control Panel, or go directly to /admin/link-migrator.
The wizard walks through the migration in five stages:
- Audit: read-only scan of Hyper fields, supported mappings, warnings, and template mismatches.
- Prepare native fields: create native Link fields beside the source Hyper fields. Requires admin access and a confirmation checkbox.
- Migrate content: copy Hyper values into prepared native fields, write backups, and verify saved values.
- Review template impact: inspect likely Hyper-only Twig or PHP API usage before cutover.
- Finalize: remove Hyper fields from field layouts after live content is verified. Hyper fields themselves are not deleted.
For most sites, this is the recommended workflow. Use the CLI when you want dry runs, single-field runs, CI checks, or scripted deployment steps.
CLI Workflow
Before the first write, back up your database and project config. Then run each stage explicitly:
# 1. Inspect fields and template impact. php craft link-migrator/migrate/audit --dry-run=1 php craft link-migrator/migrate/mismatches # 2. Preview and prepare parallel native fields. php craft link-migrator/migrate/prepare-fields --dry-run=1 php craft link-migrator/migrate/prepare-fields --force=1 # 3. Preview and migrate content with backups. php craft link-migrator/migrate/content --dry-run=1 --create-backup=1 php craft link-migrator/migrate/content --force=1 --create-backup=1 --batch-size=100 # 4. Check progress, then preview and finalize the layout cutover. php craft link-migrator/migrate/status php craft link-migrator/migrate/mismatches php craft link-migrator/migrate/finalize --dry-run=1 php craft link-migrator/migrate/finalize --force=1 --acknowledge-mismatches=1
Run php craft project-config/apply separately if your deployment workflow requires it.
Migrate one field
Use the source Hyper field handle with --field:
php craft link-migrator/migrate/prepare-fields --field=ctaLink --force=1 php craft link-migrator/migrate/content --field=ctaLink --force=1 --create-backup=1 php craft link-migrator/migrate/finalize --field=ctaLink --force=1 --acknowledge-mismatches=1
Multi-Environment Deployment
Field definitions and layout placements live in Craft's project config, so prepare-fields and finalize deploy as YAML. Content and the plugin's migration state live in each environment's database, so content migration must run in every environment.
The recommended flow for a local → production pipeline (for example Docker with project-config/apply on deploy):
# 1. Locally: audit, prepare fields, migrate local content, and verify. php craft link-migrator/migrate/prepare-fields --force=1 php craft link-migrator/migrate/content --force=1 --create-backup=1 php craft link-migrator/migrate/status # Commit the project config changes and deploy. Do not deploy template # changes that render the native fields yet — they are still empty in # production at this point. # 2. On production, after project config is applied: adopt the deployed # native fields so this environment knows the source-to-target mappings. php craft link-migrator/migrate/adopt-prepared --dry-run=1 php craft link-migrator/migrate/adopt-prepared --force=1 # 3. On production: migrate content and verify. php craft link-migrator/migrate/content --force=1 --create-backup=1 php craft link-migrator/migrate/status # 4. Locally: update templates, finalize, then commit and deploy the # project config and template changes together. php craft link-migrator/migrate/mismatches php craft link-migrator/migrate/finalize --force=1 --acknowledge-mismatches=1
Local content migration in step 1 is required: finalize refuses fields whose content has not been migrated and verified in the environment where it runs. Ship template updates that render the native fields with the second deploy, after every environment has migrated its content — deploying them earlier would render empty native fields in production.
adopt-prepared matches each Hyper field to a native Link field named <sourceHandle>Native (use --field together with --target for a different handle) and records the mapping without creating or changing any fields, so it is safe with allowAdminChanges disabled. Fields that already have a mapping in that environment are skipped, the command warns when the matched field does not allow the link types the mapping needs, and it refuses to guess when several candidate handles exist (for example ctaLinkNative and ctaLinkNative2) — pass --field and --target to resolve those explicitly. It exits non-zero when nothing was adopted or previously recorded, so a misconfigured deploy fails loudly in scripts.
Deploy the finalize project config only after status is clean in every environment: applying YAML replays the layout cutover without the plugin's content verification, so finalizing before an environment has migrated its content would expose empty native fields there.
How the Migration Works
| Stage | What it does | Writes data? |
|---|---|---|
audit |
Discovers Hyper fields, mapping support, code references, and likely API mismatches. | No |
prepare-fields |
Creates native Link fields, places them beside their source fields in layouts, and records the mappings. | Yes |
adopt-prepared |
Records mappings for native Link fields that arrived through deployed project config, without creating fields. | Plugin state only |
content |
Copies supported values into prepared native fields and verifies saved values. | Yes |
status |
Shows each field's phase, target handle, and migration counters. | No |
finalize |
Reconciles live content, then removes source Hyper fields from layouts when every value is ready. | Yes |
prepare-fields, adopt-prepared, content, and finalize refuse CLI writes unless --force=1 is present. If template mismatches are found, finalization also requires --acknowledge-mismatches=1 after you have reviewed and accepted the template impact. Dry runs do not write field mappings, migration state, project config, or content.
Finalization does not delete Hyper fields. It removes them from field layouts and leaves the prepared native Link fields in place.
Supported Mappings
| Hyper type | Native Link type | Support |
|---|---|---|
| URL | URL | Full |
| Entry | Entry | Full |
| Asset | Asset | Full |
| Category | Category | Full |
| Full | ||
| Phone | Phone (tel) |
Full |
| Custom or plugin type | URL when a scalar URL is available | Partial; review required |
The migration also carries over label/text, target/new-tab behavior, URL suffix, title, class, ID, and rel where the installed Craft version supports them. Prepared target handles default to <sourceHandle>Native.
Unsupported or lossy cases
- Hyper fields that allow multiple links
- Embed-only data
- SMS links, because Craft's native Link field has no SMS type
- User, site, or plugin-specific link types without a native equivalent
- Custom fields attached to Hyper link types
Unsupported values are skipped and reported. Custom link data is included in optional backups but is not converted into native Link data.
Template Impact
Hyper and native Link values do not expose the same Twig and PHP APIs. Run the scanner before finalizing:
php craft link-migrator/migrate/mismatches
The command exits non-zero when it finds likely mismatches, making it useful in CI and deployment checklists.
Common changes include:
| Hyper | Native Link |
|---|---|
.text or .linkText |
.label |
linkValue |
.value or .url |
getElement() |
.element |
hasElement() |
Check .element directly |
| Hyper link classes | Short type handles such as entry, asset, or url |
getLink(), getHtml(), getData() |
Render or map explicitly |
The scanner is a guide, not proof that every integration is compatible. GraphQL output also changes. Read Template Impact before migrating production content.
Reports, Backups, and State
Audit, mismatch, prepare, content, and finalize runs each write a JSON report and log file to:
storage/runtime/link-migrator/
With --create-backup=1, content migration writes per-element source payloads to:
storage/runtime/link-migrator/backups/
Resumable per-element state is stored in {{%linkmigrator_migrations}}. Prepared source-to-target mappings are stored in {{%linkmigrator_fieldmappings}} only after prepare-fields or adopt-prepared writes them. Audit, status, and the Control Panel index remain read-only.
Use the informational summary at any time:
php craft link-migrator/migrate/rollback-info
This reports migrated, skipped, warning, error, and backup counts. It does not restore content automatically.
Safety Checklist
- Back up the database and project config before every non-dry run.
- Review audit warnings, unsupported fields, and mismatch results before continuing.
- Keep Hyper installed until reports are clean, templates are updated, and finalization has succeeded.
- Run content migration in each environment because Craft content is environment-specific.
- Verify the site and templates before removing Hyper from the project.
Support
- Bug reports: GitHub Issues. Include Craft, PHP, and Hyper versions plus the relevant JSON report.
- Changelog: CHANGELOG.md
- Template migration guide: docs/TEMPLATE-IMPACT.md
- Plugin Store description: docs/plugin-store-description.md
License
Link Migrator is released under the MIT License.
Screenshots
The migration wizard — audit, prepare, migrate, review, and finalize in one guided workflow.
Built by Luremo for the Craft CMS community.
