Skip to content

Priority System

Braden Keith edited this page Sep 20, 2025 · 1 revision

Priority System

Understanding and managing rule priorities effectively.

How Priority Works

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.

Priority Strategy

Layered Approach

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)

Example Implementation

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,
]);

Common Patterns

Basic Business Hours

// 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,
]);

Seasonal with Overrides

// 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,
]);

Priority Best Practices

1. Leave Gaps

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

2. Document Priority Ranges

/**
 * 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)
 */

3. Use Constants

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;
}

Debugging Priority Issues

View Evaluation Order

$rules = $resource->availabilityRules()
    ->where('enabled', true)
    ->orderBy('priority')
    ->get();

foreach ($rules as $rule) {
    echo "Priority {$rule->priority}: {$rule->type}{$rule->effect}\n";
}

Trace Evaluation

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";
}

Complex Priority Scenarios

Multiple Locations with Different Rules

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', ...],
                ];
        }
    }
}

Dynamic Priority Assignment

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;
    }
}

Conditional 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,
]);

Priority Anti-Patterns

❌ Don't: Random Priorities

// Bad: No clear strategy
$resource->availabilityRules()->create(['priority' => 37, ...]);
$resource->availabilityRules()->create(['priority' => 82, ...]);
$resource->availabilityRules()->create(['priority' => 13, ...]);

❌ Don't: Same Priority for Different Effects

// Confusing: Unpredictable order
$resource->availabilityRules()->createMany([
    ['priority' => 50, 'effect' => Effect::Allow, ...],
    ['priority' => 50, 'effect' => Effect::Deny, ...],
]);

✅ Do: Clear Priority Groups

// Good: Organized and predictable
const PRIORITY_GROUPS = [
    'base_schedule' => 10,
    'refined_schedule' => 20,
    'special_events' => 40,
    'manual_overrides' => 60,
    'system_blackouts' => 80,
];

Testing Priority Logic

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));
}

Next Steps

Getting Started

Installation
Set up the package in your Laravel app

Quick Start
Get running in 5 minutes

Basic Usage
Common patterns and examples


Core Concepts

How It Works
Understanding the evaluation engine

Rule Types
Available rule types and configurations

Priority System
How rule priority affects evaluation


Advanced Topics

Inventory Gates
Dynamic availability based on stock

Custom Evaluators
Build your own rule types

Complex Scenarios
Real-world implementation patterns

Performance Tips
Optimization strategies


API Reference

Configuration
Package configuration options

Models & Traits
Available models and traits

Testing
Testing your availability rules

Clone this wiki locally