### Quick Start Guide Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/MANIFEST.txt Guidance for new users on how to navigate and utilize the project documentation. ```APIDOC ## Quick Start ### For First-Time Users: 1. Start with README.md (overview and navigation) 2. Read 00-overview.md (architecture and quick start) 3. Jump to your specific use case file ### For API Reference Lookup: 1. Use INDEX.md (quick links by task) 2. Navigate to the relevant component file 3. Find the exact signature and example ### For Custom Integration: 1. Read 07-configuration.md (setup) 2. Read 03-inventory-system.md (custom driver interface) 3. See working examples in the specific driver section ### For Troubleshooting: 1. Check 05-events-and-exceptions.md (error conditions) 2. Look at error handling patterns 3. Review listener registration examples ``` -------------------------------- ### Example GET Requests for Product Listing Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/06-http-endpoints.md Demonstrates various ways to request the product listing endpoint, including basic listing, searching, filtering by category and price, and applying multiple tags. ```bash # List all published products GET /catalog/products ``` ```bash # Search and filter GET /catalog/products?q=shoes&category=footwear&min_price=100000&in_stock=1 ``` ```bash # Multiple tags (AND logic) GET /catalog/products?tags[]=sale&tags[]=new-arrival ``` ```bash # Sort by price ascending, page 2 GET /catalog/products?sort_by=price&sort_direction=asc&page=2&per_page=24 ``` -------------------------------- ### Install Laravel Product Catalog Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/README.md Install the package via Composer. After installation, publish and run the migrations to set up the necessary database tables. Optionally, publish the configuration file for customization. ```bash composer require aliziodev/laravel-product-catalog ``` ```bash php artisan vendor:publish --tag=product-catalog-migrations php artisan migrate ``` ```bash php artisan vendor:publish --tag=product-catalog-config ``` ```bash php artisan catalog:install ``` -------------------------------- ### Install Package Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/README.md Install the package using Composer. ```bash composer require aliziodev/laravel-product-catalog ``` -------------------------------- ### Install Laravel Product Catalog Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/00-overview.md Install the package using Composer and run the installation artisan command. ```bash composer require aliziodev/laravel-product-catalog php artisan catalog:install ``` -------------------------------- ### Artisan Commands Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/skills/laravel-product-catalog/references/api-reference.md Provides essential Artisan commands for installing, configuring, and seeding the product catalog. Use `catalog:install` for setup and `catalog:seed-demo` for development data. ```bash php artisan catalog:install # interactive: publish config + migrate ``` ```bash php artisan catalog:seed-demo # seed demo data (dev only) ``` -------------------------------- ### Install Spatie Media Library Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/README.md Install the spatie/laravel-medialibrary package and publish its migrations. ```bash composer require spatie/laravel-medialibrary php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="medialibrary-migrations" php artisan migrate ``` -------------------------------- ### Manual Installation Steps Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/skills/laravel-product-catalog/SKILL.md Alternatively, manually publish migrations, run migrations, and optionally publish the configuration file. ```bash php artisan vendor:publish --tag=product-catalog-migrations php artisan migrate php artisan vendor:publish --tag=product-catalog-config # optional ``` -------------------------------- ### Run Installation Command Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/README.md Run the artisan command to publish necessary files and configurations. ```bash php artisan catalog:install ``` -------------------------------- ### Install Laravel Product Catalog Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/skills/laravel-product-catalog/SKILL.md Install the package using Composer and run the artisan command to publish configuration and migrations. ```bash composer require aliziodev/laravel-product-catalog php artisan catalog:install # interactive — publishes config + runs migrations ``` -------------------------------- ### Example Listener for ProductPublished Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/05-events-and-exceptions.md An example listener for the ProductPublished event. This listener sends a new product notification email. ```php use Aliziodev\ProductCatalog\Events\ProductPublished; class SendNewProductNotification { public function handle(ProductPublished $event): void { $product = $event->product; // Send notification, sync to search engine, etc. Mail::to(config('app.admin_email')) ->send(new ProductPublishedMail($product)); } } ``` -------------------------------- ### Example Custom Inventory Provider for Own Table Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/docs/custom-inventory-provider.md This example demonstrates how to implement the InventoryProviderInterface for an application that manages its inventory in a custom 'inventories' table. It includes methods to find inventory records, get quantities, check stock, fulfill requests, adjust stock levels, and set absolute stock values. ```php sku)->first(); } public function getQuantity(ProductVariant $variant): int { return $this->find($variant)?->quantity ?? 0; } public function isInStock(ProductVariant $variant): bool { return $this->getQuantity($variant) > 0; } public function canFulfill(ProductVariant $variant, int $quantity): bool { return $this->getQuantity($variant) >= $quantity; } public function adjust( ProductVariant $variant, int $delta, string $reason = '', ?Model $reference = null, ): void { $record = $this->find($variant); if (! $record) { throw new \RuntimeException("SKU not found in inventory: {$variant->sku}"); } $newQty = $record->quantity + $delta; if ($newQty < 0) { throw InventoryException::insufficientStock($variant, abs($delta)); } $record->update(['quantity' => $newQty]); } public function set( ProductVariant $variant, int $quantity, string $reason = '', ?Model $reference = null, ): void { Inventory::updateOrCreate( ['sku' => $variant->sku], ['quantity' => max(0, $quantity)] ); } } ``` -------------------------------- ### Install Typesense Engine Driver Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/docs/scout-integration.md Install the Typesense PHP client for use with Laravel Scout. ```bash composer require typesense/typesense-php ``` -------------------------------- ### Install Algolia Engine Driver Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/docs/scout-integration.md Install the Algolia search client for use with Laravel Scout. ```bash composer require algolia/algoliasearch-client-php ``` -------------------------------- ### Install Meilisearch Engine Driver Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/docs/scout-integration.md Install the Meilisearch PHP client and HTTP factory for use with Laravel Scout. ```bash composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle ``` -------------------------------- ### Create a Mixed Product with Variants Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/docs/digital-physical-listing.md This example shows how to create a product that offers both physical and digital variants. Each variant is configured with its specific `InventoryPolicy` and attributes. ```php $course = Product::create([ 'name' => 'Laravel Mastery Bundle', 'type' => ProductType::Variable, ]); $formatOption = $course->options()->create(['name' => 'Format', 'position' => 1]); $digital = $formatOption->values()->create(['value' => 'Digital', 'position' => 1]); $physical = $formatOption->values()->create(['value' => 'Print + Digital', 'position' => 2]); // Digital variant $dvVariant = ProductVariant::create([ 'product_id' => $course->id, 'sku' => 'LMB-DIG', 'price' => 149000, ]); $dvVariant->optionValues()->attach($digital); $dvVariant->inventoryItem()->create(['policy' => InventoryPolicy::Allow, 'quantity' => 0]); // Physical bundle variant $pbVariant = ProductVariant::create([ 'product_id' => $course->id, 'sku' => 'LMB-PRINT', 'price' => 349000, 'weight' => 0.450, ]); $pbVariant->optionValues()->attach($physical); $pbVariant->inventoryItem()->create([ 'policy' => InventoryPolicy::Track, 'quantity' => 150, 'low_stock_threshold' => 20, ]); $course->publish(); ``` -------------------------------- ### Handle ProductPublished Event Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/README.md Example of a listener for the ProductPublished event. Access the product via $event->product. ```php use Aliziodev\ProductCatalog\Events\ProductPublished; class SendNewProductNotification { public function handle(ProductPublished $event): void { // $event->product } } ``` -------------------------------- ### Upload and Retrieve Product Media Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/README.md Examples for uploading featured images and gallery items, and retrieving their URLs using different conversions. ```php // Upload featured image $product->addMediaFromRequest('image')->toMediaCollection('featured'); // Upload gallery $product->addMediaFromRequest('gallery')->toMediaCollection('gallery'); // Get URLs $product->getFirstMediaUrl('featured', 'thumb'); $product->getMedia('gallery')->map->getUrl('webp'); ``` -------------------------------- ### InventoryLowStock Event Listener Example Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/05-events-and-exceptions.md An example listener for the InventoryLowStock event. This listener sends a Slack notification when the stock level for a product variant becomes low. ```php use Aliziodev\ProductCatalog\Events\InventoryLowStock; class AlertLowStock { public function handle(InventoryLowStock $event): void { Slack::message("⚠️ {$event->variant->sku} is low: {$event->quantity} units"); } } ``` -------------------------------- ### Example Listener for InventoryReserved Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/05-events-and-exceptions.md An example listener for the InventoryReserved event. This listener logs reservation actions and can notify customers. ```php use Aliziodev\ProductCatalog\Events\InventoryReserved; class TrackReservationLifecycle { public function handle(InventoryReserved $event): void { if ($event->isReserve()) { Log::info("Stock reserved: variant {$event->variant->id}, qty {$event->quantity}"); // Notify customer: order is processing } elseif ($event->isRelease()) { Log::info("Reservation released: variant {$event->variant->id}, qty {$event->quantity}"); // Notify customer: order was cancelled } } } ``` -------------------------------- ### Publish a Product Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/01-models.md Use the publish method to transition a product to the published status and trigger the ProductPublished event. No specific setup is required beyond having a Product instance. ```php public function publish(): void { // ... implementation details } ``` ```php $product->publish(); ``` -------------------------------- ### Managing Product Taxonomy Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/skills/laravel-product-catalog/SKILL.md Examples of creating and associating brands, categories (including hierarchical), and tags with products. ```php // Brand $brand = Brand::create(['name' => 'Nike', 'slug' => 'nike']); $product->update(['brand_id' => $brand->id]); // Category (supports parent–child hierarchy) $apparel = Category::create(['name' => 'Apparel', 'slug' => 'apparel']); $shoes = Category::create(['name' => 'Shoes', 'slug' => 'shoes', 'parent_id' => $apparel->id]); $product->update(['primary_category_id' => $shoes->id]); $product->categories()->sync([$apparel->id, $shoes->id]); // multiple categories // Category tree $tree = Category::whereNull('parent_id')->with('children')->orderBy('position')->get(); // Tag $tag = Tag::create(['name' => 'new-arrival', 'slug' => 'new-arrival']); $product->tags()->attach($tag); ``` -------------------------------- ### Example Listener for InventoryAdjusted Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/05-events-and-exceptions.md An example listener for the InventoryAdjusted event. This listener logs the inventory change details. ```php use Aliziodev\ProductCatalog\Events\InventoryAdjusted; class LogInventoryChange { public function handle(InventoryAdjusted $event): void { $movement = $event->movement; $delta = $event->quantityAfter - $event->quantityBefore; Log::info("Stock adjusted for variant {$event->variant->id}", [ 'delta' => $delta, 'reason' => $event->reason, 'movement_id' => $movement?->id, 'before' => $event->quantityBefore, 'after' => $event->quantityAfter, ]); } } ``` -------------------------------- ### Use ProductType Enum Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/02-enums-and-types.md Example of how to create a product with a specific type using the ProductType enum and how to check the product type. ```php use Aliziodev\ProductCatalog\Enums\ProductType; $product = Product::create([ 'type' => ProductType::Simple, ]); if ($product->type === ProductType::Variable) { // Handle variable product } ``` -------------------------------- ### Use ProductStatus Enum and Methods Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/02-enums-and-types.md Example of accessing a product's status and using its methods like isLive(). Demonstrates lifecycle transitions such as publishing, unpublishing, making private, and archiving. ```php use Aliziodev\ProductCatalog\Enums\ProductStatus; $status = $product->status; // ProductStatus::Published if ($status->isLive()) { // Product is accessible (publicly or privately) } // Lifecycle transitions $product->publish(); // → Published $product->unpublish(); // → Draft $product->makePrivate(); // → Private $product->archive(); // → Archived ``` -------------------------------- ### Start a New Search Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/04-search-system.md Initiates a new product search with an optional text query. Use this to begin building a search query. ```php use Aliziodev\ProductCatalog\Search\ProductSearchBuilder; ProductSearchBuilder::query('red shirt') ->priceBetween(50000, 500000) ->onlyInStock() ->paginate(24); ``` -------------------------------- ### Get Product by Slug Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/INDEX.md Retrieves a single product by its unique slug. ```APIDOC ## GET /products/{slug} ### Description Retrieves a single product identified by its unique slug. ### Method GET ### Endpoint /products/{slug} #### Path Parameters - **slug** (string) - Required - The unique slug of the product to retrieve. ### Response #### Success Response (200) - **data** (object) - The product resource. - **links** (object) - Links related to the product. - **meta** (object) - Metadata about the product. ``` -------------------------------- ### Inventory Operations via Facade Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/skills/laravel-product-catalog/SKILL.md Provides examples of using the `ProductCatalog::inventory()` facade to perform various inventory operations like reading stock levels, checking availability, and writing changes such as setting, adjusting, reserving, releasing, and committing stock. ```APIDOC ## Inventory Operations via Facade The `ProductCatalog` facade provides a unified interface for interacting with the active inventory driver. These operations are designed to be safe under concurrent requests, with write operations running within a database transaction and using `lockForUpdate`. ### Getting the Inventory Facade ```php $inventory = ProductCatalog::inventory(); // Resolves the active driver ``` ### Read Operations - **`getQuantity(Variant $variant)`**: Returns the available quantity (total quantity minus reserved quantity). - **`isInStock(Variant $variant)`**: Returns a boolean indicating if the variant is currently in stock. - **`canFulfill(Variant $variant, int $quantity)`**: Returns a boolean indicating if the requested quantity can be fulfilled from the available stock. ### Write Operations All write operations are executed within a `DB::transaction` and use `lockForUpdate` to prevent race conditions. - **`set(Variant $variant, int $quantity, InventoryReason $reason)`**: Sets the stock quantity to a specific value. - **`adjust(Variant $variant, int $quantity, InventoryReason $reason, ?Order $order = null)`**: Adjusts the stock quantity. Use a negative value to deduct stock (e.g., for sales) and a positive value to restock (e.g., for returns or stock takes). - **`reserve(Variant $variant, int $quantity, InventoryReason $reason, Order $order)`**: Reserves a quantity of stock, effectively placing a soft hold on it. This is typically used when an order is placed but not yet fulfilled. - **`release(Variant $variant, int $quantity, InventoryReason $reason, Order $order)`**: Releases a previously reserved quantity of stock. This is used when an order is cancelled or the reservation expires. - **`commit(Variant $variant, int $quantity, InventoryReason $reason, Order $order)`**: Commits a reserved quantity, permanently deducting it from the total stock. This is typically used when an order is fulfilled. ### Reservation Lifecycle The reservation lifecycle describes the flow of stock from reservation to fulfillment or cancellation: - `reserve()` → `release()`: Occurs when an order is cancelled or a cart reservation expires. - `reserve()` → `commit()`: Occurs when an order is fulfilled, and the reserved quantity is permanently deducted. | Operation | quantity | reserved_quantity | available | Description | |----------------|-----------------|-------------------|-----------|-------------------------------------------------| | `reserve(5)` | unchanged | +5 | -5 | Increases reserved quantity, decreases available | | `release(5)` | unchanged | -5 | +5 | Decreases reserved quantity, increases available | | `commit(5)` | -5 | -5 | unchanged | Decreases total quantity and reserved quantity | | `adjust(-5)` | -5 | unchanged | -5 | Directly changes total quantity and available | ``` -------------------------------- ### Product Slug Routing Example Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/README.md Demonstrates how slugs are generated with a route key suffix and how renaming a product preserves the route key for backward compatibility. Old and new slugs can be used to find a product. ```text /catalog/wireless-mouse-a1b2c3d4 ← original slug /catalog/ergonomic-mouse-a1b2c3d4 ← after rename — same route_key suffix ``` ```php // Find by slug (both old and new slugs resolve) $product = Product::findBySlug('ergonomic-mouse-a1b2c3d4'); $product = Product::findBySlugOrFail('ergonomic-mouse-a1b2c3d4'); // Scope variant Product::published()->bySlug($slug)->firstOrFail(); ``` -------------------------------- ### InventoryItem::isLowStock() Method Example Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/01-models.md Demonstrates how to check if an inventory item is low on stock. Use this to trigger reorder alerts. ```php if ($item->isLowStock()) { // Trigger reorder alert } ``` -------------------------------- ### Check Fulfillment Example Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/03-inventory-system.md Verifies if a specific quantity of a product variant can be fulfilled from current stock. Useful before proceeding with an order. ```php if ($inventory->canFulfill($variant, 10)) { // Safe to proceed with order } ``` -------------------------------- ### Example: Extended Product Model Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/07-configuration.md Demonstrates how to extend the default Product model to add custom relationships, scopes, or Scout integration. Remember to update the configuration file to point to your custom model. ```php // app/Models/Product.php namespace App\Models; use Aliziodev\ProductCatalog\Models\Product as BaseProduct; use Aliziodev\ProductCatalog\Concerns\Searchable as CatalogSearchable; use Laravel\Scout\Searchable; class Product extends BaseProduct { use Searchable; use CatalogSearchable; // Custom relationships public function reviews() { ... } public function ratings() { ... } // Custom scopes public function scopePopular(Builder $query) { ... } } ``` ```php // config/product-catalog.php 'model' => \App\Models\Product::class, ``` -------------------------------- ### ProductSearchBuilder::query() Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/04-search-system.md Starts a new product search with an optional text query. This is a static method used to initiate the search builder. ```APIDOC ## ProductSearchBuilder::query() ### Description Starts a new search with an optional text query. This method initializes the search builder. ### Method `static function query(string $query = '')` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **query** (string) - Optional - Search term (empty string = list all) ### Returns static (builder instance) ### Example ```php use Aliziodev\ProductCatalog\Search\ProductSearchBuilder; ProductSearchBuilder::query('red shirt') ->priceBetween(50000, 500000) ->onlyInStock() ->paginate(24); ``` ``` -------------------------------- ### Handle Product Catalog Driver Not Found Exception Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/05-events-and-exceptions.md Example of catching a ProductCatalogException when attempting to use an unregistered inventory driver. ```php try { $inventory = ProductCatalog::inventory('erp'); // Not registered } catch (ProductCatalogException $e) { // Driver not found } ``` -------------------------------- ### InventoryItem::availableQuantity() Method Example Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/01-models.md Calculates the available quantity for purchase by subtracting reserved quantity from the total quantity. The result is clamped to a minimum of 0. ```php $available = $item->availableQuantity(); // 100 - 10 = 90 ``` -------------------------------- ### GET /catalog/products Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/06-http-endpoints.md Lists products with support for pagination, filtering, searching, and sorting. The endpoint is accessible at `/catalog/products` (or a custom prefix) if enabled in the configuration. ```APIDOC ## GET /catalog/products ### Description Lists products with pagination and filtering capabilities. Supports searching by text, filtering by category, brand, price range, stock status, product type, and status. Allows sorting by various criteria. ### Method GET ### Endpoint /catalog/products ### Parameters #### Query Parameters - **page** (int) - Optional - Page number, defaults to 1. - **per_page** (int) - Optional - Items per page, defaults to 15, max 50. - **q** (string) - Optional - Text search query. - **search** (string) - Optional - Alias for `q`. - **category** (int|string) - Optional - Category ID or slug. - **brand** (int|string) - Optional - Brand ID or slug. - **tags[]** (array) - Optional - Tag IDs or slugs (AND logic). - **tag** (int|string) - Optional - Backward-compatible single tag. - **min_price** (float) - Optional - Minimum price filter. - **max_price** (float) - Optional - Maximum price filter. - **in_stock** (boolean) - Optional - Truthy values: "1", "true", "yes", "on". - **type** (string) - Optional - "simple" or "variable". - **sort_by** (string) - Optional - "price", "name", "newest", "oldest", defaults to "newest". - **sort_direction** (string) - Optional - "asc" or "desc", defaults to "desc". - **status** (string) - Optional - "draft", "published", "private", "archived", defaults to "published". ### Response #### Success Response (200 OK) - **data** (array) - An array of product objects. - **id** (int) - Product ID. - **name** (string) - Product name. - **code** (string) - Product code. - **slug** (string) - Product slug. - **type** (string) - Product type ("simple" or "variable"). - **status** (string) - Product status. - **description** (string) - Product description. - **short_description** (string) - Product short description. - **featured_image_path** (string) - Path to the featured image. - **meta_title** (string) - Meta title for SEO. - **meta_description** (string) - Meta description for SEO. - **meta** (object) - Additional meta information. - **published_at** (string) - Publication date and time. - **brand** (object) - Brand details. - **id** (int) - Brand ID. - **name** (string) - Brand name. - **primary_category** (object) - Primary category details. - **id** (int) - Category ID. - **name** (string) - Category name. - **tags** (array) - Array of tag objects. - **id** (int) - Tag ID. - **name** (string) - Tag name. - **slug** (string) - Tag slug. - **variants** (array) - Array of product variant objects. - **id** (int) - Variant ID. - **sku** (string) - Variant SKU. - **price** (string) - Variant price. - **compare_price** (string) - Compare-at price. - **cost_price** (string) - Cost price. - **is_on_sale** (boolean) - Indicates if the variant is on sale. - **discount_percentage** (float) - Discount percentage. - **weight** (string) - Variant weight. - **length** (string) - Variant length. - **width** (string) - Variant width. - **height** (string) - Variant height. - **meta** (object) - Additional meta information for the variant. - **links** (object) - Pagination links. - **meta** (object) - Pagination metadata. ### Response Example ```json { "data": [ { "id": 1, "name": "Running Shoes", "code": "RS-AIR", "slug": "running-shoes-a1b2c3d4", "type": "variable", "status": "published", "description": "High-performance running shoes.", "short_description": "Lightweight and responsive.", "featured_image_path": null, "meta_title": "Running Shoes — Best Price", "meta_description": "Premium running shoes with responsive cushioning.", "meta": {}, "published_at": "2026-06-19T10:30:00Z", "brand": { "id": 1, "name": "Nike" }, "primary_category": { "id": 5, "name": "Footwear" }, "tags": [ { "id": 10, "name": "sale", "slug": "sale" } ], "variants": [ { "id": 100, "sku": "RS-AIR-RED-42", "price": "850000.0000", "compare_price": "1000000.0000", "cost_price": null, "is_on_sale": true, "discount_percentage": 15, "weight": "0.350", "length": "30.00", "width": "15.00", "height": "12.00", "meta": {} } ] } ], "links": { "first": "http://example.com/catalog/products?page=1", "last": "http://example.com/catalog/products?page=5", "prev": null, "next": "http://example.com/catalog/products?page=2" }, "meta": { "current_page": 1, "from": 1, "last_page": 5, "path": "http://example.com/catalog/products", "per_page": 15, "to": 15, "total": 73 } } ``` ### Example Requests ```bash # List all published products GET /catalog/products # Search and filter GET /catalog/products?q=shoes&category=footwear&min_price=100000&in_stock=1 # Multiple tags (AND logic) GET /catalog/products?tags[]=sale&tags[]=new-arrival # Sort by price ascending, page 2 GET /catalog/products?sort_by=price&sort_direction=asc&page=2&per_page=24 ``` ``` -------------------------------- ### Implement Custom Search Driver Interface Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/docs/scout-integration.md Implement the `SearchDriverInterface` to create a custom search driver if Scout does not fit your stack. This requires defining `paginate` and `get` methods. ```php getQuantity($variant); // 90 (100 total - 10 reserved) ``` -------------------------------- ### Create and Manage Products and Variants Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/00-overview.md Demonstrates creating a simple product, its default variant, setting inventory, publishing, and querying products. Ensure necessary imports are included. ```php use Aliziodev\ProductCatalog\Models\Product; use Aliziodev\ProductCatalog\Enums\ProductType; use Aliziodev\ProductCatalog\Facades\ProductCatalog; // Create product $product = Product::create([ 'name' => 'T-Shirt', 'code' => 'TS-001', 'type' => ProductType::Simple, ]); // Create variant $variant = $product->variants()->create([ 'sku' => 'TS-001-WHT', 'price' => 150000, 'is_default' => true, ]); // Set stock $variant->inventoryItem()->create(['quantity' => 100]); // Publish $product->publish(); // Query Product::published()->inStock()->with('variants')->get(); ``` -------------------------------- ### Get ProductVariant Display Name Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/01-models.md Generates a human-readable label for the product variant based on its option values. Example: "Red / XL". ```php public function displayName(): string { // Human-readable label from option values. Example: "Red / XL". } ``` ```php echo $variant->displayName(); // "Red / XL" ``` -------------------------------- ### Register Inventory Drivers in Service Provider Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/docs/custom-inventory-provider.md Extend the ProductCatalog facade to register custom inventory drivers. This example shows how to register both an 'app' driver and an 'erp' driver. ```php use Aliziodev\ProductCatalog\Facades\ProductCatalog; public function boot(): void { // Driver backed by your app table ProductCatalog::extend('app', function ($app) { return new \App\Inventory\AppInventoryProvider; }); // Driver backed by an external ERP ProductCatalog::extend('erp', function ($app) { return new \App\Inventory\ErpInventoryProvider( baseUrl: config('services.erp.url'), apiKey: config('services.erp.key'), ); }); } ``` -------------------------------- ### Create and Manage Simple Products Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/README.md Demonstrates how to create a simple product with basic details and manage its lifecycle (publish, unpublish, archive) and state checks. ```php use Aliziodev\ProductCatalog\Models\Product; use Aliziodev\ProductCatalog\Enums\ProductType; // Simple product (single SKU) $product = Product::create([ 'name' => 'Wireless Mouse', 'code' => 'WM-001', // optional parent SKU / product code 'type' => ProductType::Simple, 'short_description' => 'Ergonomic wireless mouse, 2.4 GHz.', 'meta_title' => 'Wireless Mouse — Best Price', 'meta' => ['warranty' => '1 year'], ]); // Lifecycle $product->publish(); // draft → published, fires ProductPublished event $product->unpublish(); // published → draft $product->archive(); // → archived, fires ProductArchived event // State checks $product->isPublished(); $product->isDraft(); $product->isArchived(); $product->isSimple(); $product->isVariable(); ``` -------------------------------- ### Custom ERP Inventory Driver Implementation Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/03-inventory-system.md Implement the InventoryProviderInterface to connect your own stock system, such as an external ERP. This example shows how to fetch, check, and adjust stock quantities. ```php namespace App\Inventory; use Aliziodev\ProductCatalog\Contracts\InventoryProviderInterface; use Aliziodev\ProductCatalog\Exceptions\InventoryException; use Aliziodev\ProductCatalog\Models\ProductVariant; use App\Models\ErpInventory; use Illuminate\Database\Eloquent\Model; class ErpInventoryProvider implements InventoryProviderInterface { public function getQuantity(ProductVariant $variant): int { return ErpInventory::where('sku', $variant->sku) ->value('available_qty') ?? 0; } public function isInStock(ProductVariant $variant): bool { return $this->getQuantity($variant) > 0; } public function canFulfill(ProductVariant $variant, int $quantity): bool { return $this->getQuantity($variant) >= $quantity; } public function adjust( ProductVariant $variant, int $delta, string $reason = '', ?Model $reference = null, ): void { $record = ErpInventory::where('sku', $variant->sku)->firstOrFail(); $new = $record->available_qty + $delta; if ($new < 0) { throw InventoryException::insufficientStock(abs($delta), $record->available_qty); } $record->update(['available_qty' => $new]); // Log to ERP audit trail } public function set( ProductVariant $variant, int $quantity, string $reason = '', ?Model $reference = null, ): void { ErpInventory::updateOrCreate( ['sku' => $variant->sku], ['available_qty' => max(0, $quantity)] ); } public function reserve( ProductVariant $variant, int $quantity, string $reason = '', ?Model $reference = null, ): void { // Query ERP for reserved inventory // Throw InventoryException if unavailable } public function release( ProductVariant $variant, int $quantity, string $reason = '', ?Model $reference = null, ): void { // Release from ERP reservation system } public function commit( ProductVariant $variant, int $quantity, string $reason = '', ?Model $reference = null, ): void { // Convert ERP reservation to permanent deduction } } ``` -------------------------------- ### Create and Manage Products and Variants Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/README.md Demonstrates how to create a product, add variants, set inventory, publish the product, and query published, in-stock products. ```php use Aliziodev\ProductCatalog\Models\Product; use Aliziodev\ProductCatalog\Models\ProductVariant; use Aliziodev\ProductCatalog\Enums\ProductType; use Aliziodev\ProductCatalog\Enums\InventoryPolicy; use Aliziodev\ProductCatalog\Facades\ProductCatalog; // 1. Create product $product = Product::create(['name' => 'T-Shirt', 'code' => 'TS-001', 'type' => ProductType::Simple]); // 2. Create variant $variant = $product->variants()->create(['sku' => 'TS-001-WHT', 'price' => 150000, 'is_default' => true]); // 3. Set stock $variant->inventoryItem()->create(['quantity' => 100, 'policy' => InventoryPolicy::Track]); // 4. Publish $product->publish(); // 5. Query Product::published()->inStock()->with('variants')->get(); ``` -------------------------------- ### Set Stock Example Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/03-inventory-system.md Sets the absolute stock quantity for a product variant. Typically used after a physical stocktake. Requires a non-negative quantity and an audit reason. ```php // After physical stocktake counts 87 units $inventory->set($variant, 87, InventoryReason::STOCKTAKE); ``` -------------------------------- ### Table Prefix Examples Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/docs/configuration.md Illustrates how different `table_prefix` values affect the names of tables created by the package. Changing the prefix requires manual table renaming if migrations have already run. ```markdown | Value | Tables created | |---|---| | `catalog_` (default) | `catalog_products`, `catalog_product_variants`, … | | `shop_` | `shop_products`, `shop_product_variants`, … | | `''` (empty) | `products`, `product_variants`, … | ``` -------------------------------- ### Adjust Inventory with Custom Reason Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/07-configuration.md Example of adjusting inventory levels using a custom reason code. This demonstrates how to use custom reasons in application code after defining them in the configuration. ```php $inventory->adjust($variant, -2, 'promotion', $promo); ``` -------------------------------- ### Create a Simple Product Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/skills/laravel-product-catalog/SKILL.md Use the Product::create method to instantiate a new simple product with a single SKU. Ensure all required fields like name, code, type, and descriptions are provided. ```php // Simple product (single SKU) $product = Product::create([ 'name' => 'Wireless Mouse', 'code' => 'WM-001', // parent SKU (optional) 'type' => ProductType::Simple, 'short_description' => 'Ergonomic wireless mouse, 2.4 GHz', 'meta_title' => 'Wireless Mouse — Best Price', 'meta' => ['warranty' => '1 year'], // free-form JSON ]); ``` -------------------------------- ### Setting Up InventoryItem Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/skills/laravel-product-catalog/SKILL.md Demonstrates how to create an inventory item for a product variant. It's crucial to always create an inventory item, even for the 'Allow' policy, to ensure accurate stock tracking and visibility in queries. ```APIDOC ## Setting Up InventoryItem This section explains the process of creating an `inventoryItem` for a product variant. It is essential to create an `inventoryItem` for every variant, regardless of the `InventoryPolicy` set, to ensure that the variant is correctly recognized by stock-related queries. ### Method ```php $variant->inventoryItem()->create([ 'quantity' => 100, 'policy' => InventoryPolicy::Track, // Track | Allow | Deny 'low_stock_threshold' => 10, ]); ``` ### Policies **Track**: Checks actual stock; denies when `quantity <= reserved_quantity`. **Allow**: Always in stock; overselling is permitted (e.g., for digital products or pre-orders). **Deny**: Always out of stock; the variant is unavailable. **Important Note**: The `Product::inStock()` method relies on `whereHas('inventoryItem', ...)`. Variants lacking an `inventoryItem` record will be excluded, even if intended for the 'Allow' policy. Always ensure an `inventoryItem` is created. ``` -------------------------------- ### Create Variable Products with Options and Variants Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/README.md Shows how to create a variable product, define options (like color and size) with their values, create product variants, and attach option values to them. Includes helpers for SKU generation and display names. ```php use Aliziodev\ProductCatalog\Models\ProductVariant; use Aliziodev\ProductCatalog\Enums\ProductType; // Variable product $product = Product::create([ 'name' => 'Running Shoes', 'code' => 'RS-AIR', 'type' => ProductType::Variable, ]); // Define options $colorOption = $product->options()->create(['name' => 'Color', 'position' => 1]); $red = $colorOption->values()->create(['value' => 'Red', 'position' => 1]); $blue = $colorOption->values()->create(['value' => 'Blue', 'position' => 2]); $sizeOption = $product->options()->create(['name' => 'Size', 'position' => 2]); $size42 = $sizeOption->values()->create(['value' => '42', 'position' => 1]); $size43 = $sizeOption->values()->create(['value' => '43', 'position' => 2]); // Create variant $variant = ProductVariant::create([ 'product_id' => $product->id, 'sku' => 'RS-AIR-RED-42', 'price' => 850000, 'compare_price' => 1000000, // original price (for sale badge) 'cost_price' => 500000, // internal cost 'weight' => 0.350, 'length' => 30, 'width' => 15, 'height' => 12, 'is_default' => true, 'is_active' => true, 'meta' => ['barcode' => '8991234567890'], ]); // Attach option values to variant $variant->optionValues()->sync([$red->id, $size42->id]); // Auto-generate SKU from product code + option values $variant->load('optionValues'); $suggested = $product->buildVariantSku($variant); // "RS-AIR-RED-42" // Human-readable label $variant->displayName(); // "Red / 42" // Pricing helpers $variant->isOnSale(); // true — compare_price > price $variant->discountPercentage(); // 15 (int) ``` -------------------------------- ### Install Laravel Scout Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/04-search-system.md Use Composer to install the Laravel Scout package, which provides a bridge for various search engines. ```bash composer require laravel/scout ``` -------------------------------- ### Create a Variable Product in PHP Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/00-overview.md Demonstrates how to create a product with multiple options (like color and size) and their corresponding variants. Ensure the ProductType and InventoryReason constants are available. ```php 'Running Shoes', 'code' => 'RS-AIR', 'type' => ProductType::Variable, ]); // Define options $color = $product->options()->create(['name' => 'Color', 'position' => 1]); $red = $color->values()->create(['value' => 'Red', 'position' => 1]); $blue = $color->values()->create(['value' => 'Blue', 'position' => 2]); $size = $product->options()->create(['name' => 'Size', 'position' => 2]); $s42 = $size->values()->create(['value' => '42', 'position' => 1]); // Create variant $variant = ProductVariant::create([ 'product_id' => $product->id, 'sku' => 'RS-AIR-RED-42', 'price' => 850000, 'is_default' => true, ]); // Attach option values $variant->optionValues()->sync([$red->id, $s42->id]); // Generate SKU suggestion $variant->load('optionValues'); $variant->update(['sku' => $product->buildVariantSku($variant)]); ``` -------------------------------- ### Get All Product Search Results Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/04-search-system.md Use the `get()` method to execute a product search and retrieve all matching results as an Eloquent Collection, without pagination. ```php public function get(): Collection ``` ```php $all = ProductSearchBuilder::query('shoes')->get(); ``` -------------------------------- ### Key Configuration Options Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/skills/laravel-product-catalog/SKILL.md Explore essential configuration settings for models, table prefixes, inventory drivers, slug generation, search drivers, and routes. ```php 'model' => \App\Models\Product::class, // override when extending the base Product model 'table_prefix' => env('PRODUCT_CATALOG_TABLE_PREFIX', 'catalog_'), // set BEFORE migrate 'inventory' => [ 'driver' => env('PRODUCT_CATALOG_INVENTORY_DRIVER', 'database'), // 'database' | 'null' | custom 'movement_reasons' => [], // add app-specific reason strings here ], 'slug' => [ 'auto_generate' => true, 'route_key_length' => 8, // random suffix length (4–32) ], 'search' => [ 'driver' => env('PRODUCT_CATALOG_SEARCH_DRIVER', 'database'), // 'database' | 'scout' | custom ], 'routes' => [ 'enabled' => env('PRODUCT_CATALOG_ROUTES_ENABLED', false), 'prefix' => 'catalog', 'middleware' => ['api'], ] ``` -------------------------------- ### Product State Transitions Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/00-overview.md Demonstrates the methods used to transition a product between different states like publishing, archiving, or making it private. These methods are part of the product lifecycle management. ```php $product->publish() $product->archive() $product->unpublish() $product->makePrivate() ``` -------------------------------- ### Get Inventory Movements for ProductVariant Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/01-models.md Retrieves all inventory movement records for a product variant. ```php public function movements(): HasMany { // All inventory movement records. } ``` -------------------------------- ### Get Parent Product of ProductVariant Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/01-models.md Retrieves the parent product associated with a product variant. ```php public function product(): BelongsTo { // Parent product. } ``` -------------------------------- ### get() Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/04-search-system.md Executes the search query and returns all results as a collection, without applying pagination. ```APIDOC ## get() ### Description Executes the search query and returns all results as a collection, without applying pagination. ### Method ```php public function get(): Collection ``` ### Returns Illuminate\Database\Eloquent\Collection ### Example ```php $all = ProductSearchBuilder::query('shoes')->get(); ``` ``` -------------------------------- ### Get Inventory Item for ProductVariant Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/01-models.md Retrieves the stock tracking record associated with a product variant. ```php public function inventoryItem(): HasOne { // Stock tracking record. } ``` -------------------------------- ### Import Products for Scout Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/04-search-system.md Run the Scout artisan command to import existing products into the search index. ```bash php artisan scout:import "App\Models\Product" ``` -------------------------------- ### Get Option Values for ProductVariant Source: https://github.com/aliziodev/laravel-product-catalog/blob/main/_autodocs/01-models.md Retrieves all option values that define a specific product variant. ```php public function optionValues(): BelongsToMany { // Option values defining this variant. } ```