Documentation

Ecommerce Products

Ecommerce Products

UltraViolet Pro provides comprehensive product management features for creating, organizing, and displaying products in your online store.

Product Management

Creating Products

Basic Product Information

// Product creation example
$product = Product::create([
    'name' => 'Premium Wireless Headphones',
    'slug' => 'premium-wireless-headphones',
    'description' => 'Experience premium sound quality with our wireless headphones...',
    'short_description' => 'Premium wireless headphones with noise cancellation',
    'sku' => 'PWH-001',
    'price' => 299.99,
    'compare_price' => 399.99,
    'cost_price' => 150.00,
    'stock_quantity' => 100,
    'low_stock_threshold' => 10,
    'weight' => 0.5,
    'dimensions' => [
        'length' => 20,
        'width' => 18,
        'height' => 8
    ],
    'status' => 'active',
    'featured' => true,
    'meta_title' => 'Premium Wireless Headphones - Best Audio Quality',
    'meta_description' => 'Shop premium wireless headphones with noise cancellation...'
]);

Product Categories

// Assign categories to product
$product->categories()->attach([1, 2, 3]); // Category IDs

// Create category hierarchy
$electronics = Category::create([
    'name' => 'Electronics',
    'slug' => 'electronics',
    'description' => 'Electronic devices and accessories',
    'parent_id' => null,
    'sort_order' => 1
]);

$audio = Category::create([
    'name' => 'Audio',
    'slug' => 'audio',
    'description' => 'Audio equipment and accessories',
    'parent_id' => $electronics->id,
    'sort_order' => 1
]);

$headphones = Category::create([
    'name' => 'Headphones',
    'slug' => 'headphones',
    'description' => 'Headphones and earphones',
    'parent_id' => $audio->id,
    'sort_order' => 1
]);

Product Images

Uploading and Managing Images

// Upload product images
$product = Product::find(1);

// Primary image
$primaryImage = $product->images()->create([
    'image_path' => 'products/headphones-main.jpg',
    'alt_text' => 'Premium Wireless Headphones - Main View',
    'sort_order' => 1,
    'is_primary' => true
]);

// Additional images
$product->images()->create([
    'image_path' => 'products/headphones-side.jpg',
    'alt_text' => 'Premium Wireless Headphones - Side View',
    'sort_order' => 2,
    'is_primary' => false
]);

$product->images()->create([
    'image_path' => 'products/headphones-detail.jpg',
    'alt_text' => 'Premium Wireless Headphones - Detail View',
    'sort_order' => 3,
    'is_primary' => false
]);

Image Upload Form

<form action="{{ route('admin.products.images.store', $product) }}" method="POST" enctype="multipart/form-data">
    @csrf

    <div class="mb-3">
        <label for="images" class="form-label">Product Images</label>
        <input type="file" class="form-control" id="images" name="images[]" multiple accept="image/*">
        <div class="form-text">Upload multiple images. First image will be set as primary.</div>
    </div>

    <div class="mb-3">
        <div class="form-check">
            <input class="form-check-input" type="checkbox" id="optimize_images" name="optimize_images" checked>
            <label class="form-check-label" for="optimize_images">
                Automatically optimize images
            </label>
        </div>
    </div>

    <button type="submit" class="btn btn-primary">Upload Images</button>
</form>

Product Variants

Creating Product Variants

// Product with variants (e.g., different colors and sizes)
$product = Product::create([
    'name' => 'Classic T-Shirt',
    'slug' => 'classic-t-shirt',
    'description' => 'Comfortable cotton t-shirt...',
    'sku' => 'CTS-BASE', // Base SKU
    'price' => 24.99,
    'status' => 'active'
]);

// Create variants
$variants = [
    [
        'name' => 'Classic T-Shirt - Black - Small',
        'sku' => 'CTS-BLK-S',
        'price' => 24.99,
        'stock_quantity' => 50,
        'attributes' => [
            'color' => 'Black',
            'size' => 'Small'
        ]
    ],
    [
        'name' => 'Classic T-Shirt - Black - Medium',
        'sku' => 'CTS-BLK-M',
        'price' => 24.99,
        'stock_quantity' => 75,
        'attributes' => [
            'color' => 'Black',
            'size' => 'Medium'
        ]
    ],
    [
        'name' => 'Classic T-Shirt - White - Small',
        'sku' => 'CTS-WHT-S',
        'price' => 24.99,
        'stock_quantity' => 30,
        'attributes' => [
            'color' => 'White',
            'size' => 'Small'
        ]
    ]
];

foreach ($variants as $variantData) {
    $product->variants()->create($variantData);
}

Variant Selection Interface

<div class="product-variants">
    <div class="variant-group mb-3">
        <label class="form-label fw-bold">Color:</label>
        <div class="variant-options d-flex gap-2">
            <button type="button" class="btn btn-outline-secondary variant-option active" 
                    data-variant="color" data-value="black">
                <span class="color-swatch me-2" style="width: 20px; height: 20px; background-color: #000; border-radius: 50%; display: inline-block;"></span>
                Black
            </button>
            <button type="button" class="btn btn-outline-secondary variant-option" 
                    data-variant="color" data-value="white">
                <span class="color-swatch me-2" style="width: 20px; height: 20px; background-color: #fff; border: 1px solid #ddd; border-radius: 50%; display: inline-block;"></span>
                White
            </button>
            <button type="button" class="btn btn-outline-secondary variant-option" 
                    data-variant="color" data-value="blue">
                <span class="color-swatch me-2" style="width: 20px; height: 20px; background-color: #007bff; border-radius: 50%; display: inline-block;"></span>
                Blue
            </button>
        </div>
    </div>

    <div class="variant-group mb-3">
        <label class="form-label fw-bold">Size:</label>
        <div class="variant-options d-flex gap-2">
            <button type="button" class="btn btn-outline-secondary variant-option active" 
                    data-variant="size" data-value="small">S</button>
            <button type="button" class="btn btn-outline-secondary variant-option" 
                    data-variant="size" data-value="medium">M</button>
            <button type="button" class="btn btn-outline-secondary variant-option" 
                    data-variant="size" data-value="large">L</button>
            <button type="button" class="btn btn-outline-secondary variant-option" 
                    data-variant="size" data-value="xlarge">XL</button>
        </div>
    </div>
</div>

<script>
document.addEventListener('DOMContentLoaded', function() {
    const variantOptions = document.querySelectorAll('.variant-option');
    let selectedVariants = {};

    variantOptions.forEach(option => {
        option.addEventListener('click', function() {
            const variant = this.dataset.variant;
            const value = this.dataset.value;

            // Remove active class from siblings
            this.parentNode.querySelectorAll('.variant-option').forEach(opt => {
                opt.classList.remove('active');
            });

            // Add active class to clicked option
            this.classList.add('active');

            // Update selected variants
            selectedVariants[variant] = value;

            // Update product info based on selected variants
            updateProductInfo(selectedVariants);
        });
    });

    function updateProductInfo(variants) {
        // Make AJAX request to get variant info
        fetch(`/api/products/${productId}/variant`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
            },
            body: JSON.stringify(variants)
        })
        .then(response => response.json())
        .then(data => {
            if (data.variant) {
                document.querySelector('.product-price .current-price').textContent = `$${data.variant.price}`;
                document.querySelector('.product-sku').textContent = `SKU: ${data.variant.sku}`;
                document.querySelector('.stock-status').textContent = data.variant.stock_quantity > 0 
                    ? `In Stock (${data.variant.stock_quantity} available)` 
                    : 'Out of Stock';
            }
        });
    }
});
</script>

Product Display

Product Grid Layout

<div class="row">
    @foreach($products as $product)
    <div class="col-lg-3 col-md-4 col-sm-6 mb-4">
        <div class="card product-card h-100">
            <div class="product-image-container">
                <img src="{{ $product->primary_image->image_path ?? '/images/placeholder.jpg' }}" 
                     class="card-img-top" alt="{{ $product->name }}">
                @if($product->discount_percentage > 0)
                <div class="discount-badge">
                    -{{ $product->discount_percentage }}%
                </div>
                @endif
                <div class="product-actions">
                    <button class="btn btn-sm btn-outline-primary" onclick="quickView({{ $product->id }})">
                        <i class="bi bi-eye"></i>
                    </button>
                    <button class="btn btn-sm btn-outline-danger" onclick="addToWishlist({{ $product->id }})">
                        <i class="bi bi-heart"></i>
                    </button>
                </div>
            </div>
            <div class="card-body d-flex flex-column">
                <h6 class="card-title">{{ $product->name }}</h6>
                <div class="product-rating mb-2">
                    <div class="stars">
                        @for($i = 1; $i <= 5; $i++)
                            <i class="bi bi-star{{ $i <= 4 ? '-fill' : '' }} text-warning"></i>
                        @endfor
                    </div>
                    <small class="text-muted">(4.5)</small>
                </div>
                <div class="product-price mb-3">
                    <span class="current-price fw-bold">${{ $product->price }}</span>
                    @if($product->compare_price)
                    <span class="original-price text-muted text-decoration-line-through ms-2">
                        ${{ $product->compare_price }}
                    </span>
                    @endif
                </div>
                <div class="mt-auto">
                    @if($product->is_in_stock)
                    <button class="btn btn-primary w-100" onclick="addToCart({{ $product->id }})">
                        <i class="bi bi-cart-plus"></i> Add to Cart
                    </button>
                    @else
                    <button class="btn btn-secondary w-100" disabled>
                        Out of Stock
                    </button>
                    @endif
                </div>
            </div>
        </div>
    </div>
    @endforeach
</div>

Product List Layout

<div class="product-list">
    @foreach($products as $product)
    <div class="product-item border-bottom py-3">
        <div class="row align-items-center">
            <div class="col-md-2">
                <img src="{{ $product->primary_image->image_path ?? '/images/placeholder.jpg' }}" 
                     class="img-fluid rounded" alt="{{ $product->name }}">
            </div>
            <div class="col-md-6">
                <h5 class="product-title mb-1">
                    <a href="route-products.show-product-gt-slug.html" class="text-decoration-none">
                        {{ $product->name }}
                    </a>
                </h5>
                <p class="product-description text-muted mb-2">
                    {{ Str::limit($product->short_description, 100) }}
                </p>
                <div class="product-rating">
                    <div class="stars d-inline-block">
                        @for($i = 1; $i <= 5; $i++)
                            <i class="bi bi-star{{ $i <= 4 ? '-fill' : '' }} text-warning"></i>
                        @endfor
                    </div>
                    <small class="text-muted ms-2">(4.5) 128 reviews</small>
                </div>
            </div>
            <div class="col-md-2">
                <div class="product-price text-center">
                    <div class="current-price fw-bold">${{ $product->price }}</div>
                    @if($product->compare_price)
                    <div class="original-price text-muted text-decoration-line-through">
                        ${{ $product->compare_price }}
                    </div>
                    @endif
                </div>
            </div>
            <div class="col-md-2">
                <div class="product-actions text-center">
                    @if($product->is_in_stock)
                    <button class="btn btn-primary btn-sm mb-2" onclick="addToCart({{ $product->id }})">
                        <i class="bi bi-cart-plus"></i> Add to Cart
                    </button>
                    @else
                    <button class="btn btn-secondary btn-sm mb-2" disabled>
                        Out of Stock
                    </button>
                    @endif
                    <div>
                        <button class="btn btn-outline-secondary btn-sm me-1" onclick="addToWishlist({{ $product->id }})">
                            <i class="bi bi-heart"></i>
                        </button>
                        <button class="btn btn-outline-secondary btn-sm" onclick="quickView({{ $product->id }})">
                            <i class="bi bi-eye"></i>
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </div>
    @endforeach
</div>

Product Search and Filtering

Search Interface

<div class="product-search mb-4">
    <div class="row">
        <div class="col-md-8">
            <div class="input-group">
                <input type="text" class="form-control" id="productSearch" 
                       placeholder="Search products..." value="{{ request('search') }}">
                <button class="btn btn-outline-secondary" type="button" onclick="searchProducts()">
                    <i class="bi bi-search"></i>
                </button>
            </div>
        </div>
        <div class="col-md-4">
            <select class="form-select" id="sortBy" onchange="sortProducts()">
                <option value="name_asc" {{ request('sort') == 'name_asc' ? 'selected' : '' }}>Name A-Z</option>
                <option value="name_desc" {{ request('sort') == 'name_desc' ? 'selected' : '' }}>Name Z-A</option>
                <option value="price_asc" {{ request('sort') == 'price_asc' ? 'selected' : '' }}>Price Low to High</option>
                <option value="price_desc" {{ request('sort') == 'price_desc' ? 'selected' : '' }}>Price High to Low</option>
                <option value="newest" {{ request('sort') == 'newest' ? 'selected' : '' }}>Newest First</option>
                <option value="popular" {{ request('sort') == 'popular' ? 'selected' : '' }}>Most Popular</option>
            </select>
        </div>
    </div>
</div>

Filter Sidebar

<div class="row">
    <div class="col-md-3">
        <div class="filter-sidebar">
            <h5>Filters</h5>

            <!-- Price Range -->
            <div class="filter-group mb-4">
                <h6>Price Range</h6>
                <div class="price-range">
                    <input type="range" class="form-range" id="priceMin" min="0" max="1000" value="0">
                    <input type="range" class="form-range" id="priceMax" min="0" max="1000" value="1000">
                    <div class="price-display">
                        $<span id="minPrice">0</span> - $<span id="maxPrice">1000</span>
                    </div>
                </div>
            </div>

            <!-- Categories -->
            <div class="filter-group mb-4">
                <h6>Categories</h6>
                <div class="category-filters">
                    @foreach($categories as $category)
                    <div class="form-check">
                        <input class="form-check-input" type="checkbox" 
                               id="category{{ $category->id }}" 
                               value="{{ $category->id }}"
                               {{ in_array($category->id, request('categories', [])) ? 'checked' : '' }}>
                        <label class="form-check-label" for="category{{ $category->id }}">
                            {{ $category->name }}
                            <span class="text-muted">({{ $category->products_count }})</span>
                        </label>
                    </div>
                    @endforeach
                </div>
            </div>

            <!-- Brands -->
            <div class="filter-group mb-4">
                <h6>Brands</h6>
                <div class="brand-filters">
                    @foreach($brands as $brand)
                    <div class="form-check">
                        <input class="form-check-input" type="checkbox" 
                               id="brand{{ $brand->id }}" 
                               value="{{ $brand->id }}"
                               {{ in_array($brand->id, request('brands', [])) ? 'checked' : '' }}>
                        <label class="form-check-label" for="brand{{ $brand->id }}">
                            {{ $brand->name }}
                            <span class="text-muted">({{ $brand->products_count }})</span>
                        </label>
                    </div>
                    @endforeach
                </div>
            </div>

            <!-- Availability -->
            <div class="filter-group mb-4">
                <h6>Availability</h6>
                <div class="form-check">
                    <input class="form-check-input" type="checkbox" id="inStock" 
                           {{ request('in_stock') ? 'checked' : '' }}>
                    <label class="form-check-label" for="inStock">
                        In Stock Only
                    </label>
                </div>
            </div>

            <button class="btn btn-primary w-100" onclick="applyFilters()">Apply Filters</button>
            <button class="btn btn-outline-secondary w-100 mt-2" onclick="clearFilters()">Clear All</button>
        </div>
    </div>

    <div class="col-md-9">
        <!-- Product results will be displayed here -->
        <div id="productResults">
            @include('products.partials.product-grid', ['products' => $products])
        </div>
    </div>
</div>

Inventory Management

Stock Tracking

// Update stock quantities
public function updateStock($productId, $quantity, $operation = 'set')
{
    $product = Product::findOrFail($productId);

    switch ($operation) {
        case 'add':
            $product->increment('stock_quantity', $quantity);
            break;
        case 'subtract':
            $product->decrement('stock_quantity', $quantity);
            break;
        case 'set':
        default:
            $product->update(['stock_quantity' => $quantity]);
            break;
    }

    // Check for low stock alert
    if ($product->stock_quantity <= $product->low_stock_threshold) {
        $this->sendLowStockAlert($product);
    }

    return $product;
}

// Low stock alert
private function sendLowStockAlert($product)
{
    // Send notification to admin
    Notification::route('mail', config('mail.admin_email'))
        ->notify(new LowStockAlert($product));
}

Bulk Operations

<!-- Bulk product operations -->
<div class="bulk-actions mb-3">
    <div class="row align-items-center">
        <div class="col-md-6">
            <div class="form-check">
                <input class="form-check-input" type="checkbox" id="selectAll">
                <label class="form-check-label" for="selectAll">
                    Select All Products
                </label>
            </div>
        </div>
        <div class="col-md-6">
            <div class="bulk-actions-buttons">
                <select class="form-select d-inline-block w-auto me-2" id="bulkAction">
                    <option value="">Bulk Actions</option>
                    <option value="activate">Activate</option>
                    <option value="deactivate">Deactivate</option>
                    <option value="feature">Mark as Featured</option>
                    <option value="unfeature">Remove from Featured</option>
                    <option value="delete">Delete</option>
                </select>
                <button class="btn btn-primary" onclick="executeBulkAction()">Apply</button>
            </div>
        </div>
    </div>
</div>

This comprehensive product management system provides everything needed to run a professional ecommerce store with advanced features for product organization, display, and inventory management.