# Schema changes

Source: https://docs.externa.qiick.io/docs/schema-changes

Renaming fields, changing field types, or toggling the translatable flag on collections with existing content requires careful handling to avoid data loss. This page documents behaviour as of externa-core **1.2.1+**, the repair command for older drafts, and the type-change risks that remain open.

> Field renames are safe from externa-core **1.2.1** ([#218](https://github.com/qiick-io/externa-core/issues/218), [#223](https://github.com/qiick-io/externa-core/pull/223)). If you renamed fields on an older version, run the repair command in [Repairing older drafts](#repairing-older-drafts) right after upgrading, **before** the scheduler runs or anyone publishes.

---

## Renaming fields

### Current behaviour

Field values are stored internally by **field ID**, not by name (`app/Services/Collections/CollectionItemValuesWriter.php` in externa-core). This means:

- **Rename without pending drafts:** Content is preserved. The published API immediately reflects the new field name.
- **Rename with pending drafts (versioned collections):** From **1.2.1**, pending drafts and older revisions keep the renamed field's value, so publishing (manual or scheduled) and restoring revisions preserve content. On **1.2.0 and earlier**, the value was dropped because drafts and revisions stored a snapshot keyed by the old field name.

Fixed in [v1.2.1](https://github.com/qiick-io/externa-core/releases/tag/v1.2.1) ([#218](https://github.com/qiick-io/externa-core/issues/218), [#223](https://github.com/qiick-io/externa-core/pull/223)).

### Repairing older drafts

Drafts and revisions created before 1.2.1 may still hold a pre-rename field key. Right after deploying 1.2.1, **before** the scheduler runs or anyone publishes:

1. Dry run (default, no writes): `php artisan externa:repair-renamed-field-drafts`
2. Apply: `php artisan externa:repair-renamed-field-drafts --apply`

Mappings come from the CollectionField activity-log rename history. For orphaned keys with no history, pass `--map=collection:old=new` (collection slug or id; repeatable). The command never overwrites a value already stored under the new key, and publish-time behaviour is unchanged. Keys reported as **unmappable** belong to deleted fields and can be ignored.

If values were already lost by publishing on an older version, restore from a database backup taken before the rename, or re-enter them via the admin UI or [Collections API](/docs/collections-api).

### API breaking change

Renaming a field is a **breaking change** for API consumers. The Public CMS API, Collections API, and GraphQL all use the current field name as the key. Update your frontend or client code after renaming.

---

## Changing field types

### Current behaviour

Changing a field's type **does not convert stored values**. Until [core#219](https://github.com/qiick-io/externa-core/issues/219) ships:

- The admin UI **prevents** type changes once a field exists (the type dropdown is disabled).
- The server endpoint and AI assistant (`ManageCollections.updateField`) can still change it, leaving values unconverted and potentially breaking validation or rendering.

**Do not change field types** if the field has content, unless you are prepared to manually convert all values.

### Safe path

1. **Create a new field** with the desired type.
2. **Migrate content** from the old field to the new field:
   - Export the collection, transform the values, and re-import; or
   - Use the [Collections API](/docs/collections-api) or [Admin API](/docs/admin-api) to update each item programmatically.
3. **Delete the old field** after verifying the migration.

### Planned fix

[qiick-io/externa-core#219](https://github.com/qiick-io/externa-core/issues/219) will reject type changes on fields with stored values (admin + AI tool), returning a validation error. Type conversion tooling is tracked separately in [core#220](https://github.com/qiick-io/externa-core/issues/220).

---

## Toggling the translatable flag

### Current behaviour

Toggling `translatable` on a field with existing content **does not convert stored values**. Values keep their original shape:

- **Non-translatable → translatable:** Values remain scalars (e.g., `"Hello"`). The API will not return a locale map until you re-save the item.
- **Translatable → non-translatable:** Values remain locale maps (e.g., `{ "en": "Hello", "it": "Ciao" }`). The API and admin UI may break or show the raw map.

This is the same root cause as type changes: no automatic conversion.

### Safe path

Follow the same migration strategy as [type changes](#safe-path) above:

1. Create a new field with the desired `translatable` setting.
2. Migrate and transform values (flatten or expand locale maps as needed).
3. Delete the old field.

### Planned fix

[qiick-io/externa-core#219](https://github.com/qiick-io/externa-core/issues/219) will reject `translatable` changes on fields with stored values.

---

## Rename/convert tooling

[qiick-io/externa-core#220](https://github.com/qiick-io/externa-core/issues/220) tracks **safe type conversion with dry run** for fields with existing content (e.g., `text` → `textarea`, `string` → `number`, translatable toggle with automatic map creation/flattening). This is planned for externa-core **1.4.0**.

Until then, follow the manual migration paths above.

---

## Related pages

- [Collections](/docs/collections) — Collection and field CRUD
- [Field types](/docs/field-types) — Field type reference and validation
- [Content versioning](/docs/content-versioning) — Drafts, approvals, and revisions
- [Collections data model](/docs/collections-data-model) — EAV storage and field IDs
- [Collections API](/docs/collections-api) — Programmatic item updates for migration
- [Upgrade](/docs/upgrade) — Version-specific upgrade notes
