DE

developing-shopper

Provides architectural patterns and coding requirements for building within the Shopper Laravel monorepo.

Install

mkdir -p .claude/skills/developing-shopper && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/3849" && unzip -o skill.zip -d .claude/skills/developing-shopper && rm skill.zip

Installs to .claude/skills/developing-shopper

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Coding standards and patterns for Shopper monorepo development. Use when creating or modifying Models, Actions, Enums, Livewire components, migrations, tests, or building admin UI with Filament Schemas, Tables, and Actions.
223 chars✓ has a “when” trigger
Intermediate

Key capabilities

  • Define class element order for PHP files
  • Implement swappable model patterns
  • Structure actions with single responsibility
  • Create enums with color, icon, and label interfaces
  • Develop Livewire components with actions and schemas
  • Write migrations with common and SEO fields

How it works

The skill provides guidelines and examples for structuring PHP code within the Shopper monorepo, covering class element order, model patterns, and integration with Filament components.

Inputs & outputs

You give it
PHP code for Models, Actions, Enums, Livewire components, or Migrations
You get back
Code adhering to Shopper coding standards and patterns

When to use developing-shopper

  • Build a new Shopper admin Livewire component
  • Create a new Shopper Core model
  • Implement a payment driver
  • Apply Shopper coding standards to a service

About this skill

Developing Shopper

Monorepo Structure

PackageNamespacePurpose
packages/adminShopper\Livewire components, views, routes, assets
packages/coreShopper\Core\Models, Actions, Enums, Contracts
packages/cartShopper\Cart\Cart management, pipeline-based calculation
packages/paymentShopper\Payment\Payment processing, driver architecture
packages/shippingShopper\Shipping\Shipping providers, driver architecture
packages/sidebarShopper\Sidebar\Sidebar navigation builder
packages/stripeShopper\Stripe\Stripe payment driver
packages/typesTypeScript type definitions (NPM package)
packages/upgradeUpgrade utilities

Required in Every PHP File

<?php

declare(strict_types=1);

Class Element Order

  1. Traits → 2. Cases → 3. Constants → 4. Properties → 5. Constructor → 6. Magic methods → 7. Methods (public → protected → private)

Models

See MODELS.md for the swappable model pattern.

class Product extends Model implements ProductContract
{
    use HasFactory;
    use HasModelContract;
    use HasSlug;

    protected $guarded = [];

    public static function configuredClass(): string
    {
        return config('shopper.models.product', static::class);
    }

    public function getTable(): string
    {
        return shopper_table('products');
    }

    protected function casts(): array
    {
        return ['featured' => 'boolean'];
    }
}

Actions

final class CreateOrderAction
{
    public function execute(array $data): Order
    {
        // Single responsibility
    }
}

Enums

enum OrderStatus: string implements HasColor, HasIcon, HasLabel
{
    use ArrayableEnum;
    use HasEnumStaticMethods;

    case Pending = 'pending';

    public function getLabel(): string
    {
        return __('shopper-core::enum/order.pending');
    }
}

Livewire Components

class ProductForm extends Component implements HasActions, HasSchemas
{
    use InteractsWithActions;
    use InteractsWithSchemas;

    public ?array $data = [];

    #[Computed]
    public function categories(): Collection
    {
        return resolve(CategoryContract::class)::query()->get();
    }

    public function render(): View
    {
        return view('shopper::livewire.product-form');
    }
}

Migrations

return new class extends \Shopper\Core\Helpers\Migration
{
    public function up(): void
    {
        Schema::create($this->getTableName('products'), function (Blueprint $table): void {
            $this->addCommonFields($table, hasSoftDelete: true);
            $this->addSeoFields($table);
            $this->addShippingFields($table);
            $this->addForeignKey($table, 'brand_id', $this->getTableName('brands'));
        });
    }
};

Testing

beforeEach(function (): void {
    $this->user = User::factory()->create();
});

it('creates a product', function (): void {
    $this->actingAs($this->user);

    Livewire::test(ProductForm::class)
        ->set('data.name', 'Test')
        ->call('save')
        ->assertHasNoErrors();
})->group('products');

Commands

composer test:sqlite   # Run tests
composer test:types    # PHPStan
composer cs            # Rector + Pint + Prettier

Livewire Patterns

Component Types

TypeBase ClassLocation
PageAbstractPageComponentLivewire/Pages/
SlideOverSlideOverComponent (Laravelcm\LivewireSlideOvers)Livewire/SlideOvers/
ComponentComponentLivewire/Components/

Page with Table

class Index extends AbstractPageComponent implements HasActions, HasSchemas, HasTable
{
    use InteractsWithActions;
    use InteractsWithSchemas;
    use InteractsWithTable;

    public function mount(): void
    {
        $this->authorize('browse_brands');
    }

    public function table(Table $table): Table
    {
        return $table
            ->query(resolve(BrandContract::class)::query()->latest())
            ->columns([
                TextColumn::make('name')
                    ->label(__('shopper::forms.label.name'))
                    ->searchable()
                    ->sortable(),
            ])
            ->recordActions([
                Action::make('edit')
                    ->icon(Untitledui::Edit03)
                    ->iconButton()
                    ->action(fn ($record) => $this->dispatch(
                        'openPanel',
                        component: 'shopper-slide-overs.brand-form',
                        arguments: ['brand' => $record]
                    )),
            ]);
    }

    public function render(): View
    {
        return view('shopper::livewire.pages.brand.index')
            ->title(__('shopper::pages/brands.menu'));
    }
}

Blade View Structure

<x-shopper::container>
    {{-- Breadcrumb --}}
    <x-shopper::breadcrumb :back="route('shopper.settings.index')" :current="__('Shipping')">
        <x-untitledui-chevron-left class="size-4 shrink-0 text-gray-300 dark:text-gray-600" />
        <x-shopper::breadcrumb.link
            :link="route('shopper.settings.index')"
            :title="__('Settings')"
        />
    </x-shopper::breadcrumb>

    {{-- Page Heading --}}
    <x-shopper::heading class="my-6" :title="__('Shipping Methods')">
        <x-slot name="action">
            <x-filament::button wire:click="create">
                {{ __('Add Method') }}
            </x-filament::button>
        </x-slot>
    </x-shopper::heading>

    {{-- Content --}}
    <x-shopper::card class="mt-5">
        {{ $this->table }}
    </x-shopper::card>
</x-shopper::container>

Blade Components

ComponentPurpose
<x-shopper::container>Main content wrapper
<x-shopper::card>Card wrapper
<x-shopper::heading :title="">Page title with optional action slot
<x-shopper::breadcrumb>Navigation breadcrumb
<x-shopper::separator>Section separator
<x-shopper::empty-card>Empty state
<x-filament::button>Primary button

SlideOver with Form

/**
 * @property-read Schema $form
 */
class BrandForm extends SlideOverComponent implements HasActions, HasSchemas, SlideOverForm
{
    use InteractsWithActions;
    use InteractsWithSchemas;

    public Brand $brand;
    public ?array $data = [];

    public function mount(?Brand $brand = null): void
    {
        $this->brand = $brand ?? resolve(Brand::class)::query()->newModelInstance();
        $this->form->fill($this->brand->toArray());
    }

    public function form(Schema $schema): Schema
    {
        return $schema
            ->components([
                Section::make(__('shopper::words.general'))
                    ->collapsible()
                    ->compact()
                    ->schema([
                        TextInput::make('name')
                            ->label(__('shopper::forms.label.name'))
                            ->required()
                            ->live(onBlur: true)
                            ->afterStateUpdated(fn ($state, Set $set) => $set('slug', Str::slug($state))),
                        Hidden::make('slug'),
                    ]),
            ])
            ->statePath('data')
            ->model($this->brand);
    }

    public function save(): void
    {
        if ($this->brand->id) {
            $this->authorize('edit_brands', $this->brand);
            $this->brand->update($this->form->getState());
        } else {
            $this->authorize('add_brands');
            $brand = resolve(Brand::class)::query()->create($this->form->getState());
            $this->form->model($brand)->saveRelationships();
        }

        Notification::make()
            ->title(__('shopper::notifications.save', ['item' => __('shopper::pages/brands.single')]))
            ->success()
            ->send();

        $this->redirectRoute('shopper.brands.index', navigate: true);
    }

    public function render(): View
    {
        return view('shopper::livewire.slide-overs.brand-form');
    }
}

SlideOver Operations

// Open
$this->dispatch(
    'openPanel',
    component: 'shopper-slide-overs.brand-form',
    arguments: ['brand' => $record]
);

// Close
$this->closePanel();

// Close with events
$this->closePanelWithEvents(['refresh']);

SlideOver Configuration

public static function panelMaxWidth(): string
{
    return '2xl'; // sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl, 7xl
}

public static function closePanelOnClickAway(): bool
{
    return false;
}

Computed Properties

#[Computed]
public function categories(): Collection
{
    return resolve(CategoryContract::class)::query()->get();
}

Authorization

// In mount
$this->authorize('browse_brands');

// In table actions
->visible(Shopper::auth()->user()->can('edit_brands'))

When not to use it

  • When developing outside the Shopper monorepo
  • When not using Laravel or Filament components

Limitations

  • Specific to Shopper monorepo development
  • Requires adherence to defined class element order
  • Livewire components use specific base classes

How it compares

This skill enforces specific coding standards and architectural patterns tailored for the Shopper monorepo, ensuring consistency and maintainability across its packages.

Compared to similar skills

developing-shopper side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
developing-shopper (this skill)34moReviewIntermediate
laravel-pdf113moReviewBeginner
laravel-specialist123moNo flagsIntermediate
pennant-development25moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

laravel-pdf

spatie

Generate PDFs from Blade views or HTML using spatie/laravel-pdf. Covers creating, formatting, saving, downloading, and testing PDFs with the Browsershot, Cloudflare, or DOMPDF driver.

1140

laravel-specialist

Jeffallan

Use when building Laravel 10+ applications requiring Eloquent ORM, API resources, or queue systems. Invoke for Laravel models, Livewire components, Sanctum authentication, Horizon queues.

1215

pennant-development

laravel

Manages feature flags with Laravel Pennant. Activates when creating, checking, or toggling feature flags; showing or hiding features conditionally; implementing A/B testing; working with @feature directive; or when the user mentions feature flags, feature toggles, Pennant, conditional features, rollouts, or gradually enabling features.

29

developing-with-turbo-streams

hotwired-laravel

Basics of developing with Turbo Streams in web applications. Activate when working on projects that utilize Turbo Streams for enhancing user experience through real-time updates, dynamic content changes, and partial page updates without full reloads.

15

extending-shopper

shopperlabs

Provides patterns for extending Laravel Shopper with custom sidebar items, component overrides, event listeners, and domain features like stock, pricing, and media. Use when customizing Shopper behavior.

14

developing-with-turbo-basics

hotwired-laravel

Basics of developing with Turbo in web applications. Activate when working on projects that utilize Turbo for enhancing user experience through partial page updates, real-time interactions, and seamless navigation without full page reloads.

13

Search skills

Search the agent skills registry