PHPackages                             wizcodepl/lunar-product-schemas - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. wizcodepl/lunar-product-schemas

ActiveLibrary[Database &amp; ORM](/categories/database)

wizcodepl/lunar-product-schemas
===============================

Migration-style schema builder for Lunar product types and attributes (toggle filterable/searchable/required, attach/detach attrs, rename, drop with data cleanup).

v1.5.1(3w ago)377↓100%MITPHPPHP ^8.2CI passing

Since Apr 25Pushed 3w agoCompare

[ Source](https://github.com/wizcodepl/lunar-product-schemas)[ Packagist](https://packagist.org/packages/wizcodepl/lunar-product-schemas)[ RSS](/packages/wizcodepl-lunar-product-schemas/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (6)Dependencies (5)Versions (13)Used By (0)

 [![Lunar Product Schemas](art/logo.svg)](art/logo.svg)

Lunar Product Schemas
=====================

[](#lunar-product-schemas)

[![Latest Version on Packagist](https://camo.githubusercontent.com/f7cfc0d2f398de5fc098654c4b927efb5aebdb6728c284c25fcdceb235450b31/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f77697a636f6465706c2f6c756e61722d70726f647563742d736368656d61732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/wizcodepl/lunar-product-schemas)[![Tests](https://camo.githubusercontent.com/e33b2622bfdf6a0b73f551387c454b3ec5e143beca76e9be1dfab40a2e86e34e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f77697a636f6465706c2f6c756e61722d70726f647563742d736368656d61732f74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/wizcodepl/lunar-product-schemas/actions/workflows/tests.yml)[![License](https://camo.githubusercontent.com/a984d25dbfc4e2f97655ed92c950dba99d1bd009fa16c52bdc41694123eed673/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f77697a636f6465706c2f6c756e61722d70726f647563742d736368656d61732e7376673f7374796c653d666c61742d737175617265)](LICENSE)

Migration-style schema builder for [Lunar](https://lunarphp.io) product types and attributes. Manage `searchable` / `filterable` / `required` flags, attach or detach attributes per product type (both **product-level** and **variant-level**), rename or drop attributes (with cleanup of values stored in `attribute_data` JSON on either layer) — all from versioned definition files that ship with your code.

Inspired by Laravel's `Schema::table()` builder, but for the catalog layer Lunar exposes through `Attribute`, `AttributeGroup`, and `ProductType`.

Why
---

[](#why)

Lunar lets you toggle `Attribute::filterable` / `searchable` / `required` from the Filament admin panel. That works, but on a real shop you typically want:

- Attribute structure tracked in **code**, not panel clicks.
- A clear **history** of changes (who, when, why).
- Repeatable, environment-agnostic deploys.

Doing this with raw `Attribute::where(...)->update(...)` calls in Laravel migrations works, but quickly turns into copy-pasted boilerplate. This package is the thin wrapper that makes those operations readable, plus a dedicated `product-schema:*` command set so catalog changes don't fight Laravel's own `migrations` table for history.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Lunar core ^1.3 (which itself pulls in Laravel 11 or 12)

Install
-------

[](#install)

```
composer require wizcodepl/lunar-product-schemas
```

The service provider auto-registers via Laravel package discovery.

Run `migrate` once to create the tracking table the package ships with:

```
php artisan migrate
```

This creates `product_schema_migrations` (separate from Laravel's own `migrations` table) so DB schema changes and product-catalog changes don't share batch numbers.

(Optional) publish the config to override the path where definitions live:

```
php artisan vendor:publish --tag=lunar-product-schemas-config
```

```
// config/lunar-product-schemas.php
return [
    'path' => database_path('product-schemas'),

    // Optional: throw `UnknownAttributeException` when a Product / ProductVariant
    // is saved with `attribute_data` keys not declared in the product type's schema.
    'strict_mode' => env('LUNAR_PRODUCT_SCHEMAS_STRICT', false),

    // Optional: throw `MissingRequiredAttributeException` when a Product / ProductVariant
    // is saved without values for attributes marked `required: true` in its schema.
    'enforce_required' => env('LUNAR_PRODUCT_SCHEMAS_ENFORCE_REQUIRED', false),
];
```

Strict mode
-----------

[](#strict-mode)

Enable `strict_mode` (config or `LUNAR_PRODUCT_SCHEMAS_STRICT=true` in `.env`) and the package observes Lunar's `Product` and `ProductVariant` saves: any `attribute_data` key not declared in the product type's schema throws `WizcodePl\LunarProductSchemas\Exceptions\UnknownAttributeException`. The schema becomes the source of truth — schema drift surfaces as a loud failure instead of silently corrupting `attribute_data` JSON.

Off by default so adopting the package on an existing catalog is a no-op; flip on once your schemas cover everything you actually persist.

Enforce required
----------------

[](#enforce-required)

Enable `enforce_required` (config or `LUNAR_PRODUCT_SCHEMAS_ENFORCE_REQUIRED=true` in `.env`) and the same observers reject any save where attributes marked `required: true` in the schema are missing or empty (`null`, `''`, empty list/dict on a Lunar `FieldType`). They throw `WizcodePl\LunarProductSchemas\Exceptions\MissingRequiredAttributeException` listing the missing handles.

Independent from `strict_mode` — turn either or both on. Off by default because back-filling required values across an existing catalog can break unrelated workflows (admin edits, programmatic saves) until every record is migrated.

Concepts: product-level vs variant-level attributes
---------------------------------------------------

[](#concepts-product-level-vs-variant-level-attributes)

Lunar stores attribute values on two layers, and this package manages both:

Where the value livesUse it forLunar admin tabThis package`Product.attribute_data` JSONSame value for all variants of one product (e.g. material, season, gender)"Product Attributes"`attribute()``ProductVariant.attribute_data` JSONPer-SKU descriptive data the customer doesn't pick (e.g. lead time, pantone code, batch number)"Variant Attributes"`variantAttribute()`What this package does **not** manage: customer-pickable variant axes like Size and Color — those are Lunar's separate `ProductOption` / `ProductOptionValue` mechanism. See **Out of scope** below.

Quick start
-----------

[](#quick-start)

Create a definition file:

```
php artisan product-schema:make add_t_shirt_attributes
# → database/product-schemas/2026_05_01_120000_add_t_shirt_attributes.php
```

Fill it in:

```
