# Product Option Types Lunar's `ProductOption`/`ProductOptionValue` are generic by design — a "Color" option and a "Size" option are both just a handle, a translated name, and a list of values. Each `ProductOptionValue` carries a free-form `meta` jsonb column, but nothing in Lunar's own admin UI exposes it — there's no way for an admin to, say, attach a hex code to a "Red" value without editing the database directly. `Modules\Core\Catalog\Contracts\ProductOptionTypeInterface` describes how a category of option behaves — what structured data its values carry in `meta`, and how an admin edits that data — without introducing a new model. `ProductOption`/ `ProductOptionValue` stay exactly as Lunar defines them. --- ## Registering a type A shop registers a type class from its own service provider's `boot()`, the same shape as `Modules\Core\Notification\NotificationRegistry`: ```php use Modules\Core\Catalog\Services\ProductOptionTypeManager; ProductOptionTypeManager::get()->register([ \App\ProductOptions\ColorOptionType::class, ]); ``` Not a published config array — the mapping isn't per-`ProductOption`, so there's nothing for a shop to *key* by. Instead, an admin picks a type per-option from a dropdown on the `ProductOption` edit form itself (see below); the choice is stored in `ProductOption::meta['option_type']`, deliberately **not** tied to the option's `handle` (a shop's own handle naming — transliterated Greek, legacy import slugs — shouldn't have to match a type's key). A `ProductOption` with no type selected behaves exactly as stock Lunar does — plain name/position, no extra meta form. --- ## Writing a type ```php namespace App\ProductOptions; use Filament\Forms\Components\ColorPicker; use Modules\Core\Catalog\Contracts\ProductOptionTypeInterface; class ColorOptionType implements ProductOptionTypeInterface { public static function getKey(): string { return 'color'; } public function getMetaForm(): array { return [ ColorPicker::make('meta.hex') ->label('Color') ->required(), ]; } } ``` `getMetaForm()` returns Filament form components, keyed under `meta.*` dot notation — the path they save to on `ProductOptionValue::meta` (cast as `AsArrayObject`, a plain jsonb column). `getKey()` is the identifier used in the admin's "Option Type" dropdown and in `ProductOption::meta['option_type']` — it has no relationship to the `ProductOption::handle`. A reference implementation ships at `Modules\Core\Catalog\OptionTypes\ColorOptionType`, registered automatically by `Modules\Core\Providers\CatalogServiceProvider` — no shop setup needed for it to appear in the "Option Type" dropdown, though an admin still has to pick it per-`ProductOption` for it to take effect. --- ## How it's wired into the admin UI `Modules\Core\Catalog\Services\ProductOptionTypeManager` is a singleton registry: - `get(): static` — the shared instance. - `register(array $types): void` — registers one or more type classes, keyed internally by `getKey()`. - `unregister(string $key): void` - `resolve(?string $key): ?ProductOptionTypeInterface` — looks up a registered type by key (or `null` if no key / not found). - `all(): array` — every registered type's class, keyed by `getKey()`. Two extensions hook into Lunar's admin via its extension system (`LunarPanel::extensions([...])`, registered in `CorePlugin`) — no forking of Lunar's classes needed: - `Modules\Core\Catalog\Filament\Extensions\ProductOptionResourceExtension` extends `Lunar\Admin\Filament\Resources\ProductOptionResource`'s own form with a `Select` (`meta.option_type`) listing every enabled type's key. Shown only when at least one type is enabled. - `Modules\Core\Catalog\Filament\Extensions\ValuesRelationManagerExtension` extends the "Values" tab's form. Its `extendForm()` reads `$option->meta['option_type']` off the owning `ProductOption`, resolves it via `ProductOptionTypeManager`, and appends `getMetaForm()`'s fields to the stock name field. A `ProductOption` with no type selected gets the stock form unchanged. --- ## Reading the value back Storefront code reads `ProductOptionValue::meta` like any other jsonb column — e.g. `$value->meta['hex']` for a color swatch. `ProductOptionTypeManager` is an admin-side concern only (describing *how to edit* the meta); nothing requires the storefront to go through it to *read* the meta.