370 lines
9.3 KiB
Markdown
370 lines
9.3 KiB
Markdown
# Laravel Shop Documentation
|
|
|
|
## Table of Contents
|
|
|
|
### Product Types
|
|
- [Booking Products](./ProductTypes/01-booking-products.md) - Time-based reservations and rentals
|
|
- [Pool Products](./ProductTypes/02-pool-products.md) - Managing groups of booking items
|
|
|
|
### Core Features
|
|
- [Products Overview](./01-products.md) - Basic product management
|
|
- [Stripe Integration](./02-stripe.md) - Payment processing
|
|
- [Purchasing](./03-purchasing.md) - Order and purchase flow
|
|
- [Stripe Checkout](./04-stripe-checkout.md) - Checkout integration
|
|
- [Product Relations](./05-product-relations.md) - How products relate to each other
|
|
|
|
## Quick Start
|
|
|
|
### Understanding Booking Products
|
|
|
|
Booking products are time-based items that can be reserved for specific date ranges. Perfect for:
|
|
- Hotel rooms
|
|
- Rental equipment
|
|
- Parking spaces
|
|
- Event tickets
|
|
- Service appointments
|
|
|
|
[Learn more →](./ProductTypes/01-booking-products.md)
|
|
|
|
### Understanding Pool Products
|
|
|
|
Pool products manage groups of individual items as a unified offering. Customers book from a "pool" and the system automatically assigns an available item. Ideal for:
|
|
- Hotel room categories ("Standard Rooms")
|
|
- Equipment fleets ("Rental Cars - Compact")
|
|
- Parking facilities ("Parking Spaces")
|
|
- General admission seating
|
|
|
|
[Learn more →](./ProductTypes/02-pool-products.md)
|
|
|
|
### Understanding Product Relations
|
|
|
|
The relation system enables complex product associations for marketing and structural purposes:
|
|
- **Marketing**: Upsells, cross-sells, related products
|
|
- **Structural**: Variations, bundles, pool/single items
|
|
|
|
[Learn more →](./05-product-relations.md)
|
|
|
|
## Key Concepts
|
|
|
|
### Stock Management
|
|
- **Booking Products**: Track time-based availability with claims
|
|
- **Pool Products**: Aggregate availability from single items
|
|
- **Claims**: Temporary stock reservations (cart, bookings)
|
|
|
|
### Pricing Strategies
|
|
Pool products support flexible pricing:
|
|
- **LOWEST** (default): Cheapest available item
|
|
- **HIGHEST**: Most expensive available item
|
|
- **AVERAGE**: Average of available items
|
|
|
|
Prices are calculated from **available** items only, ensuring customers see accurate pricing.
|
|
|
|
### Product Relations
|
|
Nine relation types for different purposes:
|
|
- RELATED, UPSELL, CROSS_SELL, DOWNSELL, ADD_ON (marketing)
|
|
- VARIATION, BUNDLE (structural)
|
|
- SINGLE, POOL (special bidirectional for pool management)
|
|
|
|
## Common Workflows
|
|
|
|
### Creating a Booking Product
|
|
|
|
```php
|
|
$room = Product::create([
|
|
'name' => 'Deluxe Suite',
|
|
'type' => ProductType::BOOKING,
|
|
'manage_stock' => true,
|
|
]);
|
|
|
|
$room->increaseStock(5); // 5 rooms available
|
|
|
|
ProductPrice::create([
|
|
'purchasable_id' => $room->id,
|
|
'purchasable_type' => Product::class,
|
|
'unit_amount' => 20000, // $200/day
|
|
'is_default' => true,
|
|
]);
|
|
```
|
|
|
|
### Creating a Pool Product
|
|
|
|
```php
|
|
// 1. Create pool
|
|
$pool = Product::create([
|
|
'name' => 'Parking Spaces',
|
|
'type' => ProductType::POOL,
|
|
'manage_stock' => false,
|
|
]);
|
|
|
|
// 2. Create single items
|
|
$spot1 = Product::create([
|
|
'type' => ProductType::BOOKING,
|
|
'manage_stock' => true,
|
|
]);
|
|
$spot1->increaseStock(1);
|
|
|
|
// 3. Link them
|
|
$pool->attachSingleItems([$spot1->id, $spot2->id, $spot3->id]);
|
|
|
|
// 4. Set pricing strategy
|
|
$pool->setPricingStrategy(PricingStrategy::LOWEST);
|
|
```
|
|
|
|
### Booking a Product
|
|
|
|
```php
|
|
$from = Carbon::parse('2025-01-15');
|
|
$until = Carbon::parse('2025-01-20');
|
|
|
|
// Check availability
|
|
if ($product->isAvailableForBooking($from, $until, $quantity = 1)) {
|
|
// Add to cart (claims stock automatically)
|
|
$cart->addToCart($product, 1, [], $from, $until);
|
|
}
|
|
```
|
|
|
|
### Setting Up Product Relations
|
|
|
|
```php
|
|
// Cross-sells
|
|
$laptop->productRelations()->attach([
|
|
$mouse->id => ['type' => ProductRelationType::CROSS_SELL->value],
|
|
$bag->id => ['type' => ProductRelationType::CROSS_SELL->value],
|
|
]);
|
|
|
|
// Upsells
|
|
$basicPlan->productRelations()->attach($premiumPlan->id, [
|
|
'type' => ProductRelationType::UPSELL->value
|
|
]);
|
|
|
|
// Retrieve
|
|
$crossSells = $laptop->crossSellProducts;
|
|
$upsell = $basicPlan->upsellProducts->first();
|
|
```
|
|
|
|
## Architecture Overview
|
|
|
|
### Product Types
|
|
|
|
```
|
|
ProductType Enum:
|
|
├── SIMPLE → Standard products
|
|
├── VARIABLE → Products with variations
|
|
├── GROUPED → Product groups
|
|
├── EXTERNAL → External/affiliate products
|
|
├── BOOKING → Time-based reservations ⭐
|
|
├── VARIATION → Variant of a variable product
|
|
└── POOL → Container for booking items ⭐
|
|
```
|
|
|
|
### Relation Types
|
|
|
|
```
|
|
ProductRelationType Enum:
|
|
├── RELATED → Similar products
|
|
├── UPSELL → Premium alternatives
|
|
├── CROSS_SELL → Complementary products
|
|
├── DOWNSELL → Lower-priced alternatives
|
|
├── ADD_ON → Optional extras
|
|
├── VARIATION → Product variants
|
|
├── BUNDLE → Package components
|
|
├── SINGLE → Pool → Single items ⭐
|
|
└── POOL → Single item → Pool ⭐
|
|
```
|
|
|
|
### Stock System
|
|
|
|
```
|
|
Stock Flow:
|
|
├── INCREASE → Add inventory (COMPLETED)
|
|
├── DECREASE → Remove inventory (COMPLETED)
|
|
├── CLAIMED → Reserve inventory (PENDING) ⭐
|
|
│ ├── Creates DECREASE entry (reduces available)
|
|
│ └── Creates CLAIMED entry (tracks reservation)
|
|
└── RETURN → Return to inventory (COMPLETED)
|
|
|
|
Available Stock = Sum(COMPLETED entries) - Sum(active CLAIMS)
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### Booking Products
|
|
✅ Always set `manage_stock = true`
|
|
✅ Check availability before booking
|
|
✅ Validate configuration with `validateBookingConfiguration()`
|
|
✅ Handle date ranges properly (from < until)
|
|
|
|
### Pool Products
|
|
✅ Set pool `manage_stock = false`
|
|
✅ Set single items `manage_stock = true`
|
|
✅ Use `attachSingleItems()` for bidirectional relations
|
|
✅ Validate with `validatePoolConfiguration()`
|
|
✅ Set explicit pricing strategy
|
|
|
|
### Relations
|
|
✅ Use appropriate relation types semantically
|
|
✅ Use helper methods (`upsellProducts` not manual queries)
|
|
✅ Eager load to avoid N+1 queries
|
|
✅ Add `sort_order` for display ordering
|
|
|
|
## Troubleshooting
|
|
|
|
### "Not enough stock available"
|
|
- Check if stock was added: `$product->increaseStock(5)`
|
|
- Check if stock is claimed by other bookings
|
|
- Verify `manage_stock = true`
|
|
|
|
### "Pool product has no single items"
|
|
- Use `attachSingleItems()` to link items
|
|
- Verify single items exist and have stock
|
|
|
|
### Pool/Single relations not bidirectional
|
|
- Must use `attachSingleItems()` not regular `attach()`
|
|
- This creates both SINGLE and POOL relations automatically
|
|
|
|
### Pricing shows wrong value
|
|
- Check pricing strategy: `$pool->getPricingStrategy()`
|
|
- Verify which items are available: `getSingleItemsAvailability()`
|
|
- Remember: only available items are priced
|
|
|
|
## Development Tips
|
|
|
|
### Testing Bookings
|
|
|
|
```php
|
|
// Create test booking product
|
|
$product = Product::factory()->create([
|
|
'type' => ProductType::BOOKING,
|
|
'manage_stock' => true,
|
|
]);
|
|
$product->increaseStock(10);
|
|
|
|
// Test availability
|
|
$this->assertTrue($product->isAvailableForBooking($from, $until, 5));
|
|
|
|
// Test booking
|
|
$cart->addToCart($product, 5, [], $from, $until);
|
|
$this->assertEquals(5, $product->getAvailableStock());
|
|
```
|
|
|
|
### Testing Pools
|
|
|
|
```php
|
|
// Create pool with items
|
|
$pool = Product::factory()->create(['type' => ProductType::POOL]);
|
|
$items = Product::factory()->count(3)->create([
|
|
'type' => ProductType::BOOKING,
|
|
'manage_stock' => true,
|
|
]);
|
|
$items->each->increaseStock(1);
|
|
|
|
$pool->attachSingleItems($items->pluck('id')->toArray());
|
|
|
|
// Test availability
|
|
$this->assertEquals(3, $pool->getPoolMaxQuantity($from, $until));
|
|
```
|
|
|
|
### Debugging Relations
|
|
|
|
```php
|
|
// Check relation exists
|
|
dd($product->productRelations()
|
|
->where('related_product_id', $otherId)
|
|
->where('type', ProductRelationType::RELATED->value)
|
|
->exists());
|
|
|
|
// Check bidirectional
|
|
dd($pool->singleProducts()->pluck('id'));
|
|
dd($singleItem->poolProducts()->pluck('id'));
|
|
```
|
|
|
|
## Performance
|
|
|
|
### Eager Loading
|
|
|
|
```php
|
|
// Load all relations
|
|
Product::with([
|
|
'singleProducts',
|
|
'poolProducts',
|
|
'crossSellProducts',
|
|
'upsellProducts',
|
|
'prices'
|
|
])->get();
|
|
|
|
// Load nested
|
|
Product::with('singleProducts.prices')->get();
|
|
```
|
|
|
|
### Caching
|
|
|
|
```php
|
|
// Cache availability calendars
|
|
Cache::remember("pool:{$id}:availability", 3600, function() {
|
|
return $pool->getPoolAvailabilityCalendar($from, $until);
|
|
});
|
|
|
|
// Cache pricing
|
|
Cache::remember("pool:{$id}:price", 3600, function() {
|
|
return $pool->getCurrentPrice();
|
|
});
|
|
```
|
|
|
|
## API Examples
|
|
|
|
### Get Booking Availability
|
|
|
|
```php
|
|
GET /api/products/{id}/availability?from=2025-01-15&until=2025-01-20
|
|
|
|
Response:
|
|
{
|
|
"available": true,
|
|
"max_quantity": 5,
|
|
"calendar": {
|
|
"2025-01-15": 5,
|
|
"2025-01-16": 3,
|
|
...
|
|
}
|
|
}
|
|
```
|
|
|
|
### Get Pool Information
|
|
|
|
```php
|
|
GET /api/products/{id}/pool-details
|
|
|
|
Response:
|
|
{
|
|
"pool": {
|
|
"id": 1,
|
|
"name": "Parking Spaces",
|
|
"pricing_strategy": "lowest"
|
|
},
|
|
"single_items": [
|
|
{"id": 10, "name": "Spot 1", "available": 1},
|
|
{"id": 11, "name": "Spot 2", "available": 0},
|
|
{"id": 12, "name": "Spot 3", "available": 1}
|
|
],
|
|
"price_range": {
|
|
"min": 2000,
|
|
"max": 3000
|
|
}
|
|
}
|
|
```
|
|
|
|
## Contributing
|
|
|
|
When adding new features:
|
|
1. Update relevant documentation
|
|
2. Add test cases for booking/pool scenarios
|
|
3. Consider stock management implications
|
|
4. Validate relation logic
|
|
|
|
## Support
|
|
|
|
For issues or questions:
|
|
- Check troubleshooting sections
|
|
- Review test cases for examples
|
|
- See related documentation for context
|