-
Notifications
You must be signed in to change notification settings - Fork 0
Priority System
Understanding and managing rule priorities effectively.
Rules are evaluated in ascending priority order (lowest number first, highest number last).
Priority 10 → Priority 20 → Priority 30 → Priority 40
(First) (Last)
The last matching rule determines the final availability state.
Think of priorities as layers, each building upon or overriding the previous:
// Layer 1: Base availability (Priority 0-19)
// Layer 2: Regular schedule (Priority 20-39)
// Layer 3: Special rules (Priority 40-59)
// Layer 4: Overrides (Priority 60-79)
// Layer 5: Emergency/Blackouts (Priority 80-100)class AvailabilityPriority
{
const BASE = 10;
const SCHEDULE = 20;
const SPECIAL = 40;
const OVERRIDE = 60;
const EMERGENCY = 80;
}
// Usage
$resource->availabilityRules()->create([
'type' => 'weekdays',
'config' => ['days' => [1,2,3,4,5]],
'effect' => Effect::Allow,
'priority' => AvailabilityPriority::BASE,
]);// Priority 10: Days of operation
$business->availabilityRules()->create([
'type' => 'weekdays',
'config' => ['days' => [1,2,3,4,5]],
'effect' => Effect::Allow,
'priority' => 10,
]);
// Priority 20: Hours of operation
$business->availabilityRules()->create([
'type' => 'time_of_day',
'config' => ['from' => '09:00', 'to' => '17:00'],
'effect' => Effect::Allow,
'priority' => 20,
]);
// Priority 30: Lunch break
$business->availabilityRules()->create([
'type' => 'time_of_day',
'config' => ['from' => '12:00', 'to' => '13:00'],
'effect' => Effect::Deny,
'priority' => 30,
]);// Priority 10: Default season
$resort->availabilityRules()->create([
'type' => 'months_of_year',
'config' => ['months' => [5,6,7,8,9]],
'effect' => Effect::Allow,
'priority' => 10,
]);
// Priority 50: Special events (overrides season)
$resort->availabilityRules()->create([
'type' => 'date_range',
'config' => [
'from' => '2025-12-20',
'to' => '2026-01-05',
'kind' => 'absolute',
],
'effect' => Effect::Allow,
'priority' => 50,
]);
// Priority 90: Maintenance blackouts (overrides everything)
$resort->availabilityRules()->create([
'type' => 'blackout_date',
'config' => ['dates' => ['2025-04-01', '2025-10-01']],
'effect' => Effect::Deny,
'priority' => 90,
]);Don't use consecutive numbers. Leave room for future rules:
// Good: Gaps allow insertion
10, 20, 30, 50, 80
// Avoid: No room to insert
10, 11, 12, 13, 14/**
* Priority ranges for availability rules:
* 0-19: Base availability (seasons, general hours)
* 20-39: Schedule refinements (specific times, days)
* 40-59: Business rules (capacity, inventory)
* 60-79: Manual overrides (special events, promotions)
* 80-100: System overrides (maintenance, emergencies)
*/class RulePriorities
{
// Base rules
const SEASON = 10;
const WEEKDAY = 15;
// Schedule
const BUSINESS_HOURS = 20;
const BREAK_TIME = 25;
// Special
const HOLIDAY_HOURS = 40;
const EVENT_OVERRIDE = 45;
// Blackouts
const MAINTENANCE = 80;
const EMERGENCY = 90;
const SYSTEM_BLACKOUT = 100;
}$rules = $resource->availabilityRules()
->where('enabled', true)
->orderBy('priority')
->get();
foreach ($rules as $rule) {
echo "Priority {$rule->priority}: {$rule->type} → {$rule->effect}\n";
}function traceAvailability($resource, $moment)
{
$engine = app(AvailabilityEngine::class);
$state = $resource->availability_default ?? Effect::Allow;
echo "Starting state: {$state}\n";
$rules = $resource->availabilityRules()
->where('enabled', true)
->orderBy('priority')
->get();
foreach ($rules as $rule) {
// You'd need to check if rule matches here
echo "Priority {$rule->priority} ({$rule->type}): ";
// if (matches) ...
echo "→ {$rule->effect}\n";
}
$final = $engine->isAvailable($resource, $moment);
echo "Final state: " . ($final ? 'Available' : 'Unavailable') . "\n";
}class LocationRules
{
public static function applyRules($location, $baseP = 10)
{
switch($location->type) {
case 'office':
return [
['priority' => $baseP, 'type' => 'weekdays', ...],
['priority' => $baseP + 10, 'type' => 'time_of_day', ...],
];
case 'warehouse':
return [
['priority' => $baseP, 'type' => 'weekdays', ...],
['priority' => $baseP + 10, 'type' => 'time_of_day', ...],
['priority' => $baseP + 20, 'type' => 'inventory_gate', ...],
];
}
}
}class DynamicPriorityService
{
public function assignPriority($ruleType, $isOverride = false)
{
$basePriorities = [
'months_of_year' => 10,
'weekdays' => 20,
'time_of_day' => 30,
'date_range' => 40,
'blackout_date' => 50,
'inventory_gate' => 60,
];
$priority = $basePriorities[$ruleType] ?? 70;
// Overrides get +40 priority bump
if ($isOverride) {
$priority += 40;
}
return $priority;
}
}// Regular hours (lower priority)
$store->availabilityRules()->create([
'type' => 'time_of_day',
'config' => ['from' => '09:00', 'to' => '21:00'],
'effect' => Effect::Allow,
'priority' => 20,
]);
// Holiday hours (higher priority during holiday season)
$holidayPriority = Carbon::now()->month === 12 ? 60 : 25;
$store->availabilityRules()->create([
'type' => 'time_of_day',
'config' => ['from' => '08:00', 'to' => '23:00'],
'effect' => Effect::Allow,
'priority' => $holidayPriority,
]);// Bad: No clear strategy
$resource->availabilityRules()->create(['priority' => 37, ...]);
$resource->availabilityRules()->create(['priority' => 82, ...]);
$resource->availabilityRules()->create(['priority' => 13, ...]);// Confusing: Unpredictable order
$resource->availabilityRules()->createMany([
['priority' => 50, 'effect' => Effect::Allow, ...],
['priority' => 50, 'effect' => Effect::Deny, ...],
]);// Good: Organized and predictable
const PRIORITY_GROUPS = [
'base_schedule' => 10,
'refined_schedule' => 20,
'special_events' => 40,
'manual_overrides' => 60,
'system_blackouts' => 80,
];public function test_blackout_overrides_regular_schedule()
{
$resource = Resource::factory()->create([
'availability_default' => Effect::Deny,
]);
// Regular schedule
$resource->availabilityRules()->create([
'type' => 'weekdays',
'config' => ['days' => [1,2,3,4,5]],
'effect' => Effect::Allow,
'priority' => 10,
]);
// Blackout with higher priority
$resource->availabilityRules()->create([
'type' => 'blackout_date',
'config' => ['dates' => ['2025-01-15']],
'effect' => Effect::Deny,
'priority' => 50,
]);
$engine = app(AvailabilityEngine::class);
$wednesday = Carbon::parse('2025-01-15 10:00:00'); // Wednesday
// Should be denied despite being a weekday
$this->assertFalse($engine->isAvailable($resource, $wednesday));
}- Complex Scenarios - Real-world priority implementations
- Custom Evaluators - Building custom rule types
- Testing - Testing priority-based logic
Romega Software is software development agency specializing in helping customers integrate AI and custom software into their business, helping companies in growth mode better acquire, visualize, and utilize their data, and helping entrepreneurs bring their ideas to life.
Installation
Set up the package in your Laravel app
Quick Start
Get running in 5 minutes
Basic Usage
Common patterns and examples
How It Works
Understanding the evaluation engine
Rule Types
Available rule types and configurations
Priority System
How rule priority affects evaluation
Inventory Gates
Dynamic availability based on stock
Custom Evaluators
Build your own rule types
Complex Scenarios
Real-world implementation patterns
Performance Tips
Optimization strategies
Configuration
Package configuration options
Models & Traits
Available models and traits
Testing
Testing your availability rules