Skip to main content
Attributes store custom, translatable data against Eloquent models using configurable field types.

Overview

Attributes allow custom data to be stored against Eloquent models. They are most commonly used with products, where different information needs to be stored and presented to visitors. For example, a television might have the following attributes assigned:
  • Screen Size
  • Screen Technology
  • Tuner
  • Resolution
Attributes are organized into Attribute Groups for display purposes. A group like “SEO” might contain attributes for “Meta Title” and “Meta Description”.

Attribute Groups

Lunar\Models\AttributeGroup
Attribute groups form a logical collection of attributes. Each group belongs to a specific model type (e.g. products, collections, customers).

Fields

FieldTypeDescription
ididPrimary key
attributable_typestringThe morph map name of the model type this group belongs to (e.g. product)
namejsonTranslated name, e.g. {"en": "SEO"}
handlestringKebab-cased reference, e.g. seo. Must be unique
positionintegerSort order of the group
created_attimestamp
updated_attimestamp

Relationships

RelationshipTypeRelated ModelDescription
attributesHasManyLunar\Models\AttributeAll attributes in this group, ordered by position

Attributes

Lunar\Models\Attribute

Fields

FieldTypeDescription
ididPrimary key
attribute_typestringThe morph map name of the model type that can use this attribute, e.g. product
attribute_group_idforeignIdThe associated attribute group
positionintegerSort order within the attribute group
namejsonTranslated name, e.g. {"en": "Screen Size"}
descriptionjson nullableTranslated description
handlestringKebab-cased reference, e.g. screen-size. Unique per attribute_type
sectionstring nullableAn optional label to define where an attribute should be used in the UI
typestringThe field type class, e.g. Lunar\FieldTypes\Number
requiredbooleanWhether a value must be provided
default_valuestring nullableDefault value for the attribute
configurationjsonField-type-specific configuration stored as a collection
systembooleanIf true, the attribute should not be deleted
validation_rulesstring nullableLaravel validation rules, e.g. required|max:255
filterablebooleanWhether the attribute can be used for filtering (default false)
searchablebooleanWhether the attribute is included in search indexing (default true)
created_attimestamp
updated_attimestamp

Relationships

RelationshipTypeRelated ModelDescription
attributeGroupBelongsToLunar\Models\AttributeGroupThe group this attribute belongs to

Scopes

ScopeDescription
system(string $type)Filter to system attributes for a given model type

Field Types

Field types determine how an attribute’s value is stored and retrieved. Each type implements the Lunar\Base\FieldType interface.
TypeDescription
Lunar\FieldTypes\TextSingle-line, multi-line, or rich text
Lunar\FieldTypes\TranslatedTextTranslatable text with a Text value per locale
Lunar\FieldTypes\NumberInteger or decimal value
Lunar\FieldTypes\ToggleBoolean on/off value
Lunar\FieldTypes\DropdownSingle selection from a list of predefined options
Lunar\FieldTypes\ListFieldA reorderable list of text values
Lunar\FieldTypes\FileSingle or multiple file references
Lunar\FieldTypes\YouTubeA YouTube video ID or URL
Lunar\FieldTypes\VimeoA Vimeo video ID or URL

Custom Field Types

Custom field types can be created by implementing the Lunar\Base\FieldType interface. Once created, the field type should be registered with the FieldTypeManifest in a service provider:
use Lunar\Facades\FieldTypeManifest;

FieldTypeManifest::add(\App\FieldTypes\CustomField::class);
To make a custom field type editable in the admin panel, a corresponding Filament component and Livewire synthesizer are also needed. See Extending Attributes for a full walkthrough.

Models That Use Attributes

The following models support attributes out of the box:
  • Lunar\Models\Product
  • Lunar\Models\ProductVariant
  • Lunar\Models\Collection
  • Lunar\Models\Customer
  • Lunar\Models\Brand
  • Lunar\Models\CustomerGroup

Saving Attribute Data

Attribute values are stored in an attribute_data JSON column on the model. Each key is an attribute handle, and each value is a field type instance.
use Lunar\FieldTypes\Number;
use Lunar\FieldTypes\Text;
use Lunar\FieldTypes\TranslatedText;

$product->attribute_data = collect([
    'meta_title' => new Text('The best screwdriver you will ever buy!'),
    'pack_qty' => new Number(2),
    'description' => new TranslatedText(collect([
        'en' => new Text('Blue'),
        'fr' => new Text('Bleu'),
    ])),
]);

$product->save();

Accessing Attribute Data

When the attribute_data property is accessed, it is cast to a collection of field type instances.
dump($product->attribute_data);

Illuminate\Support\Collection {#1522
  #items: array:2 [
    "name" => Lunar\FieldTypes\TranslatedText {#1533
      #value: Illuminate\Support\Collection {#1505
        #items: array:3 [
          "de" => Lunar\FieldTypes\Text {#1506
            #value: "Leren laarzen"
          }
          "en" => Lunar\FieldTypes\Text {#1514
            #value: "Leather boots"
          }
          "fr" => Lunar\FieldTypes\Text {#1502
            #value: "Bottes en cuir"
          }
        ]
      }
    }
    "description" => Lunar\FieldTypes\Text {#1537
      #value: "<p>I'm a description!</p>"
    }
  ]
}

Retrieving a Single Attribute Value

The translateAttribute method returns the resolved value for a single attribute. For TranslatedText fields, it resolves the correct locale automatically.
// Returns the value for the current app locale
$product->translateAttribute('name');

// Returns the French translation
$product->translateAttribute('name', 'fr');

// Falls back to the first available value
$product->translateAttribute('name', 'FOO');
The shorthand attr method does the same thing:
$product->attr('name');
$product->attr('name', 'fr');
For non-translatable fields, translateAttribute returns the raw value directly:
// Returns the integer value
$product->translateAttribute('pack_qty');

Adding Attributes to a Custom Model

To make a custom model support attributes:
  1. Add the HasAttributes trait.
  2. Cast the attribute_data column using AsAttributeData.
  3. Add an attribute_data JSON column to the model’s database table.
use Lunar\Base\Casts\AsAttributeData;
use Lunar\Base\Traits\HasAttributes;
use Lunar\Base\Traits\HasTranslations;

class MyModel extends Model
{
    use HasAttributes;
    use HasTranslations;

    protected $casts = [
        'attribute_data' => AsAttributeData::class,
    ];
}
Then add the JSON column via a migration:
Schema::table('my_models', function (Blueprint $table) {
    $table->json('attribute_data')->nullable();
});
Finally, register the model as an attributable type so that attribute groups and attributes can be created for it:
use Lunar\Facades\AttributeManifest;

// In a service provider's boot method
AttributeManifest::addType(\App\Models\MyModel::class);

Attribute Manifest

The AttributeManifest manages which model types support attributes and provides access to searchable attribute data.
use Lunar\Facades\AttributeManifest;

// Get all registered attributable types
$types = AttributeManifest::getTypes();

// Get a specific type by key (lowercase class basename)
$type = AttributeManifest::getType('product');

// Register a new attributable type
AttributeManifest::addType(\App\Models\MyModel::class);

// Get all searchable attributes for a model type
$searchable = AttributeManifest::getSearchableAttributes('product');