Skip to main content
Attributes define product characteristics like Color, Size, or Material. They enable variable products, enhance filtering, and provide detailed product specifications.

Models

Shopper uses three models to manage attributes:
ModelPurpose
Shopper\Core\Models\AttributeThe attribute definition (Color, Size, Material)
Shopper\Core\Models\AttributeValuePossible values for an attribute (Red, Blue, Large)
Shopper\Core\Models\AttributeProductPivot linking attributes to products with a selected value
The Attribute model implements Shopper\Core\Models\Contracts\Attribute and uses the HasSlug trait for automatic slug generation. Unlike products or brands, the Attribute model is not configurable via config/shopper/models.php and has no admin model wrapper.

Database Schema

Attribute Table

ColumnTypeNullableDefaultDescription
idbigintnoautoPrimary key
namestringno-Attribute name
slugstringyesautoURL-friendly identifier (unique)
descriptionstringyesnullAttribute description
typestringno-Field type enum value
iconstringyesnullIcon identifier
is_enabledbooleannofalseAttribute visibility
is_searchablebooleannofalseInclude in search
is_filterablebooleannofalseShow in filters
created_attimestampyesnullCreation timestamp
updated_attimestampyesnullLast update timestamp

AttributeValue Table

ColumnTypeNullableDefaultDescription
idbigintnoautoPrimary key
valuestring(50)no-Display value
keystringno-Unique identifier key
positionsmallintyes1Display order
attribute_idbigintno-Foreign key to attribute

AttributeProduct Table (Pivot)

ColumnTypeNullableDescription
idbigintnoPrimary key
attribute_idbigintnoForeign key to attribute
product_idbigintnoForeign key to product
attribute_value_idbigintyesForeign key to attribute value
attribute_custom_valuelongtextyesCustom value for text-based attributes

Field Types

Attributes use the FieldType enum to determine input behavior:
use Shopper\Core\Enum\FieldType;

FieldType::Checkbox     // Multiple selectable values
FieldType::ColorPicker  // Color selection
FieldType::DatePicker   // Date input
FieldType::RichText     // Rich text editor
FieldType::Select       // Single selection dropdown
FieldType::Text         // Single-line text input
FieldType::Number       // Numeric input
TypeDatabase ValueHas Predefined ValuesDescription
CheckboxcheckboxYesMultiple values can be selected
ColorPickercolorpickerYesColor values with picker
DatePickerdatepickerNoDate input field
RichTextrichtextNoHTML content editor
SelectselectYesSingle value selection
TexttextNoFree text input
NumbernumberNoInteger or decimal input

Type Checking Methods

Each attribute type has helper methods to determine its behavior:
$attribute->hasMultipleValues();
$attribute->hasSingleValue();
$attribute->hasTextValue();
Attribute::fieldsWithValues();
hasMultipleValues() returns true for Checkbox and ColorPicker. hasSingleValue() returns true for Select. hasTextValue() returns true for Text, Number, RichText, and DatePicker. fieldsWithValues() returns the types that require predefined values.

Relationships

Values

$attribute->values;
To add values to an attribute:
$attribute->values()->create([
    'value' => 'Red',
    'key' => 'color-red',
    'position' => 1,
]);
$attribute->values()->createMany([
    ['value' => 'Small', 'key' => 'size-s', 'position' => 1],
    ['value' => 'Medium', 'key' => 'size-m', 'position' => 2],
    ['value' => 'Large', 'key' => 'size-l', 'position' => 3],
]);

Products

$attribute->products;
Attach an attribute to a product with a predefined value:
$attribute->products()->attach($productId, [
    'attribute_value_id' => $valueId,
]);
Or with a custom value:
$attribute->products()->attach($productId, [
    'attribute_custom_value' => 'Custom specification',
]);

Variants (AttributeValue)

AttributeValue can be linked to product variants:
$attributeValue->variants;
$attributeValue->variants()->attach($variantId);

Slug & Lookup

The HasSlug trait generates unique slugs automatically and provides a findBySlug() static method:
use Shopper\Core\Models\Attribute;

$attribute = Attribute::findBySlug('color');

Query Scopes

Filter attributes by their visibility and behavior flags:
use Shopper\Core\Models\Attribute;

Attribute::query()->enabled()->get();
Attribute::query()->isFilterable()->get();
Attribute::query()->isSearchable()->get();
Scopes can be combined:
Attribute::query()
    ->enabled()
    ->isFilterable()
    ->with('values')
    ->get();

Status Management

$attribute->updateStatus(true);
$attribute->updateStatus(false);

Display Helpers

$attribute->type_formatted;
Attribute::typesFields();
type_formatted returns a translated label like “Checkbox”. typesFields() returns all available types with their labels.

Working with AttributeProduct

The AttributeProduct model provides a computed real_value accessor:
use Shopper\Core\Models\AttributeProduct;

$attributeProduct = AttributeProduct::query()->find($id);

// Get the actual value (custom or from value relationship)
$attributeProduct->real_value;
// Returns attribute_custom_value if set, otherwise value->value

Creating Attributes

Attribute with Predefined Values

use Shopper\Core\Models\Attribute;
use Shopper\Core\Enum\FieldType;

// Create a color attribute
$color = Attribute::query()->create([
    'name' => 'Color',
    'slug' => 'color',
    'type' => FieldType::ColorPicker,
    'is_enabled' => true,
    'is_filterable' => true,
]);

// Add color values
$color->values()->createMany([
    ['value' => 'Red', 'key' => 'red', 'position' => 1],
    ['value' => 'Blue', 'key' => 'blue', 'position' => 2],
    ['value' => 'Green', 'key' => 'green', 'position' => 3],
]);

Attribute with Free Text

// Create a text attribute (no predefined values needed)
$material = Attribute::query()->create([
    'name' => 'Material',
    'slug' => 'material',
    'type' => FieldType::Text,
    'is_enabled' => true,
    'is_searchable' => true,
]);

Assigning Attributes to Products

use Shopper\Models\Product;

$product = Product::query()->find($id);

// Assign attribute with predefined value (via options relationship)
$product->options()->attach($attributeId, [
    'attribute_value_id' => $valueId,
]);

// Assign attribute with custom value
$product->options()->attach($attributeId, [
    'attribute_custom_value' => '100% Cotton',
]);

// Get product attributes with values
foreach ($product->options as $attribute) {
    $valueId = $attribute->pivot->attribute_value_id;
    $customValue = $attribute->pivot->attribute_custom_value;
}

Retrieving Attributes

use Shopper\Core\Models\Attribute;

// Get all enabled attributes with values
$attributes = Attribute::query()
    ->enabled()
    ->with('values')
    ->get();

// Get filterable attributes for storefront
$filters = Attribute::query()
    ->enabled()
    ->isFilterable()
    ->with(['values' => fn ($q) => $q->orderBy('position')])
    ->get();

// Get attribute by slug
$size = Attribute::findBySlug('size');
$size->load('values');

Observer Behavior

The AttributeObserver handles cleanup when deleting:
// When an attribute is deleted:
// 1. All product associations are detached
// 2. All attribute values are deleted

$attribute->delete(); // Triggers observer cleanup

Disabling Attribute Feature

// config/shopper/features.php
use Shopper\Enum\FeatureState;

return [
    'attribute' => FeatureState::Disabled,
];

Permissions

The admin panel generates five permissions for attribute management:
PermissionDescription
browse_attributesView the attributes list
read_attributesView a single attribute
add_attributesCreate new attributes
edit_attributesEdit existing attributes
delete_attributesDelete attributes

Components

Attribute components are part of the product configuration. To customize them:
php artisan shopper:component:publish product
Attribute-related components in config/shopper/components/product.php:
use Shopper\Livewire;

return [
    'pages' => [
        // ...
        'attribute-index' => Livewire\Pages\Attribute\Browse::class,
    ],
    'components' => [
        // ...
        'slide-overs.attribute-form' => Livewire\SlideOvers\AttributeForm::class,
        'slide-overs.choose-product-attributes' => Livewire\SlideOvers\ChooseProductAttributes::class,
        'slide-overs.attribute-values' => Livewire\SlideOvers\AttributeValues::class,
    ],
];

Storefront Example

namespace App\Http\Controllers;

use Shopper\Core\Models\Attribute;
use Shopper\Models\Product;

class ProductFilterController extends Controller
{
    public function index()
    {
        // Get filterable attributes for sidebar
        $filters = Attribute::query()
            ->enabled()
            ->isFilterable()
            ->with(['values' => fn ($q) => $q->orderBy('position')])
            ->get();

        return view('products.index', compact('filters'));
    }

    public function filter(Request $request)
    {
        $query = Product::query()->publish();

        foreach ($request->input('attributes', []) as $slug => $values) {
            $query->whereHas('options', function ($q) use ($slug, $values) {
                $q->where('slug', $slug)
                    ->wherePivotIn('attribute_value_id', $values);
            });
        }

        return $query->paginate(12);
    }
}

Use Cases

Attribute TypeExampleUse Case
CheckboxSizeMultiple sizes available for clothing
ColorPickerColorProduct color options
SelectMaterialSingle material selection
TextSKU SuffixCustom text per product
NumberWeightNumeric specifications
DatePickerRelease DateProduct launch date
RichTextCare InstructionsDetailed HTML content