Common Use Cases
Real-world scenarios every Laravel developer faces. Find your situation and see exactly how specifications solve your specific problems.
🛍️ E-commerce & Shopping
Product Search & Filtering
Your Challenge:
"Our product search has 15 different filters, the controller is 200+ lines, and adding new filters breaks existing functionality."
The Specification Solution:
// Instead of controller chaos
class ProductController extends Controller
{
public function search(ProductSearchRequest $request)
{
$searchSpec = ProductSearchSpecification::fromRequest($request);
return Product::whereSpecification($searchSpec)->paginate(20);
}
}
// Clean, composable search logic
class ProductSearchSpecification extends AbstractSpecification
{
public static function fromRequest($request): self
{
$builder = new SpecificationBuilder();
if ($request->categories) {
$builder->whereIn('category_id', $request->categories);
}
if ($request->price_range) {
$builder->whereBetween('price', $request->price_range[0], $request->price_range[1]);
}
if ($request->in_stock_only) {
$builder->where('stock_quantity', '>', 0);
}
return new self($builder->build());
}
}
Business Impact:
- ✅ New filters in minutes, not hours
- ✅ Zero regression risk when adding features
- ✅ Reusable filters across mobile API, web, admin panel
- ✅ A/B testing different filter combinations
Shopping Cart Eligibility
Your Challenge:
"We have complex rules for discounts, free shipping, promotions, and they're scattered across controllers, jobs, and policies."
The Specification Solution:
class CartEligibilityService
{
public function checkDiscountEligibility(Cart $cart, User $user): array
{
$checks = [
'free_shipping' => new FreeShippingEligibleSpecification($cart->total),
'bulk_discount' => new BulkDiscountEligibleSpecification($cart->itemCount),
'loyalty_discount' => new LoyaltyDiscountEligibleSpecification($user),
'first_time_buyer' => new FirstTimeBuyerSpecification($user),
'seasonal_promo' => new SeasonalPromoEligibleSpecification(),
];
return collect($checks)->map(function($spec, $type) use ($cart) {
return [
'type' => $type,
'eligible' => $spec->isSatisfiedBy($cart),
'spec' => $spec
];
})->toArray();
}
}
👥 User Management & Access Control
User Permission System
Your Challenge:
"Our permission system is a nightmare. We have role checks scattered everywhere, and nobody understands who can access what."
The Specification Solution:
// Clear, testable access control
class DocumentAccessSpecification extends AbstractSpecification
{
public function __construct(private User $user) {}
public function isSatisfiedBy(mixed $candidate): bool
{
if (!$candidate instanceof Document) {
return false;
}
return $this->ownerAccess($candidate)
->or($this->adminAccess())
->or($this->teamAccess($candidate))
->or($this->publicAccess($candidate))
->isSatisfiedBy($candidate);
}
private function ownerAccess(Document $doc): AbstractSpecification
{
return new WhereSpecification('user_id', $this->user->id);
}
private function adminAccess(): AbstractSpecification
{
return $this->user->hasRole('admin')
? new AlwaysTrueSpecification()
: new AlwaysFalseSpecification();
}
private function teamAccess(Document $doc): AbstractSpecification
{
return new WhereHasSpecification('team.members',
new WhereSpecification('user_id', $this->user->id)
);
}
private function publicAccess(Document $doc): AbstractSpecification
{
return (new WhereSpecification('is_public', true))
->and(new WhereSpecification('published_at', '<=', now()));
}
}
// Use everywhere consistently
class DocumentPolicy
{
public function view(User $user, Document $document)
{
return (new DocumentAccessSpecification($user))->isSatisfiedBy($document);
}
}
class DocumentController extends Controller
{
public function index()
{
$accessSpec = new DocumentAccessSpecification(auth()->user());
return Document::whereSpecification($accessSpec)->paginate(10);
}
}
User Segmentation for Marketing
Your Challenge:
"We need to send targeted emails to different user segments, but the query logic is scattered across multiple marketing tools."
The Specification Solution:
class UserSegmentationSpecifications
{
public static function vipCustomers(): AbstractSpecification
{
return (new WhereSpecification('total_spent', '>=', 10000))
->and(new WhereSpecification('orders_count', '>=', 20))
->and(new WhereHasSpecification('reviews',
new WhereSpecification('rating', '>=', 4)
));
}
public static function atRiskCustomers(): AbstractSpecification
{
return (new WhereSpecification('last_purchase_at', '<=', now()->subMonths(6)))
->and(new WhereSpecification('total_spent', '>=', 500))
->and(new WhereSpecification('email_unsubscribed', false));
}
public static function newCustomers(): AbstractSpecification
{
return (new WhereSpecification('created_at', '>=', now()->subDays(30)))
->and(new WhereSpecification('orders_count', '<=', 3));
}
}
// Use in marketing campaigns
class EmailCampaignService
{
public function sendVipNewsletter()
{
User::whereSpecification(UserSegmentationSpecifications::vipCustomers())
->chunk(100, function ($users) {
foreach ($users as $user) {
Mail::to($user)->queue(new VipNewsletterMail());
}
});
}
}
📊 Analytics & Reporting
Dynamic Report Filters
Your Challenge:
"Our reporting dashboard has 20+ filter combinations, and the SQL queries are becoming unmaintainable monsters."
The Specification Solution:
class SalesReportSpecification extends AbstractSpecification
{
public function __construct(
private ?array $dateRange = null,
private ?array $salespeople = null,
private ?array $regions = null,
private ?float $minAmount = null,
private ?string $customerSegment = null
) {}
public static function fromRequest(ReportRequest $request): self
{
return new self(
dateRange: $request->date_range ? [
Carbon::parse($request->date_range[0]),
Carbon::parse($request->date_range[1])
] : null,
salespeople: $request->salespeople,
regions: $request->regions,
minAmount: $request->min_amount,
customerSegment: $request->customer_segment
);
}
public function toQuery(Builder $query): Builder
{
return $this->buildReportCriteria()->toQuery($query);
}
private function buildReportCriteria(): AbstractSpecification
{
$builder = Specification::create();
if ($this->dateRange) {
$builder->whereBetween('created_at', $this->dateRange[0], $this->dateRange[1]);
}
if ($this->salespeople) {
$builder->whereIn('salesperson_id', $this->salespeople);
}
if ($this->regions) {
$builder->whereHas('customer',
new WhereInSpecification('region', $this->regions)
);
}
if ($this->minAmount) {
$builder->where('total_amount', '>=', $this->minAmount * 100);
}
if ($this->customerSegment) {
$builder->raw($this->getCustomerSegmentSpec());
}
return $builder->build();
}
}
// Use across multiple report types
class ReportController extends Controller
{
public function sales(ReportRequest $request)
{
$spec = SalesReportSpecification::fromRequest($request);
$data = Sale::whereSpecification($spec)
->with(['customer', 'salesperson', 'products'])
->get();
return new SalesReportResource($data);
}
public function exportSales(ReportRequest $request)
{
$spec = SalesReportSpecification::fromRequest($request);
return Excel::download(
new SalesExport($spec),
'sales-report.xlsx'
);
}
}
📧 Content Management & Moderation
Content Publishing Workflow
Your Challenge:
"We have complex content approval rules based on author reputation, content type, and moderation policies. The logic is everywhere."
The Specification Solution:
class ContentPublishableSpecification extends AbstractSpecification
{
public function __construct(
private User $author,
private ?string $contentType = null
) {}
public function isSatisfiedBy(mixed $candidate): bool
{
if (!$candidate instanceof Content) {
return false;
}
return $this->authorEligible()
->and($this->contentMeetsStandards($candidate))
->and($this->noViolations($candidate))
->and($this->typeSpecificRules($candidate))
->isSatisfiedBy($candidate);
}
private function authorEligible(): AbstractSpecification
{
return (new WhereSpecification('reputation_score', '>=', 50))
->and(new WhereSpecification('account_status', 'active'))
->and(new WhereSpecification('violations_count', '<', 3));
}
private function contentMeetsStandards(Content $content): AbstractSpecification
{
return (new WhereSpecification('word_count', '>=', 100))
->and(new WhereSpecification('is_spam', false))
->and(new WhereSpecification('profanity_score', '<', 0.1));
}
private function typeSpecificRules(Content $content): AbstractSpecification
{
return match ($content->type) {
'video' => new VideoContentSpecification(),
'article' => new ArticleContentSpecification(),
'tutorial' => new TutorialContentSpecification(),
default => new AlwaysTrueSpecification()
};
}
}
// Use in multiple contexts
class ContentModerationService
{
public function autoApprove(Content $content): bool
{
$publishableSpec = new ContentPublishableSpecification($content->author);
return $publishableSpec->isSatisfiedBy($content);
}
}
class ContentController extends Controller
{
public function publish(Content $content)
{
$publishableSpec = new ContentPublishableSpecification(auth()->user());
if (!$publishableSpec->isSatisfiedBy($content)) {
return response()->json([
'error' => 'Content does not meet publishing requirements'
], 422);
}
$content->update(['status' => 'published']);
return response()->json(['message' => 'Content published']);
}
}
🚚 Logistics & Operations
Order Fulfillment Rules
Your Challenge:
"We have complex shipping rules, warehouse allocation logic, and fulfillment criteria scattered across our system."
The Specification Solution:
class OrderFulfillmentSpecifications
{
public static function readyToShip(): AbstractSpecification
{
return (new WhereSpecification('payment_status', 'paid'))
->and(new WhereSpecification('inventory_allocated', true))
->and(new WhereNullSpecification('shipped_at'))
->and(new WhereSpecification('fraud_check_passed', true));
}
public static function expressShippingEligible(): AbstractSpecification
{
return (new WhereSpecification('total_amount', '>=', 10000)) // $100+
->and(new WhereSpecification('weight', '<=', 5000)) // 5kg max
->and(new WhereSpecification('contains_hazardous', false))
->and(new WhereHasSpecification('shippingAddress',
new WhereInSpecification('country', ['US', 'CA'])
));
}
public static function requiresSpecialHandling(): AbstractSpecification
{
return (new WhereSpecification('is_fragile', true))
->or(new WhereSpecification('is_perishable', true))
->or(new WhereSpecification('value', '>=', 100000)) // $1000+
->or(new WhereHasSpecification('items',
new WhereSpecification('requires_signature', true)
));
}
}
// Use in fulfillment workflows
class FulfillmentService
{
public function processOrderQueue(): void
{
// Process ready-to-ship orders
Order::whereSpecification(
OrderFulfillmentSpecifications::readyToShip()
)->chunk(50, function ($orders) {
foreach ($orders as $order) {
$this->allocateInventory($order);
$this->generateShippingLabel($order);
}
});
// Flag special handling orders
Order::whereSpecification(
OrderFulfillmentSpecifications::requiresSpecialHandling()
)->update(['requires_manual_review' => true]);
}
public function suggestShippingOptions(Order $order): array
{
$options = ['standard'];
if (OrderFulfillmentSpecifications::expressShippingEligible()->isSatisfiedBy($order)) {
$options[] = 'express';
$options[] = 'overnight';
}
return $options;
}
}
💳 Financial & Payments
Risk Assessment & Fraud Detection
Your Challenge:
"Our fraud detection has dozens of rules, risk scoring algorithms, and payment validation logic that's becoming unmaintainable."
The Specification Solution:
class PaymentRiskAssessmentSpecification extends AbstractSpecification
{
public function __construct(private Payment $payment) {}
public function getRiskScore(): float
{
$riskFactors = [
'high_amount' => $this->highAmountRisk(),
'new_customer' => $this->newCustomerRisk(),
'suspicious_location' => $this->locationRisk(),
'velocity_check' => $this->velocityRisk(),
'payment_method' => $this->paymentMethodRisk()
];
return collect($riskFactors)->sum(fn($spec) =>
$spec->isSatisfiedBy($this->payment) ? $spec->getRiskWeight() : 0
);
}
public function requiresManualReview(): bool
{
return $this->getRiskScore() >= 0.7;
}
public function shouldBlock(): bool
{
return $this->getRiskScore() >= 0.9;
}
private function highAmountRisk(): RiskSpecification
{
return new RiskSpecification(
new WhereSpecification('amount', '>=', 100000), // $1000+
weight: 0.3
);
}
private function newCustomerRisk(): RiskSpecification
{
return new RiskSpecification(
new WhereHasSpecification('customer',
new WhereSpecification('created_at', '>=', now()->subDays(7))
),
weight: 0.4
);
}
private function velocityRisk(): RiskSpecification
{
$recentPayments = Payment::where('customer_id', $this->payment->customer_id)
->where('created_at', '>=', now()->subHour())
->count();
return new RiskSpecification(
$recentPayments > 5 ? new AlwaysTrueSpecification() : new AlwaysFalseSpecification(),
weight: 0.6
);
}
}
// Integrate with payment processing
class PaymentProcessor
{
public function processPayment(PaymentRequest $request): PaymentResult
{
$payment = Payment::create($request->toArray());
$riskAssessment = new PaymentRiskAssessmentSpecification($payment);
if ($riskAssessment->shouldBlock()) {
$payment->update(['status' => 'blocked']);
return PaymentResult::blocked('High risk transaction');
}
if ($riskAssessment->requiresManualReview()) {
$payment->update(['status' => 'pending_review']);
dispatch(new ReviewPaymentJob($payment));
return PaymentResult::pending('Manual review required');
}
return $this->authorizePayment($payment);
}
}
🎓 Use Case Selection Guide
How to Choose Your First Use Case
Start with Pain Points
- Look for methods with 10+ conditional statements
- Find logic that's duplicated across controllers
- Identify complex business rules that are hard to test
Pick High-Impact Areas
- User authentication and authorization
- Search and filtering functionality
- Business rule validation
- Reporting and analytics queries
Consider Team Benefits
- Areas where junior developers struggle
- Code that causes frequent bugs
- Logic that's hard to explain in code reviews
- Features that require frequent changes
Implementation Priority
Priority | Use Case Type | Why Start Here |
---|---|---|
🔥 High | Search/Filtering | Immediate user impact, clear boundaries |
🔥 High | User Permissions | Security critical, well-defined rules |
🟡 Medium | Business Validation | Complex logic, high test value |
🟡 Medium | Report Filtering | Query optimization benefits |
🟢 Low | Content Moderation | Complex workflows, gradual migration |
Ready to Implement?
Found your use case? Ready to see some mind-blowing advanced patterns?
- 🤯 Holy $#!% Moments - Advanced possibilities that will blow your mind
- 🚀 5-Minute Transformations - Step-by-step implementation guides
- 📚 Complete Guide - Master all features and patterns