PHPackages                             luremo/craft-link-migrator - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. luremo/craft-link-migrator

ActiveCraft-plugin[Database &amp; ORM](/categories/database)

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.

1.0.0(1mo ago)00MITPHPPHP ^8.2CI passing

Since Jul 5Pushed 1w agoCompare

[ Source](https://github.com/LuremoDigital/Link-migration)[ Packagist](https://packagist.org/packages/luremo/craft-link-migrator)[ Docs](https://github.com/LuremoDigital/Link-migration)[ RSS](/packages/luremo-craft-link-migrator/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (2)Versions (7)Used By (0)

 [![Link Migrator icon](https://camo.githubusercontent.com/a3e8618a889b0db66fa0470a63929157b9cff3e5749134c100172a01507688b8/68747470733a2f2f706c7567696e69636f6e732e63726166742d63646e2e636f6d2f6c696e6b2d6d69677261746f727a7577667a6272747465646c636d6b6463617a7362636c70696c7169716b6268707568742e7376673f31373834323231323133)](https://camo.githubusercontent.com/a3e8618a889b0db66fa0470a63929157b9cff3e5749134c100172a01507688b8/68747470733a2f2f706c7567696e69636f6e732e63726166742d63646e2e636f6d2f6c696e6b2d6d69677261746f727a7577667a6272747465646c636d6b6463617a7362636c70696c7169716b6268707568742e7376673f31373834323231323133)

Link Migrator
=============

[](#link-migrator)

 Migrate Verbb Hyper or Typed Link Field fields and content to Craft CMS native Link fields — safely, in stages, from the CLI.

 [![Craft Plugin Store](https://camo.githubusercontent.com/3670870fb9941850ee18d036f1536fa3a286a16de7694cfe7b097493bb2bfeae/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4372616674253230506c7567696e25323053746f72652d6c696e6b2d2d6d69677261746f722d4535343232422e737667)](https://plugins.craftcms.com/link-migrator) [![Craft CMS 5.x](https://camo.githubusercontent.com/fd9919b87607a22e2f12aed4391ef68503f642a4616d0a4c8ce019de4ee68de6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4372616674253230434d532d352e782d4535343232422e737667)](https://camo.githubusercontent.com/fd9919b87607a22e2f12aed4391ef68503f642a4616d0a4c8ce019de4ee68de6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4372616674253230434d532d352e782d4535343232422e737667) [![PHP 8.2+](https://camo.githubusercontent.com/c2588b5670f2c910b8cc849ace22a22efda8956b7c2f797d11d2096bbfc7b1f5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322532422d3737374242342e737667)](https://camo.githubusercontent.com/c2588b5670f2c910b8cc849ace22a22efda8956b7c2f797d11d2096bbfc7b1f5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322532422d3737374242342e737667) [![Free edition](https://camo.githubusercontent.com/ee63215352db8021e91a9eb4dce99e4227a60602a13a811dacf70d4035abd557/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f65646974696f6e2d467265652d3045413545392e737667)](https://camo.githubusercontent.com/ee63215352db8021e91a9eb4dce99e4227a60602a13a811dacf70d4035abd557/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f65646974696f6e2d467265652d3045413545392e737667) [![MIT license](https://camo.githubusercontent.com/f91949d5d9dfb97ed969c3a36913f3ee8bde954806b2357b484026f141e9d0fe/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d3046313732412e737667)](https://camo.githubusercontent.com/f91949d5d9dfb97ed969c3a36913f3ee8bde954806b2357b484026f141e9d0fe/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d3046313732412e737667)

---

**Link Migrator** gives Craft teams a staged CLI workflow from [Verbb Hyper](https://plugins.craftcms.com/hyper) or [Typed Link Field](https://plugins.craftcms.com/typedlinkfield) to Craft's native Link field. 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. 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
--------

[](#features)

- **CLI only**: run the staged workflow locally, in CI, or from deployment scripts.
- **Audit before writing**: inspect Hyper and Typed Link 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- and Typed-Link-only properties and methods that need updating.
- **Track every run**: write human-readable logs and JSON reports to Craft's runtime storage.
- **Automate safely**: use dry runs, explicit write confirmation, and reports in deployment scripts.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Craft CMS 5.3+
- Each source plugin installed and enabled until preparation, content migration, and finalization are complete
- Craft CMS 5.6+ recommended for the full native Link advanced-field set

Installation
------------

[](#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.

Migration Walkthrough
---------------------

[](#migration-walkthrough)

Use this sequence for a single environment. Back up the database and project config first, keep Hyper installed, and inspect each generated report before proceeding.

```
# 1. Audit is read-only. Fix or explicitly accept anything in its report.
php craft link-migrator/migrate/audit
php craft link-migrator/migrate/mismatches

# 2. Preview, then create native Link fields beside the Hyper fields.
php craft link-migrator/migrate/prepare-fields --dry-run=1
php craft link-migrator/migrate/prepare-fields --force=1

# 3. Preview, then copy and verify content. Backups are written only by
#    the non-dry run.
php craft link-migrator/migrate/content --dry-run=1
php craft link-migrator/migrate/content --force=1 --create-backup=1 --batch-size=100
php craft link-migrator/migrate/status

# 4. Update templates for native Link values, then scan again. A non-zero
#    exit from mismatches means potential changes were found and must be reviewed.
php craft link-migrator/migrate/mismatches

# 5. Preview, then remove Hyper fields from layouts. The fields and their
#    source values are not deleted.
php craft link-migrator/migrate/finalize --dry-run=1
php craft link-migrator/migrate/finalize --force=1 --acknowledge-mismatches=1
```

`audit`, `status`, and `mismatches` do not change fields, content, or migration mappings. `mismatches` intentionally exits non-zero when it finds potential source Link API usage. Every non-dry-run write command requires `--force=1`; `finalize` additionally requires `--acknowledge-mismatches=1` when the scanner finds mismatches. A successful `content` run can still return non-zero for warnings, so resolve its report before finalizing.

### Migrate one field

[](#migrate-one-field)

Pass the source Hyper field handle to every stage:

```
php craft link-migrator/migrate/audit --field=ctaLink
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/status --field=ctaLink
php craft link-migrator/migrate/finalize --field=ctaLink --force=1 --acknowledge-mismatches=1
```

Multi-Environment Deployment
----------------------------

[](#multi-environment-deployment)

`prepare-fields` and `finalize` change field definitions and layouts, so run them only in the environment that authors project config. Commit and deploy the resulting project-config changes. Content and the plugin's mapping state are database data, so run `adopt-prepared` and `content` in every deployed environment.

```
# 1. In the project-config authoring environment: prepare fields and migrate
#    its local content. Commit the generated project-config changes.
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

# 2. First deploy: apply the prepared-field project config everywhere, but
#    keep templates rendering Hyper fields. On each downstream environment:
php craft project-config/apply
php craft link-migrator/migrate/adopt-prepared --dry-run=1
php craft link-migrator/migrate/adopt-prepared --force=1
php craft link-migrator/migrate/content --dry-run=1
php craft link-migrator/migrate/content --force=1 --create-backup=1
php craft link-migrator/migrate/status

# 3. Only after every environment's content is verified: update templates in
#    the authoring environment, then finalize and commit its project config.
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

# 4. Second deploy: deploy the finalized project config and native-Link
#    template changes together.
php craft project-config/apply
```

`adopt-prepared` records the mapping for a deployed native field without changing fields or layouts, so it can run where `allowAdminChanges` is disabled. It expects `Native`; use `--field` and `--target` for a different handle. Do not deploy the final layout change until content is verified everywhere: project-config application cannot perform that per-environment verification for you.

How the Migration Works
-----------------------

[](#how-the-migration-works)

StageWhat it doesWrites data?`audit`Discovers Hyper and Typed Link Field sources, 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 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 source fields. It removes them from field layouts and leaves the prepared native Link fields in place.

Supported Mappings
------------------

[](#supported-mappings)

Typed Link Field support applies when its plugin is installed and enabled: `url`, `email`, `tel`, `entry`, `asset`, and `category` migrate to their matching native Link types. Typed `custom` values migrate as native URLs only when the custom type is enabled and the prepared native URL configuration accepts the normalized value. Root-relative URLs, anchors, and custom schemes are enabled only when Typed Link has custom validation disabled and the installed Craft version supports the matching setting; stale disabled custom settings never broaden native input. Missing query prefixes normalize to `?`, while an unrepresentable suffix is stripped without discarding the link. Custom/default text, target, automatic `noopener noreferrer`, title, ARIA label, query suffixes, and valid element-source restrictions are retained where the prepared target supports them. Typed Link's custom-text required/max-length constraints have no native equivalent and are reported as lossy. Stripped attributes produce per-element warnings and optional backups, and block readiness. Unknown enabled types, explicit-empty element sources, and entirely stale element-source restrictions refuse prepare rather than broadening access. `site`, `user`, Commerce/event, and other registered Typed Link types are skipped with their complete hydrated source payload retained in an optional backup.

Hyper typeNative Link typeSupportURLURLFullEntryEntryFullAssetAssetFullCategoryCategoryFullEmailEmailFullPhonePhone (`tel`)FullCustom or plugin typeURL when a scalar URL is availablePartial; review requiredThe 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 `Native`.

### Unsupported or lossy cases

[](#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
---------------

[](#template-impact)

Source 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. Source-only API names are scoped to audited field handles and simple Twig/PHP aliases, so unrelated application methods do not block finalization. Native no-argument `getLink()`, `getElement()`, and `getUrl()` calls are left alone.

Common changes include:

Source APINative Link`.text` or `.linkText`Usually `.label`; review source fallback behaviour`linkValue``.value` or `.url`Typed Link `customText``.label`Typed Link `getCustomText()`, `getDefaultText()`, `getText()`, `getIntrinsicText()`Review custom/intrinsic/default/fallback precedence explicitly; `.label` is not always equivalentTyped Link `customQuery``.urlSuffix`Typed Link `linkedId`, `linkedTitle`, `linkedUrl``.element`, `.value`, `.label`, or `.url` as appropriateTyped Link `linkedSiteId`, `getSiteId()`, cross-site helpersNative Link cannot preserve a target site that differs from the owner site`getElement()``.element`; remove Typed Link’s `ignoreStatus` argument`hasElement()`Check `.element` directlyTyped Link `getRawLinkAttributes()` / `getLinkAttributes()``.attributes` on Craft 5.9+, or map attributes manually; merge passed overrides explicitlyTyped Link `getUrl(options)``.url`, with URL modifications handled explicitlyTyped Link setting and empty-state helpers, including Twig getter propertiesRead the native value/owner element directlyHyper link classesShort type handles such as `entry`, `asset`, or `url``getLink()` with source-specific arguments, `getHtml()`, `getData()`Render or map explicitlyThe scanner is a guide, not proof that every integration is compatible. GraphQL output also changes, so review template integrations before migrating production content.

Reports, Backups, and State
---------------------------

[](#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 and status 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
----------------

[](#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 or Typed Link Field installed and enabled 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
-------

[](#support)

- **Bug reports:** [GitHub Issues](https://github.com/LuremoDigital/Link-migration/issues). Include Craft, PHP, source-plugin versions, and the relevant JSON report.
- **Changelog:** [CHANGELOG.md](CHANGELOG.md)
- **Template migration guide:** [docs/TEMPLATE-IMPACT.md](docs/TEMPLATE-IMPACT.md)
- **Plugin Store description:** [docs/plugin-store-description.md](docs/plugin-store-description.md)

License
-------

[](#license)

Link Migrator is released under the [MIT License](LICENSE.txt).

Built by [Luremo](https://github.com/LuremoDigital) for the Craft CMS community.

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance95

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity50

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 98.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

47d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/63ec9caba33d328dd1c6b5000424c177a2733c3dd28049c2b6546e329336c007?d=identicon)[Luremo](/maintainers/Luremo)

---

Top Contributors

[![LuukRM2000](https://avatars.githubusercontent.com/u/159145313?v=4)](https://github.com/LuukRM2000 "LuukRM2000 (71 commits)")[![LuremoDigital](https://avatars.githubusercontent.com/u/280716929?v=4)](https://github.com/LuremoDigital "LuremoDigital (1 commits)")

---

Tags

clilinkmigrationcraftcmscraft-pluginhyper

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/luremo-craft-link-migrator/health.svg)

```
[![Health](https://phpackages.com/badges/luremo-craft-link-migrator/health.svg)](https://phpackages.com/packages/luremo-craft-link-migrator)
```

###  Alternatives

[verbb/hyper

A user-friendly links field for Craft.

24153.5k15](/packages/verbb-hyper)[verbb/formie

The most user-friendly forms plugin for Craft.

101400.6k79](/packages/verbb-formie)[verbb/vizy

A flexible visual editor field for Craft.

4251.5k1](/packages/verbb-vizy)[dgrigg/craft-migration-assistant

Create content migrations at the click of a mouse. Keep environments in sync with ease.

2783.3k](/packages/dgrigg-craft-migration-assistant)[page-8/craft-manytomany

A field type to manage relationships from both sides.

8214.2k1](/packages/page-8-craft-manytomany)[verbb/events

A full-featured plugin for event management and ticketing.

2312.1k](/packages/verbb-events)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
