446 lines
13 KiB
Markdown
446 lines
13 KiB
Markdown
# Laravel Shop Package
|
|
|
|
[](https://github.com/blax-software/laravel-shop/actions/workflows/tests.yml)
|
|
[](https://packagist.org/packages/blax-software/laravel-shop)
|
|
[](https://packagist.org/packages/blax-software/laravel-shop)
|
|
[](https://packagist.org/packages/blax-software/laravel-shop)
|
|
|
|
A comprehensive headless e-commerce package for Laravel with stock management, Stripe integration, and product actions.
|
|
|
|
## Features
|
|
|
|
- 🛍️ **Product Management** - Simple, variable, grouped, and external products
|
|
- 💰 **Multi-Currency Support** - Handle multiple currencies with ease
|
|
- 📦 **Advanced Stock Management** - Stock reservations, low stock alerts, and backorders
|
|
- 💳 **Stripe Integration** - Built-in Stripe product and price synchronization
|
|
- 🎯 **Product Actions** - Execute custom actions on product events (purchases, refunds)
|
|
- 🔗 **Product Relations** - Related products, upsells, and cross-sells
|
|
- 🌍 **Translation Ready** - Built-in meta translation support
|
|
- 📊 **Stock Logging** - Complete audit trail of stock changes
|
|
- 🎨 **Headless Architecture** - Perfect for API-first applications
|
|
- ⚡ **Caching Support** - Built-in cache management for better performance
|
|
- 🛒 **Shopping Capabilities** - Built-in trait for any purchaser model
|
|
- 🎭 **Facade Support** - Clean, expressive API through Shop and Cart facades
|
|
- 👤 **Guest Cart Support** - Session-based carts for unauthenticated users
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
composer require blax-software/laravel-shop
|
|
```
|
|
|
|
Publish the configuration:
|
|
|
|
```bash
|
|
php artisan vendor:publish --provider="Blax\Shop\ShopServiceProvider"
|
|
```
|
|
|
|
Run migrations:
|
|
|
|
```bash
|
|
php artisan migrate
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
### Setup Your User Model
|
|
|
|
Add the `HasShoppingCapabilities` trait to any model that should be able to purchase products (typically your User model):
|
|
|
|
```php
|
|
use Blax\Shop\Traits\HasShoppingCapabilities;
|
|
|
|
class User extends Authenticatable
|
|
{
|
|
use HasShoppingCapabilities;
|
|
|
|
// ...existing code...
|
|
}
|
|
```
|
|
|
|
### Creating Your First Product
|
|
|
|
```php
|
|
use Blax\Shop\Models\Product;
|
|
|
|
$product = Product::create([
|
|
'slug' => 'amazing-t-shirt',
|
|
'sku' => 'TSH-001',
|
|
'type' => 'simple',
|
|
'manage_stock' => true,
|
|
'status' => 'published',
|
|
]);
|
|
|
|
$product->prices()->create([
|
|
'currency' => 'USD',
|
|
'unit_amount' => 1999, // $19.99
|
|
'sale_unit_amount' => 1499, // $14.99
|
|
'is_default' => true,
|
|
]);
|
|
|
|
$product->adjustStock(StockType::INCREASE, 100); // Add 100 items to stock
|
|
$product->adjustStock(StockType::DECREASE, 90); // Remove 100 items from stock
|
|
$product->adjustStock(
|
|
StockType::CLAIMED,
|
|
10,
|
|
from: now(),
|
|
until: now()->addDay(),
|
|
note: 'Booked'
|
|
); // Claim/reserve 10 stocks
|
|
|
|
|
|
// Add translated name
|
|
$product->setLocalized('name', 'Amazing T-Shirt', 'en');
|
|
$product->setLocalized('description', 'A comfortable cotton t-shirt', 'en');
|
|
```
|
|
|
|
### Working with Cart (Authenticated Users)
|
|
|
|
```php
|
|
use Blax\Shop\Facades\Cart;
|
|
use Blax\Shop\Models\Product;
|
|
|
|
$product = Product::find($productId);
|
|
$user = auth()->user();
|
|
|
|
// Add to cart (via facade)
|
|
Cart::add($product, quantity: 2);
|
|
|
|
// Or via user trait
|
|
$cartItem = $user->addToCart($product, quantity: 1);
|
|
|
|
// Get cart totals
|
|
$total = Cart::total();
|
|
$itemCount = Cart::itemCount();
|
|
|
|
// Check if cart is empty
|
|
if (Cart::isEmpty()) {
|
|
// Cart is empty
|
|
}
|
|
|
|
// Remove from cart
|
|
Cart::remove($product);
|
|
|
|
// Clear entire cart
|
|
Cart::clear();
|
|
|
|
// Checkout cart
|
|
$completedPurchases = Cart::checkout();
|
|
```
|
|
|
|
### Working with Guest Carts
|
|
|
|
```php
|
|
use Blax\Shop\Facades\Cart;
|
|
|
|
// Create or retrieve guest cart (uses session ID automatically)
|
|
$guestCart = Cart::guest();
|
|
|
|
// Or with specific session ID
|
|
$guestCart = Cart::guest('custom-session-id');
|
|
|
|
// Add items to guest cart
|
|
$guestCart->addToCart($product, quantity: 1);
|
|
|
|
// Get guest cart totals
|
|
$total = Cart::total($guestCart);
|
|
$itemCount = Cart::itemCount($guestCart);
|
|
|
|
// Check if guest cart is empty
|
|
if (Cart::isEmpty($guestCart)) {
|
|
// Cart is empty
|
|
}
|
|
|
|
// Clear guest cart
|
|
Cart::clear($guestCart);
|
|
|
|
// Convert guest cart to user cart on login
|
|
$guestCart->convertToUserCart($user);
|
|
```
|
|
|
|
### Purchasing Products Directly
|
|
|
|
```php
|
|
use Blax\Shop\Models\Product;
|
|
|
|
$product = Product::find($productId);
|
|
$user = auth()->user();
|
|
|
|
// Simple purchase
|
|
$purchase = $user->purchase($product, quantity: 1);
|
|
|
|
// Purchase with options
|
|
$purchase = $user->purchase($product, quantity: 2, options: [
|
|
'price_id' => $priceId,
|
|
'charge_id' => $paymentIntent->id,
|
|
]);
|
|
|
|
// Check if user has purchased
|
|
if ($user->hasPurchased($product)) {
|
|
// Grant access
|
|
}
|
|
```
|
|
|
|
### Using Shop Facade
|
|
|
|
```php
|
|
use Blax\Shop\Facades\Shop;
|
|
|
|
// Get all products
|
|
$products = Shop::products()->get();
|
|
|
|
// Get published products only
|
|
$products = Shop::published()->get();
|
|
|
|
// Get products in stock
|
|
$products = Shop::inStock()->get();
|
|
|
|
// Get featured products
|
|
$featured = Shop::featured()->get();
|
|
|
|
// Search products
|
|
$results = Shop::search('t-shirt')->get();
|
|
|
|
// Check stock availability
|
|
if (Shop::checkStock($product, quantity: 5)) {
|
|
// Sufficient stock available
|
|
}
|
|
|
|
// Get available stock
|
|
$available = Shop::getAvailableStock($product);
|
|
|
|
// Check if product is on sale
|
|
if (Shop::isOnSale($product)) {
|
|
// Show sale badge
|
|
}
|
|
|
|
// Get configuration
|
|
$currency = Shop::currency(); // USD
|
|
$config = Shop::config('cart.expire_after_days', 30);
|
|
```
|
|
|
|
## Documentation
|
|
|
|
- [Product Management](docs/01-products.md)
|
|
- [Stripe Integration](docs/02-stripe.md)
|
|
- [Purchasing Products](docs/03-purchasing.md)
|
|
- [Subscriptions](docs/04-subscriptions.md)
|
|
- [Stock Management](docs/05-stock.md)
|
|
- [API Usage](docs/06-api.md)
|
|
|
|
## Models
|
|
|
|
The package includes the following models:
|
|
|
|
- **Product** - Main product model with support for simple, variable, grouped, and external products
|
|
- **ProductPrice** - Multi-currency pricing with sale prices and subscription support
|
|
- **ProductCategory** - Hierarchical product categories
|
|
- **ProductStock** - Advanced stock management with reservations and logging
|
|
- **ProductAttribute** - Product attributes (size, color, material, etc.)
|
|
- **ProductPurchase** - Purchase records and history
|
|
- **ProductAction** - Custom actions triggered by product events
|
|
- **ProductActionRun** - Execution logs for product actions
|
|
- **Cart** - Shopping cart for authenticated users and guests
|
|
- **CartItem** - Individual items in a cart
|
|
- **PaymentMethod** - Saved payment methods
|
|
- **PaymentProviderIdentity** - Links users to payment providers (Stripe, etc.)
|
|
|
|
## Traits
|
|
|
|
Available traits for your models:
|
|
|
|
- **HasShoppingCapabilities** - Complete shopping functionality (cart + purchases)
|
|
- **HasCart** - Cart management functionality only
|
|
- **HasPaymentMethods** - Payment method management
|
|
- **HasStripeAccount** - Stripe integration for users
|
|
- **HasPrices** - Price management (for Product model)
|
|
- **HasStocks** - Stock management (for Product model)
|
|
- **HasCategories** - Category relationships (for Product model)
|
|
- **HasProductRelations** - Related products, upsells, cross-sells
|
|
- **HasChargingOptions** - Payment processing capabilities
|
|
|
|
## Facades
|
|
|
|
The package provides two facades for cleaner API access:
|
|
|
|
### Shop Facade
|
|
|
|
```php
|
|
use Blax\Shop\Facades\Shop;
|
|
|
|
Shop::products() // Get product query builder
|
|
Shop::product($id) // Find product by ID
|
|
Shop::categories() // Get categories query builder
|
|
Shop::inStock() // Get in-stock products
|
|
Shop::featured() // Get featured products
|
|
Shop::published() // Get published products
|
|
Shop::search($query) // Search products
|
|
Shop::checkStock($product, $qty) // Check stock availability
|
|
Shop::getAvailableStock($product) // Get available stock quantity
|
|
Shop::isOnSale($product) // Check if product is on sale
|
|
Shop::config($key, $default) // Get shop configuration
|
|
Shop::currency() // Get default currency
|
|
```
|
|
|
|
### Cart Facade
|
|
|
|
```php
|
|
use Blax\Shop\Facades\Cart;
|
|
|
|
Cart::current() // Get current user's cart
|
|
Cart::guest($sessionId) // Get/create guest cart
|
|
Cart::forUser($user) // Get cart for specific user
|
|
Cart::find($cartId) // Find cart by ID
|
|
Cart::add($product, $qty, $params) // Add item to cart
|
|
Cart::remove($product, $qty) // Remove item from cart
|
|
Cart::update($cartItem, $qty) // Update cart item quantity
|
|
Cart::clear($cart) // Clear cart items
|
|
Cart::checkout($cart) // Checkout cart
|
|
Cart::total($cart) // Get cart total
|
|
Cart::itemCount($cart) // Get item count
|
|
Cart::items($cart) // Get cart items
|
|
Cart::isEmpty($cart) // Check if cart is empty
|
|
Cart::isExpired($cart) // Check if cart is expired
|
|
Cart::isConverted($cart) // Check if cart was converted
|
|
Cart::unpaidAmount($cart) // Get unpaid amount
|
|
Cart::paidAmount($cart) // Get paid amount
|
|
```
|
|
|
|
## Configuration
|
|
|
|
The `config/shop.php` file contains all configuration options:
|
|
|
|
```php
|
|
return [
|
|
// Table names (customizable for multi-tenancy)
|
|
'tables' => [
|
|
'products' => 'products',
|
|
'product_categories' => 'product_categories',
|
|
'product_prices' => 'product_prices',
|
|
'product_stocks' => 'product_stocks',
|
|
'product_attributes' => 'product_attributes',
|
|
'product_purchases' => 'product_purchases',
|
|
'product_actions' => 'product_actions',
|
|
'product_action_runs' => 'product_action_runs',
|
|
'product_relations' => 'product_relations',
|
|
'carts' => 'carts',
|
|
'cart_items' => 'cart_items',
|
|
'cart_discounts' => 'cart_discounts',
|
|
'payment_methods' => 'payment_methods',
|
|
'payment_provider_identities' => 'payment_provider_identities',
|
|
],
|
|
|
|
// Model classes (allow overriding)
|
|
'models' => [
|
|
'product' => \Blax\Shop\Models\Product::class,
|
|
'product_price' => \Blax\Shop\Models\ProductPrice::class,
|
|
'product_category' => \Blax\Shop\Models\ProductCategory::class,
|
|
'product_stock' => \Blax\Shop\Models\ProductStock::class,
|
|
'product_attribute' => \Blax\Shop\Models\ProductAttribute::class,
|
|
'product_purchase' => \Blax\Shop\Models\ProductPurchase::class,
|
|
'cart' => \Blax\Shop\Models\Cart::class,
|
|
'cart_item' => \Blax\Shop\Models\CartItem::class,
|
|
'payment_provider_identity' => \Blax\Shop\Models\PaymentProviderIdentity::class,
|
|
'payment_method' => \Blax\Shop\Models\PaymentMethod::class,
|
|
],
|
|
|
|
// API Routes
|
|
'routes' => [
|
|
'enabled' => true,
|
|
'prefix' => 'api/shop',
|
|
'middleware' => ['api'],
|
|
'name_prefix' => 'shop.',
|
|
],
|
|
|
|
// Stock management
|
|
'stock' => [
|
|
'track_inventory' => true,
|
|
'allow_backorders' => false,
|
|
'low_stock_threshold' => 5,
|
|
'log_changes' => true,
|
|
'auto_release_expired' => true,
|
|
],
|
|
|
|
// Product actions
|
|
'actions' => [
|
|
'path' => app_path('Jobs/ProductAction'),
|
|
'namespace' => 'App\\Jobs\\ProductAction',
|
|
'auto_discover' => true,
|
|
],
|
|
|
|
// Stripe integration
|
|
'stripe' => [
|
|
'enabled' => env('SHOP_STRIPE_ENABLED', false),
|
|
'sync_prices' => true,
|
|
],
|
|
|
|
// Cache configuration
|
|
'cache' => [
|
|
'enabled' => env('SHOP_CACHE_ENABLED', true),
|
|
'ttl' => 3600,
|
|
'prefix' => 'shop:',
|
|
],
|
|
|
|
// Cart configuration
|
|
'cart' => [
|
|
'expire_after_days' => 30,
|
|
'auto_cleanup' => true,
|
|
'merge_on_login' => true,
|
|
],
|
|
|
|
// API response format
|
|
'api' => [
|
|
'include_meta' => true,
|
|
'wrap_response' => true,
|
|
'response_key' => 'data',
|
|
],
|
|
];
|
|
```
|
|
|
|
## Commands
|
|
|
|
### Add Example Products
|
|
|
|
Create example products for testing and demonstration purposes:
|
|
|
|
```bash
|
|
# Create 2 products of each type (default)
|
|
php artisan shop:add-example-products
|
|
|
|
# Create 5 products of each type
|
|
php artisan shop:add-example-products --count=5
|
|
|
|
# Clean existing example products first
|
|
php artisan shop:add-example-products --clean
|
|
```
|
|
|
|
This command creates:
|
|
- ✅ All 4 product types (simple, variable, grouped, external)
|
|
- ✅ Product categories
|
|
- ✅ Product attributes (material, size, color, etc.)
|
|
- ✅ Multiple pricing options (multi-currency, subscriptions)
|
|
- ✅ Example product actions (email notifications, stats updates)
|
|
- ✅ Variations for variable products
|
|
- ✅ Child products for grouped products
|
|
- ✅ Realistic data using Faker
|
|
|
|
### Reinstall Shop Tables
|
|
|
|
```bash
|
|
# With confirmation
|
|
php artisan shop:reinstall
|
|
|
|
# Force without confirmation
|
|
php artisan shop:reinstall --force
|
|
```
|
|
|
|
⚠️ **Warning:** This will delete all shop data!
|
|
|
|
## License
|
|
|
|
MIT License
|
|
|
|
## Support
|
|
|
|
For issues and questions, please use the [GitHub issue tracker](https://github.com/blax/laravel-shop/issues).
|