Skip to content

Product Variations Management (Admin)

Flow ID: AD-16 Module(s): variations Complexity: High Last Updated: 2026-04-04

Business Context

Product variations allow grouping products by shared dimensions (e.g., Size, Color, Material) so customers can switch between related products on the storefront. The admin panel provides three management areas: variation group definitions (the dimension types), variation values (the options within each dimension), and product variation groups (which products belong together and which is the master). The entire feature is gated by the enableProductVariations config flag (default: false). When disabled, variation menu items are hidden and product-level variation UI is suppressed.

Architecture: Entirely legacy HMVC -- no modern Domain/REST layer exists for admin variations. The HMVC module lives in ecommercen/variations/ with controllers, models, DTOs, and helpers. A separate API controller (AdvApiVariationsController) serves the Vue frontend for product-level variation editing. The admin Vue app (AdminVariationsPage) manages groups/values, while AdminVariationProductsPage handles per-product variation assignment within the product edit form.


Database Schema

6 Tables

TablePurposeKey Columns
variation_groupsDimension definitions (Size, Color, etc.)id, display_type, step, display_position, priority, force_display_all_values
variation_groups_muiMUI names per languagevariation_group_id, name, lang
variation_valuesOptions within a groupid, variation_group_id, display_value, priority
variation_values_muiMUI names per languagevariation_value_id, name, lang
product_variationsProduct-to-group assignmentsid, group_id, product_id, is_master, priority
product_variations_muiMUI names for product variation groupsgroup_id, name, lang
product_variation_valuesProduct-to-variation-value linksproduct_id, variation_id (unique composite)

Key relationships: variation_values.variation_group_id -> variation_groups.id; product_variations.group_id is an auto-incremented group identifier (not an FK to variation_groups); product_variation_values.variation_id -> variation_values.id.

Important distinction: variation_groups defines dimension types (e.g., "Size"), while product_variations.group_id defines product groupings (e.g., "Nike Air Max Family"). These are separate concepts with separate MUI tables.


Feature Gate

Config: $config['enableProductVariations'] = false; in application/config/app.php

When disabled:

  • Admin menu hides the "variations" route via AdminMenu::parseProductVariationMenu()
  • AdvVariationsProductsAdmin and AdvVariationsPageAdmin redirect to advisable
  • Product edit form hides the <admin-variation-products-page> component
  • Batch update actions assign_products_to_variation_group and assign_products_to_new_variation_group are suppressed

Authorization

Base class: AdvVariationsAdminBase extends Admin_c, requires roles: AUTH_ROLE_ADVISABLE, AUTH_ROLE_ADMIN, or AUTH_ROLE_PRODUCTS.

All four admin controllers inherit this base and share the same role check. The API controller (AdvApiVariationsController) extends Front_c (no admin auth -- it serves the product edit page's Vue component which is already behind admin auth).


Area 1: Variation Groups (Dimension Types)

Controller: ecommercen/variations/controllers/AdvVariationsAdmin.phpPage controller: ecommercen/variations/controllers/AdvVariationsPageAdmin.phpModel: ecommercen/variations/models/AdvVariationGroupsModel.phpView: application/views/admin/variations/vue.php (Vue SPA mount point) Vue component: assets/admin/js/variations/AdminVariationsPage.vueRoute: /variations/variations

Display Types

Defined in AdvVariationsHelper::getVariationTypes():

KeyDescriptionFrontend rendering
textPlain text labelsText buttons/pills
hex-colorColor swatchesVariationHexSelector.vue renders color circles
imageImage thumbnailsVariationImageSelector.vue renders image pills
text-range-per-valueNumeric ranges by valueGroups values into ranges based on display_value and step
text-range-per-countNumeric ranges by countGroups values into ranges of step items each

Display Positions

Defined in AdvVariationsHelper::getVariationGroupDisplayPositionTypes():

ValueMeaning
0ALL -- show on product page and in category filters
1PRODUCT only -- show on product page, hide from filters
2FILTER only -- show in category filters, hide from product page
3NONE -- hidden everywhere (data-only)

CRUD Operations

Add (POST /variations/variations/add):

  1. Validates: display_type (required), step (numeric), display_position, priority, force_display_all_values
  2. Validates MUI: name_{lang} required for each admin language
  3. AdvVariationGroupsModel::add() inserts into variation_groups + variation_groups_mui
  4. Returns JSON {id} on success

Edit (POST /variations/variations/edit):

  1. Same validation + requires id
  2. AdvVariationGroupsModel::update() updates master row + upserts MUI via updateOrInsertMui()
  3. Returns JSON {id} on success

Order (POST /variations/variations/order):

  1. Accepts JSON array of {id, priority} pairs
  2. AdvVariationGroupsModel::order() batch-updates priority column

Delete: Handled by AdvVariationGroupsModel::delete() -- checks canDelete() first: a group cannot be deleted if it has child variation values. Deletion cascades to variation_groups_mui within a transaction.

Page Data Endpoint

GET /variations/variations/getPageData returns all data needed by the Vue SPA:

  • variationGroups -- all variation_groups rows
  • variationGroupsMui -- all variation_groups_mui rows
  • variationValues -- all values joined with group info
  • variationValuesMui -- all value MUI for current language
  • variationTypes -- display type options
  • variationGroupDisplayPositionTypes -- position type options
  • configs, userRoles, configLanguages, translations

Area 2: Variation Values (Options)

Controller: ecommercen/variations/controllers/AdvVariationsValuesAdmin.phpModel: ecommercen/variations/models/AdvVariationValuesModel.phpRoute: /variations/values/{action}

Values are managed inline within the groups Vue page. Clicking a group opens its values list (VariationValuesList.vue), then values can be added/edited via VariationValuesForm.vue.

CRUD Operations

Add (POST /variations/values/add):

  1. Validates: display_value (trim), MUI name_{lang} (required per language)
  2. Validates variation_group_id exists via AdvVariationGroupsModel::getMain()
  3. AdvVariationValuesModel::add() inserts into variation_values + variation_values_mui

Edit (POST /variations/values/edit):

  1. Same validation + requires id
  2. AdvVariationValuesModel::update() updates master + upserts MUI

Order (POST /variations/values/order):

  1. Accepts JSON array of {variation_value_id, priority} pairs
  2. Batch-updates priority column

Delete (POST /variations/values/delete):

  1. Accepts variation value ID as JSON
  2. canDelete() check: if the value is referenced in product_variation_values, deletion is blocked and the referencing product IDs are returned
  3. On success: deletes from variation_values_mui + variation_values in a transaction

Batch Actions on Values

Two batch actions available via BatchAction.vue when values are selected:

Move to Another Group (BatchActionMoveValueToAnotherGroup.vue):

  • Calls api/variations/batchActionMoveVariationValues
  • Model: AdvVariationModel::moveVariationValuesToAnotherGroup() -- raw SQL UPDATE with JOIN to reassign variation_values.variation_group_id
  • Confirmation via SweetAlert2; page reloads after success

Mass Delete Values (BatchActionMassDeleteValues.vue):

  • Calls api/variations/batchActionMassDeleteValues
  • Model: AdvVariationModel::massDeleteVariationValues() -- deletes from both variation_values and variation_values_mui
  • No referential integrity check (unlike single-value delete) -- caution: can orphan product_variation_values rows

Area 3: Product Variation Groups

Controller: ecommercen/variations/controllers/AdvVariationsProductsAdmin.phpModel: ecommercen/variations/models/AdvVariationModel.phpViews: application/views/admin/variations/list.php, group.php, group_create.phpVue entry: assets/admin/js/variations/store/variations-page.jsRoutes: /variations/products, /variations/products/create, /variations/products/edit/{groupId}, /variations/products/delete/{groupId}

Product variation groups link multiple products together (e.g., same shoe in different colors). Each group has a MUI name and a designated master product.

List Page

AdvVariationsProductsAdmin::index():

  • Paginated list (20 per page) from product_variations_mui for current language
  • Displays group ID, name, edit/delete actions
  • Server-rendered table (no Vue)

Create

AdvVariationsProductsAdmin::create():

  • Renders group_create.php -> mounts AdminProductVariationGroup Vue component
  • Vue receives JSON state: configLanguages, allVariation (all variation values with groups), allAttributes, isNewGroup=true
  • On submit: parses variationProducts (product IDs with serialized variation selections), variationGroupName (MUI), masterProductId
  • AdvVariationModel::createVariationProductGroup():
    1. Begins transaction
    2. Auto-generates new group_id via createNewGroupId() (SELECT MAX + 1)
    3. For each product: clears any existing variation data, inserts into product_variations and product_variation_values
    4. Inserts MUI names into product_variations_mui
    5. Commits or rolls back

Edit

AdvVariationsProductsAdmin::edit($groupId):

  • Loads existing group data via getVariationProductsByGroupId() -> returns VariationGroupProductDTO[]
  • Renders group.php -> mounts AdminProductVariationGroup Vue component with existing data
  • On submit: processAllVariationGroupProducts():
    1. For each product in POST: delete-then-reinsert pattern (processProductVariation)
    2. Removes products no longer in the submitted list via removeOtherProductIds()
    3. Updates MUI names separately

Delete

AdvVariationsProductsAdmin::delete($groupId):

  • deleteProductVariationGroup() removes all product_variations and product_variations_mui rows for the group in a transaction
  • Note: does NOT clean up product_variation_values for affected products

Master Product Management

Each product group designates one product as master (is_master = 1). The master product:

  • Appears as the primary product in category listings (non-master variants are hidden via ProductVariationsListingTrait)
  • Serves as the canonical URL/display product

updateGroupMaster(): Transactional unset-all then set-one pattern. setAnotherProductAsMaster(): When removing the current master, auto-promotes the next product by priority.


Area 4: Per-Product Variation Assignment (Product Edit Page)

Vue component: assets/admin/js/products/variations/AdminVariationProductsPage.vueAPI controller: ecommercen/api/controllers/AdvApiVariationsController.phpApplication wrapper: application/modules/api/controllers/Api_variations.phpRoutes: /api/variations/{action}

This component appears as a tab in the product edit form (when enableProductVariations is true). It allows:

  1. Selecting which variation groups apply to this product (multiselect dropdown)
  2. Choosing specific variation values per group (via VariationGroupQuickActions)
  3. Viewing/managing the product's variation group (modal with full group management)

Internal API Endpoints

MethodActionPurpose
POSTgetVariationGroupProductsApiGet all products in a product's variation group
POSTupdateProductVariationSave variation value selections for a product
POSTupdateVariationGroupMasterChange master product in a group
POSTgetAllVariationGet all variation values with group info
POSTgetVariationGroupMuiGet MUI names for a group
POSTgetVariationGroupNameGet group name by product ID
POSTgetVariationGroupIdGet group ID by product ID
POSTgetProductVariationsGet variation data for multiple product IDs
POSTdeleteProductFromVariationGroupRemove product from its group
POSTaddVariationProductToGroupAdd product to existing group, or create new group
POSTbatchActionMoveVariationValuesMove variation values between groups
POSTchangeVariationGroupProductsPriorityReorder products within a group
POSTbatchActionMassDeleteValuesBulk delete variation values
POSTupdateProductVariationGroupNameMuiUpdate group MUI names
POSTupdateProductStockUpdate stock for a product code (SKU)
POSTgetVariationByVariationValueIdLookup variation details by value ID

Validation Rules

All endpoints validate via CI form_validation. Responses follow sendOutput()/sendError() pattern:

  • Success: {data} with 200
  • Validation error: {errors} with 400
  • Business rule violation: {error} with 422 (e.g., product already in another group, empty variation group names)

Area 5: Batch Product Assignment (Product Listing)

Controller: ecommercen/eshop/controllers/Adv_products_admin.php (batch update actions) View: application/views/admin/products/batch_update.php

Two batch actions on the product listing page:

Assign to Existing Group

Action: assign_products_to_variation_group

  1. Admin selects products and chooses an existing variation group from dropdown
  2. batchActionDropdown() provides the group combo (name + group_id from product_variations_mui)
  3. Pre-check: if any selected product already belongs to a variation group, the entire batch is rejected with error message
  4. batchAssignProductsToVariationGroup(): insert_batch into product_variations (all as non-master)

Assign to New Group

Action: assign_products_to_new_variation_group

  1. Admin selects products, provides MUI group names per language
  2. If any products are already in groups, manageExistingProductsInGroups() handles removal:
    • If product is master and group has >1 member: auto-promotes another member, then removes
    • If product is the only member: deletes entire group
    • If product is non-master: just removes from group
  3. batchAssignProductsToNewVariationGroup(): creates new group_id, inserts all products (first product becomes master), inserts MUI names

DTOs

Three DTOs in ecommercen/variations/DTO/ provide typed data transfer:

VariationGroupProductDTO: Represents a product within a variation group with its assigned variation values. Properties: id, product_id, group_id, variation_value_id, variation_group_id, variation_value_name, variation_group_name, is_master, priority.

VariationProductDTO: Represents a variation group with all its values for a product. Properties: variationGroupName, variationGroupId, variationDisplayType, variationGroupStep, variationGroupDisplayPosition, variationGroupOrder, variationGroupForceDisplayAllValues, variations[] (array of VariationValueDTO), variationProductVariationId, variationProductVariationGroupId.

VariationValueDTO: Represents a single variation value. Properties: variationId, variationName, variationDisplayValue, variationOrder.


Frontend Architecture (Admin Vue)

Variation Groups/Values Page

Entry point: assets/admin/js/variations/store/variations-page.js mounts the Vue app.

Component tree:

AdminVariationsPage
  ├── VariationGroupList     (list all groups, drag-to-reorder, click to open values)
  ├── VariationGroupForm     (add/edit group with display type, step, position, MUI names)
  ├── VariationValuesList    (list values for selected group, drag-to-reorder, batch actions)
  │   └── BatchAction
  │       ├── BatchActionMoveValueToAnotherGroup
  │       └── BatchActionMassDeleteValues
  └── VariationValuesForm    (add/edit value with display_value and MUI names)

Product Variation Group Page

Entry point: same variations-page.js, also mounts AdminProductVariationGroup.

Component tree:

AdminProductVariationGroup
  ├── AdminProductVariationGroupSearch     (product search to add)
  ├── AdminProductVariationGroupList       (product cards in group)
  │   └── AdminProductVariationGroupCard   (individual product with variation selectors)
  │       ├── AdminProductVariationGroupVariations  (variation value dropdowns)
  │       ├── AdminProductVariationGroupMaster      (master toggle)
  │       └── AdminProductVariationGroupActions     (remove, stock edit, priority)
  └── AdminProductVariationGroupInput      (hidden inputs for form submission)

Per-Product Variation (Product Edit Page)

Component: AdminVariationProductsPage.vue (mounted in product edit template). Uses Vuex stores: product (product store) and variation (variation store via variationStore.js).


Storefront Integration

Listing Behavior

ProductVariationsListingTrait (used by category/vendor/search controllers):

  • processProductsQueryForVariationMaster(): Filters product listings to show only master products; non-master variants are hidden
  • fillVariationMasterInfo(): Adds group name and member count to master product display
  • renderListingVariationGroupCombosJsonData(): Provides variation group combos for listing-level variation selectors

Filter Behavior

ProductVariationsListingTrait::initializeVariationFiltersJson():

  • Query string parameter per language: el -> προιοντα-με, en -> products-with
  • Registry setting VARIATIONS.MERGE_DUPLICATE_VALUES_BY_NAME controls whether values with identical names across groups are merged in filters
  • Active filters validated against all variation records; invalid combinations trigger 404

Product Page Behavior

ProductVariationsTrait::getVariationGroupProducts():

  • Retrieves all products in the same variation group
  • Merges live product data (prices, stock, images, URLs) via mergeProductVariationsWithLiveData()
  • Returns unique products with variation metadata for the Vue variation selector

Registry Settings

GroupKeyPurpose
VARIATIONSMERGE_DUPLICATE_VALUES_BY_NAMEWhen true, variation values with the same name across groups are merged in filter display

Key Files Reference

CategoryPath
Controllersecommercen/variations/controllers/AdvVariationsAdminBase.php
ecommercen/variations/controllers/AdvVariationsAdmin.php
ecommercen/variations/controllers/AdvVariationsValuesAdmin.php
ecommercen/variations/controllers/AdvVariationsProductsAdmin.php
ecommercen/variations/controllers/AdvVariationsPageAdmin.php
API Controllerecommercen/api/controllers/AdvApiVariationsController.php
App Wrapperapplication/modules/api/controllers/Api_variations.php
Modelsecommercen/variations/models/AdvVariationGroupsModel.php
ecommercen/variations/models/AdvVariationValuesModel.php
ecommercen/variations/models/AdvVariationModel.php
DTOsecommercen/variations/DTO/VariationGroupProductDTO.php
ecommercen/variations/DTO/VariationProductDTO.php
ecommercen/variations/DTO/VariationValueDTO.php
Helperecommercen/variations/helpers/AdvVariationsHelper.php
Traitsecommercen/eshop/traits/ProductVariationsTrait.php
ecommercen/eshop/traits/ProductVariationsListingTrait.php
Viewsapplication/views/admin/variations/vue.php
application/views/admin/variations/list.php
application/views/admin/variations/group.php
application/views/admin/variations/group_create.php
Vue (Groups/Values)assets/admin/js/variations/AdminVariationsPage.vue
assets/admin/js/variations/VariationGroupList.vue
assets/admin/js/variations/VariationGroupForm.vue
assets/admin/js/variations/VariationValuesList.vue
assets/admin/js/variations/VariationValuesForm.vue
Vue (Batch Actions)assets/admin/js/variations/batchActions/BatchAction.vue
assets/admin/js/variations/batchActions/value/BatchActionMoveValueToAnotherGroup.vue
assets/admin/js/variations/batchActions/value/BatchActionMassDeleteValues.vue
Vue (Product Groups)assets/admin/js/variations/productGroups/AdminProductVariationGroup.vue
assets/admin/js/variations/productGroups/AdminProductVariationGroupList.vue
assets/admin/js/variations/productGroups/AdminProductVariationGroupCard.vue
assets/admin/js/variations/productGroups/AdminProductVariationGroupSearch.vue
assets/admin/js/variations/productGroups/AdminProductVariationGroupMaster.vue
assets/admin/js/variations/productGroups/AdminProductVariationGroupActions.vue
assets/admin/js/variations/productGroups/AdminProductVariationGroupVariations.vue
assets/admin/js/variations/productGroups/AdminProductVariationGroupInput.vue
Vue (Per-Product)assets/admin/js/products/variations/AdminVariationProductsPage.vue
assets/admin/js/products/variations/ProductVariationInput.vue
assets/admin/js/products/variations/VariationGroupQuickActions.vue
Vuex Storeassets/admin/js/variationStore.js
assets/admin/js/variations/store/state.js
assets/admin/js/variations/store/actions.js
assets/admin/js/variations/store/mutations.js
assets/admin/js/variations/store/getters.js
assets/admin/js/variations/store/variations-page.js
Routesapplication/config/routes.php (lines 351-376, 573-576)
Configapplication/config/app.php (enableProductVariations)
Admin Menuapplication/libraries/AdminMenu.php (parseProductVariationMenu)