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:
Shopper\Core\Models\Attribute       // The attribute definition
Shopper\Core\Models\AttributeValue  // Possible values for an attribute
Shopper\Core\Models\AttributeProduct // Pivot linking attributes to products
use Shopper\Core\Models\Attribute;

// The model implements
- Shopper\Core\Models\Contracts\Attribute

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

// Check if attribute accepts multiple values
$attribute->hasMultipleValues(); // true for Checkbox, ColorPicker

// Check if attribute uses single selection
$attribute->hasSingleValue(); // true for Select

// Check if attribute uses custom text input
$attribute->hasTextValue(); // true for Text, Number, RichText, DatePicker

// Get types that require predefined values
Attribute::fieldsWithValues(); // [Checkbox, ColorPicker, Select]

Relationships

Values

// Get all values for an attribute
$attribute->values; // Collection of AttributeValue models

// Add a new value
$attribute->values()->create([
    'value' => 'Red',
    'key' => 'color-red',
    'position' => 1,
]);

// Create multiple values
$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

// Get all products with this attribute
$attribute->products;

// Attach attribute to product with a predefined value
$attribute->products()->attach($productId, [
    'attribute_value_id' => $valueId,
]);

// Attach attribute to product with a custom value
$attribute->products()->attach($productId, [
    'attribute_custom_value' => 'Custom specification',
]);

Variants (AttributeValue)

AttributeValue can be linked to product variants:
// Get variants with this value
$attributeValue->variants;

// Link value to variant
$attributeValue->variants()->attach($variantId);

Query Scopes

use Shopper\Core\Models\Attribute;

// Get only enabled attributes
Attribute::query()->enabled()->get();

// Get filterable attributes (for storefront filters)
Attribute::query()->isFilterable()->get();

// Get searchable attributes
Attribute::query()->isSearchable()->get();

// Combine scopes
Attribute::query()
    ->enabled()
    ->isFilterable()
    ->with('values')
    ->get();

Status Management

// Enable an attribute
$attribute->updateStatus(true);

// Disable an attribute
$attribute->updateStatus(false);

Display Helpers

// Get formatted type label for display
$attribute->type_formatted; // Returns translated label like "Checkbox"

// Get all available types with labels
Attribute::typesFields();
// Returns: ['checkbox' => 'Checkbox', 'colorpicker' => 'Color Picker', ...]

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\Core\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::query()
    ->where('slug', 'size')
    ->with('values')
    ->firstOrFail();

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,
];

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\Core\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();

        // Apply attribute filters
        foreach ($request->input('attributes', []) as $slug => $values) {
            $query->whereHas('options', function ($q) use ($slug, $values) {
                $q->whereHas('attribute', fn ($a) => $a->where('slug', $slug))
                    ->whereIn('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