[Feature]: Introduce "variant" as a first-class product type with nested row expansion in the product list
Pre-submission checklist
- I have searched the existing issues and this feature has not already been requested.
Is your feature request related to a problem? Please describe.
UnoPim currently has two product types — simple and configurable — defined in packages/Webkul/Product/src/Config/product_types.php. Products that are children of a configurable (i.e. variants) are stored in the products table as type = simple with a non-null parent_id. This means there is no way to distinguish a standalone simple product from a variant at a glance, or to filter on that distinction in the product list.
Concretely:
- The Type filter dropdown in the product list only offers
SimpleandConfigurable. There is no way to filter to "only top-level products" or "only variants". - Variant rows appear flat in the product list alongside their parent configurables, with no visual relationship between them.
- Exporting or bulk-editing requires manually identifying which simple products are variants vs. true standalone products.
Describe the solution you'd like
1. A variant product type
Introduce variant as a recognised value in the type system. When a product is associated to a configurable parent (i.e. parent_id IS NOT NULL), its type should be stored or derived as variant rather than simple. This makes the distinction explicit and queryable.
The product_types.php config would gain a third entry:
'variant' => [
'key' => 'variant',
'name' => 'product::app.type.variant',
'class' => 'Webkul\Product\Type\Simple', // reuses Simple behaviour
'sort' => 3,
],The existing TypeFilter and ParentFilter classes in Webkul/Product/src/Filter/Database/Property/ already provide the query infrastructure to support this cleanly.
2. Filterable type options that reflect parent/child state
With variant as a recognised type, the Type dropdown filter in the product list datagrid (ProductDataGrid.php) would expose three meaningful options:
- Simple — top-level products with no parent (standalone)
- Configurable — parent products with variants attached
- Variant — child products associated with a configurable
This allows users to quickly isolate standalone products vs. variant children, which is particularly useful for bulk editing, exports, and completeness review.
3. Nested variant expansion in the product list
In the product list view, configurable product rows should be expandable to reveal their variants inline, without leaving the page. Clicking an expand toggle on a configurable row would show its child variant rows nested below, each displaying SKU, option values (e.g. colour, size), status, and completeness.
This removes the need to open each configurable product individually to see what variants exist. The expand/collapse state would be per-row and client-side.
Describe alternatives you've considered
- Filtering by the existing
parentcolumn — theParentFilteralready supports filtering by parent SKU, but this requires knowing the parent SKU in advance. It does not support filtering for "any product that has a parent" or "any product with no parent". - A separate
is_variantboolean column — functional but redundant givenparent_idalready encodes this relationship. A derived type is cleaner. - Keeping the list flat and relying on the Parent column — the Parent column is already present in the datagrid, but scanning it visually to identify relationships across hundreds of rows is impractical.
Additional Context
The products table already has a parent_id column and the Product model already defines both parent() (BelongsTo) and variants() (HasMany) relationships. The data to support all three features above is already present — this is primarily a type-system and UI change rather than a schema change.
For the nested expansion in the product list, the variants panel already built for the configurable product edit page (which renders child variants with their option values) demonstrates that the variant data can be fetched and rendered efficiently client-side.
Source: unopim/unopim