MishkaCmsCore. Runtime. Resources. Changes. SetLanguageGroupId
(Mishka CMS Core v0.0.1)
Copy Markdown
View Source
Automatically sets language_group_id for master collections.
This change handles the multilingual collection structure by setting the language_group_id to the record's own ID for master collections (collections without a language_group_id). This creates a self-referential relationship where master collections point to themselves, while translations point to their master collection's ID.
Behavior
Master collections: When
language_group_idis nil during creation, the change automatically sets it to the record's own ID after insertion using an after_action hook.Translation collections: When
language_group_idis explicitly provided (pointing to a master collection), the changeset passes through unchanged.
Implementation Details
The change uses after_action to perform the update because we need the record's ID
first (generated during insertion). The update:
- Uses the record's site_id as the tenant context
- Bypasses authorization (internal system operation)
- Updates only the language_group_id field
Usage
create :create do
change MishkaCmsCore.Runtime.Resources.Changes.SetLanguageGroupId
endIn the same write
Pass before_action?: true and the group is written WITH the row instead of by a second write
after it:
create :create do
change {MishkaCmsCore.Runtime.Resources.Changes.SetLanguageGroupId, before_action?: true}
end
update :update do
change {MishkaCmsCore.Runtime.Resources.Changes.SetLanguageGroupId, before_action?: true}
endThe id is already on a create's changeset by the time a before-action runs, so nothing waits for
the insert. MishkaCmsCore.Runtime.Page needs this: its :update validates and signs code,
versions the page, broadcasts it and queues a compile, so the second write turned one save into
two versions, two broadcasts and two compile jobs.
On an update the same rule keeps a group from being emptied — a record whose group is set to nil becomes the master of a group of its own rather than a record with no group at all.
On a restore it gives a group to a record archived before it had one. A
prevent_archived_*_updates trigger refuses every other write to an archived row, so the
restoring write is the first one that can:
update :unarchive do
change set_attribute(:archived_at, nil)
change {MishkaCmsCore.Runtime.Resources.Changes.SetLanguageGroupId, before_action?: true}
endExample Flow
Creating a master collection (English):
# User provides: language_group_id = nil
Ash.create(Collection, %{name: "Tech", language: "en", ...}, tenant: site_id)
# After insert: language_group_id automatically set to record.id
# Result: id = "abc123", language_group_id = "abc123"Creating a translation (Persian):
# User provides: language_group_id = "abc123" (master's ID)
Ash.create(Collection, %{name: "فناوری", language: "fa", language_group_id: "abc123", ...})
# After insert: language_group_id remains "abc123"
# Result: id = "def456", language_group_id = "abc123"Database Implications
This creates a structure where:
- Master collections:
id == language_group_id - Translations:
language_group_id == master_collection.id - Query all variants:
WHERE language_group_id = X(includes master and all translations)
Summary
Functions
Applies the language_group_id assignment logic.
Functions
Applies the language_group_id assignment logic.
Parameters
changeset- The Ash changeset for the collection being createdopts-before_action?: truewrites the group with the row (see "In the same write")_context- Change context (unused)
Returns
Modified changeset with a hook that sets the group if language_group_id is nil, otherwise returns changeset unchanged