Skip to main content
Brands represent manufacturers or labels for your products. They help customers identify and filter products from their favorite manufacturers, and they give your storefront structured navigation (e.g., a “Shop by Brand” page or a brand filter in search results).

Model

The model used is Shopper\Models\Brand, which extends Shopper\Core\Models\Brand. The core model provides the business logic, relationships, scopes, and slug generation. The admin model adds media collections and conversions through Spatie MediaLibrary. The core model implements Shopper\Core\Models\Contracts\Brand and uses the HasSlug trait for automatic slug generation with collision handling, and the HasMediaCollections trait for config-driven media support.

Extending the Model

To add custom behavior, extend the admin model and update your configuration:
namespace App\Models;

use Shopper\Models\Brand as ShopperBrand;

class Brand extends ShopperBrand
{
}
Update config/shopper/models.php:
return [
    'brand' => \App\Models\Brand::class,
];

Database Schema

ColumnTypeNullableDefaultDescription
idbigintnoautoPrimary key
namestringno-Brand name
slugstringyesautoURL-friendly identifier (unique)
websitestringyesnullBrand’s official website URL
descriptionlongtextyesnullBrand description (supports rich text)
positionsmallint (unsigned)no0Display order position
is_enabledbooleannofalseBrand visibility status
seo_titlestring(60)yesnullSEO meta title
seo_descriptionstring(160)yesnullSEO meta description
metadatajsonbyesnullAdditional custom data
created_attimestampyesnullCreation timestamp
updated_attimestampyesnullLast update timestamp

Slug Generation

The HasSlug trait generates unique slugs automatically. When you set the slug attribute, the trait runs it through Str::slug() and appends an incrementing suffix (-1, -2, etc.) if a collision is detected. The trait also provides a findBySlug() static method:
use Shopper\Models\Brand;

$brand = Brand::findBySlug('nike');

Relationships

Products

A brand has many products. This is a standard HasMany relationship using the configured product model.
$brand->products;
$brand->products()->count();
To get only published products for a brand (for storefront display):
$brand->products()->publish()->get();
With eager loading:
$brands = Brand::query()->with('products')->get();

Query Scopes

Enabled Brands

The enabled scope filters brands where is_enabled is true. Use it for all storefront queries to exclude draft or hidden brands.
use Shopper\Models\Brand;

Brand::query()->enabled()->orderBy('position')->get();

Status Management

The updateStatus() method provides a convenient way to toggle brand visibility:
$brand->updateStatus(true);  // enable
$brand->updateStatus(false); // disable
You can also update the field directly:
$brand->update(['is_enabled' => true]);

Media

Brands support two media collections through Spatie MediaLibrary, using the same config-driven collection names as products.
CollectionConfig keyBehaviorDescription
Default galleryshopper.media.storage.collection_nameMultiple filesBrand images
Thumbnailshopper.media.storage.thumbnail_collectionSingle fileBrand logo
To add a brand logo:
$collection = config('shopper.media.storage.thumbnail_collection');

$brand->addMedia($file)->toMediaCollection($collection);
To retrieve the logo URL with a specific conversion:
$collection = config('shopper.media.storage.thumbnail_collection');

$url = $brand->getFirstMediaUrl($collection, 'medium');
From a URL:
$collection = config('shopper.media.storage.thumbnail_collection');

$brand->addMediaFromUrl('https://example.com/nike-logo.png')
    ->toMediaCollection($collection);

Creating Brands

To create a brand with a logo and SEO metadata:
use Shopper\Models\Brand;

$brand = Brand::query()->create([
    'name' => 'Nike',
    'slug' => 'nike',
    'website' => 'https://nike.com',
    'description' => 'Just Do It',
    'is_enabled' => true,
    'position' => 1,
    'seo_title' => 'Nike - Official Products',
    'seo_description' => 'Shop official Nike products including shoes, clothing, and accessories.',
]);

$collection = config('shopper.media.storage.thumbnail_collection');

$brand->addMedia($logoFile)->toMediaCollection($collection);

Retrieving Brands

To get all enabled brands ordered by position:
Brand::query()->enabled()->orderBy('position')->get();
To get brands that have at least one product, with product counts:
$brands = Brand::query()
    ->enabled()
    ->has('products')
    ->withCount('products')
    ->get();
To find a brand by slug:
$brand = Brand::findBySlug('nike');
For navigation or filter dropdowns where you only need name and slug:
$brands = Brand::query()
    ->enabled()
    ->select(['id', 'name', 'slug'])
    ->orderBy('name')
    ->get();

Metadata

The metadata JSON column lets you store additional custom data that doesn’t warrant its own database column:
$brand->update([
    'metadata' => [
        'country_of_origin' => 'USA',
        'founded_year' => 1964,
        'social' => [
            'instagram' => '@nike',
            'twitter' => '@nike',
        ],
    ],
]);

$country = $brand->metadata['country_of_origin'] ?? null;

Configuration

Disabling Brands

If you don’t need brands in your store, disable the feature in config/shopper/features.php:
use Shopper\Enum\FeatureState;

return [
    'brand' => FeatureState::Disabled,
];
When disabled, the brand menu item is hidden from the sidebar, brand-related routes are not registered, and brand selection is removed from product forms.

Permissions

The admin panel generates four permissions for brand management:
PermissionDescription
browse_brandsView the brands list
read_brandsView a single brand
add_brandsCreate new brands
edit_brandsEdit existing brands
delete_brandsDelete brands

Components

To customize the admin UI for brand management:
php artisan shopper:component:publish brand
This creates config/shopper/components/brand.php:
use Shopper\Livewire;

return [
    'pages' => [
        'brand-index' => Livewire\Pages\Brand\Index::class,
    ],
    'components' => [
        'slide-overs.brand-form' => Livewire\SlideOvers\BrandForm::class,
    ],
];

Storefront Example

This example shows a brand listing page and a brand detail page with its published products.
namespace App\Http\Controllers;

use Shopper\Models\Brand;

class BrandController extends Controller
{
    public function index()
    {
        $brands = Brand::query()
            ->enabled()
            ->withCount('products')
            ->orderBy('position')
            ->get();

        return view('brands.index', compact('brands'));
    }

    public function show(string $slug)
    {
        $brand = Brand::findBySlug($slug);

        $products = $brand->products()
            ->publish()
            ->paginate(12);

        return view('brands.show', compact('brand', 'products'));
    }
}

View Composer

For brands that appear in multiple views (header, footer, filters), use a View Composer to avoid repeating the query:
namespace App\View\Composers;

use Illuminate\View\View;
use Shopper\Models\Brand;

class BrandsComposer
{
    public function compose(View $view): void
    {
        $view->with('brands', Brand::query()
            ->enabled()
            ->orderBy('position')
            ->take(12)
            ->get()
        );
    }
}
Register it in your AppServiceProvider:
use Illuminate\Support\Facades\View;

public function boot(): void
{
    View::composer(['partials.brands', 'filters.brands'], BrandsComposer::class);
}