andydefer / laravel-roster
Clean and flexible scheduling for Laravel applications.
Requires
- php: ^8.2
- illuminate/database: ^12.0
- illuminate/support: ^12.0
- laravel/framework: ^12.0
Requires (Dev)
- barryvdh/laravel-ide-helper: ^3.6
- larastan/larastan: ^3.8
- laravel/pint: ^1.26
- orchestra/testbench: ^10.8
- phpunit/phpunit: ^12.5
- rector/rector: *
- symfony/var-dumper: ^7.0
- vimeo/psalm: ^6.14
This package is auto-updated.
Last update: 2026-04-20 22:50:28 UTC
README
Roster is a comprehensive Laravel package for advanced scheduling, availability, and booking management. Built with a robust architecture, it handles recurring availability, booked slots, and impediments with exhaustive business validation.
๐ฆ Installation
composer require andydefer/laravel-roster
Publish package resources:
php artisan roster:install
Or manually:
# Configuration php artisan vendor:publish --tag=roster-config # Migrations php artisan vendor:publish --tag=roster-migrations # Run migrations php artisan migrate
๐ Quick Start
1. Add the trait to your models
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Roster\Traits\HasRoster; class Doctor extends Model { use HasRoster; }
2. Create recurring availabilities
// Create an availability for a doctor $availability = availability_for($doctor)->create([ 'type' => 'consultation', 'daily_start' => '09:00:00', 'daily_end' => '17:00:00', 'days' => ['monday', 'wednesday', 'friday'], 'validity_start' => '2038-01-01', 'validity_end' => '2038-12-31', ]);
3. Schedule appointments
// Book a slot in this availability $schedule = schedule_for($availability)->create([ 'title' => 'Annual Checkup - Patient A', 'start_datetime' => '2038-01-04 10:00:00', 'end_datetime' => '2038-01-04 11:00:00', 'status' => \Roster\Enums\ScheduleStatus::BOOKED, 'metadata' => ['patient_id' => 123], ]);
4. Manage temporary unavailability
// Block a slot for training $impediment = impediment_for($availability)->create([ 'reason' => 'Mandatory medical training', 'start_datetime' => '2038-01-04 09:00:00', 'end_datetime' => '2038-01-04 12:00:00', ]);
5. Search for available slots
// Find the next available slot $nextSlot = schedule_for($availability)->findNextSlot( durationMinutes: 45, type: 'consultation', startFrom: now()->addDay() ); // Check availability for a specific slot $isAvailable = schedule_for($availability)->isTimeSlotAvailable( start: '2038-01-06 14:00:00', end: '2038-01-06 15:00:00', type: 'consultation' );
๐ก๏ธ Performance Protection: Absolute Minimum Duration
To prevent infinite loops and performance degradation, Roster enforces an absolute minimum duration of 10 minutes for ALL entity types (Availability, Schedule, Impediment).
Why 10 minutes?
When searching for available slots, the system generates time slots based on the duration. A duration that is too small would generate an enormous number of iterations:
| Duration | Generated Slots (1 year) | Performance Impact |
|---|---|---|
| 1 minute | ~525,600 slots | ๐ด Infinite loop risk - System overload, memory exhaustion |
| 5 minutes | ~105,120 slots | ๐ Very slow - Timeout possible, poor user experience |
| 10 minutes | ~52,560 slots | ๐ข Optimal - Fast and stable |
| 15 minutes | ~35,040 slots | ๐ข Excellent - Best performance |
| 30 minutes | ~17,520 slots | ๐ข Perfect - Maximum efficiency |
Technical Protection Implementation
The protection is enforced at the lowest level of the validation system:
// In AbstractRule.php - Protected against configuration errors private const ABSOLUTE_MIN_DURATION_MINUTES = 10; protected function getMinimumDuration(EntityType $entityType): int { $configuredMinutes = match ($entityType) { EntityType::AVAILABILITY => config('roster.durations.minimum_availability_minutes', 10), EntityType::SCHEDULE => config('roster.durations.minimum_schedule_minutes', 10), EntityType::IMPEDIMENT => config('roster.durations.minimum_impediment_minutes', 5), }; // FORCE absolute minimum - Configuration cannot go below 10 minutes if ($configuredMinutes < self::ABSOLUTE_MIN_DURATION_MINUTES) { $actualMinutes = $configuredMinutes; $configuredMinutes = self::ABSOLUTE_MIN_DURATION_MINUTES; // Automatic warning when configuration is overridden logger()->warning('Minimum duration configuration overridden for performance reasons', [ 'entity_type' => $entityType->value, 'configured_minutes' => $actualMinutes, 'enforced_minutes' => self::ABSOLUTE_MIN_DURATION_MINUTES, 'reason' => 'Durations below 10 minutes would generate too many iterations and slow down the system', ]); } return $configuredMinutes; }
What happens if you try to configure less than 10 minutes?
// In config/roster.php 'durations' => [ 'minimum_availability_minutes' => 5, // โ Will be forced to 10 'minimum_schedule_minutes' => 3, // โ Will be forced to 10 'minimum_impediment_minutes' => 1, // โ Will be forced to 10 ], // The system automatically: // 1. Detects the configuration below 10 minutes // 2. Logs a warning for debugging // 3. Enforces 10 minutes as the actual minimum // 4. Prevents infinite loops and performance issues
Validation in Action
// Attempt to create an availability with 5 minutes duration $context = $this->createMock(ValidationContextInterface::class); $context->method('getEntityType')->willReturn(EntityType::AVAILABILITY); $context->method('safeData')->willReturn([ 'start_time' => '09:00:00', 'end_time' => '09:05:00', // 5 minutes - BELOW absolute minimum ]); // This will FAIL with a clear error message: // "Minimum duration of 10 minutes required for availability. Got 5 minutes" // Attempt with 10 minutes $context->method('safeData')->willReturn([ 'start_time' => '09:00:00', 'end_time' => '09:10:00', // 10 minutes - MEETS absolute minimum ]); // This will PASS validation
๐ Polymorphic Scheduling Link System
Roster includes an advanced system that allows any Eloquent model to be associated with schedules with customizable metadata.
Attach resources to schedules
use Roster\Traits\AttachableToSchedules; // Add the trait to your models class Room extends Model { use AttachableToSchedules; } class Vehicle extends Model { use AttachableToSchedules; } class Equipment extends Model { use AttachableToSchedules; } // Usage: attach resources to a schedule $schedule = schedule_for($availability)->create([ 'title' => 'Scheduled Surgery', 'start_datetime' => '2038-01-04 08:00:00', 'end_datetime' => '2038-01-04 12:00:00', ]); // Attach resources with metadata $room = Room::find(1); $vehicle = Vehicle::find(1); $doctor = Doctor::find(1); $service = schedule_for($availability)->schedule($schedule); $service->attach($room, ['role' => 'operating_room', 'equipment' => 'surgical']); $service->attach($vehicle, ['role' => 'transport', 'urgent' => true]); $service->attach($doctor, ['role' => 'surgeon', 'specialty' => 'orthopedics']); // Attach multiple resources at once $service->attachMany([$room, $vehicle, $doctor], ['operation_id' => 'OP123']);
Manage attached resources
// Check if a resource is attached $service->hasAttached($room); // true // Retrieve all attached resources $attachedResources = $service->getAttached(); // Collection containing room, vehicle, doctor // Filter by model type $rooms = $service->getAttachedByType(Room::class); $doctors = $service->getAttachedByType(Doctor::class); // Detach resources $service->detach($vehicle); $service->detachMany([$room, $doctor]); // Synchronize resources completely $service->sync([$room, $doctor], ['session' => 'morning']); // Detach all resources $service->detachAll();
Direct usage from models
// From an attachable model $room->isAttachedToSchedule($schedule); // true/false $room->attachToSchedule($schedule, ['role' => 'consultation']); $room->detachFromSchedule($schedule); // Get all schedules with metadata $schedulesWithMetadata = $room->attachedSchedulesWithLinkMetadata(); // Filter by metadata $surgeries = $room->attachedSchedulesWithMetadata('role', 'operating_room'); // Synchronize schedules $room->syncSchedules([$schedule1, $schedule2], ['default_room' => true]);
Eloquent relationships
// The polymorphic relationship is automatically available $room->attachedSchedules; // Collection of schedules $schedule->linkables; // Collection of attached models (via pivot) // With link metadata $room->attachedSchedules()->withPivot('metadata')->get();
Advanced use cases
1. Operating room management
// Prepare surgery with all necessary resources $surgerySchedule = schedule_for($availability)->create([ 'title' => 'Knee Arthroscopy', 'start_datetime' => '2038-01-04 08:00:00', 'end_datetime' => '2038-01-04 10:00:00', ]); $service = schedule_for($availability)->schedule($surgerySchedule); $service->attach($operatingRoom, [ 'role' => 'operating_room', 'equipment' => ['arthroscope', 'monitor', 'instruments'], 'sterilization' => 'level_2' ]); $service->attach($surgeon, [ 'role' => 'primary_surgeon', 'specialty' => 'orthopedics', 'assistant_required' => true ]); $service->attach($anesthesiologist, [ 'role' => 'anesthesiologist', 'type_anesthesia' => 'general' ]); $service->attach($nurse, [ 'role' => 'instrument_nurse', 'experience' => 'senior' ]);
2. Shared resource booking
// Two different schedules sharing the same resources $schedule1 = schedule_for($availability)->create([...]); $schedule2 = schedule_for($availability)->create([...]); $sharedRoom = Room::find(1); $sharedEquipment = Equipment::find(1); $service1 = schedule_for($availability)->schedule($schedule1); $service2 = schedule_for($availability)->schedule($schedule2); $service1->attach($sharedRoom, ['usage' => 'consultation']); $service2->attach($sharedRoom, ['usage' => 'training']); $service1->attach($sharedEquipment, ['reserved' => true]); // The system tracks which resource is used where and when
3. Complex metadata for tracking
$service->attach($patient, [ 'medical_history' => ['hypertension', 'diabetes'], 'insurance' => 'ABC Insurance', 'priority' => 'high', 'contact' => [ 'phone' => '555-0123', 'email' => 'patient@example.com' ], 'notes' => ['allergic to penicillin', 'needs interpreter'] ]);
๐ Model Query Methods (HasRoster Trait)
The HasRoster trait includes methods to retrieve impediments and schedules of a model within a given period.
Added Methods
// 1. Get all items (impediments + schedules) in a period $items = $model->getRosterItemsInPeriod($start, $end); // Returns: ['impediments' => Collection, 'schedules' => Collection] // 2. Get only impediments in a period $impediments = $model->getImpedimentsInPeriod($start, $end); // 3. Get only schedules in a period $schedules = $model->getSchedulesInPeriod($start, $end); // 4. Check for conflicts $hasConflicts = $model->hasConflictsInPeriod($start, $end); // Returns true if at least one impediment or schedule exists
Simple Example
// A doctor with the HasRoster trait $doctor = Doctor::find(1); // Check availability for tomorrow 10am-11am $start = Carbon::parse('2024-06-10 10:00:00'); $end = Carbon::parse('2024-06-10 11:00:00'); // Check for conflicts if ($doctor->hasConflictsInPeriod($start, $end)) { // Get details $conflicts = $doctor->getRosterItemsInPeriod($start, $end); echo "Conflicting schedules: " . $conflicts['schedules']->count(); echo "Conflicting impediments: " . $conflicts['impediments']->count(); } else { echo "Time slot available"; }
Practical Use Case
// Before creating a new schedule public function createSchedule(Doctor $doctor, array $data) { $start = Carbon::parse($data['start_datetime']); $end = Carbon::parse($data['end_datetime']); // Check if the time slot is free if ($doctor->hasConflictsInPeriod($start, $end)) { return response()->json([ 'error' => 'Time slot not available', 'conflicts' => $doctor->getRosterItemsInPeriod($start, $end) ], 422); } // Create the schedule return schedule_for($doctor->availabilities()->first()) ->create($data); }
๐ Core Concepts
Immutability Principle
Roster prevents direct model mutations to ensure data integrity. All operations must go through appropriate services:
// โ FORBIDDEN: Direct modification $availability->update(['daily_end' => '18:00:00']); // Throws exception // โ ALLOWED: Via service availability_for($doctor)->update($availability->id, [ 'daily_end' => '18:00:00' ]);
Single-action context
Each service is designed for a single action with its own context:
// โ FORBIDDEN: Service reuse $service = availability_for($doctor); $service->create([...]); $service->update(1, [...]); // Corrupted context // โ ALLOWED: New context for each action availability_for($doctor)->create([...]); availability_for($doctor)->update(1, [...]);
The 3 main entities
- Availability: Defines when a resource is available (days, times, period)
- Schedule: Represents a booked slot in an availability
- Impediment: Temporarily blocks an availability
๐ก๏ธ Secure Architecture
Mutation access control
The system uses two contexts to control access:
// 1. Mutation context (internal) // Used by repositories to allow CRUD operations RosterMutationContext::allow(function () { return Availability::create([...]); // Allowed in this context }); // 2. Service context (public) // Used by helpers to allow service usage RosterServiceContext::allow(function () { return $service->create([...]); // Allowed via helper });
Secure helpers
The availability_for(), schedule_for(), and impediment_for() helpers automatically create the necessary context:
// These helpers automatically handle: // 1. Execution context creation // 2. Schedulable entity validation // 3. Reuse prevention
๐ Advanced Search and Data Consistency
first() method for targeted search
// Retrieve the first availability matching criteria $availability = availability_for($doctor) ->whereType('consultation') ->first(); // Retrieve the next upcoming appointment $nextAppointment = schedule_for($availability) ->setFilter('start_datetime', '>', now()) ->first(); // Retrieve the first scheduled impediment $firstImpediment = impediment_for($availability) ->setFilter('reason', 'like', '%training%') ->first();
Automatic days consistency
The system automatically ensures consistency between specified days and validity periods:
// During an update, days outside the period are automatically reconciled $availability = availability_for($doctor)->create([ 'validity_start' => '2024-01-01', 'validity_end' => '2024-01-07', // Week from January 1-7 'days' => ['monday', 'wednesday', 'friday'], ]); // If you extend the period, days are automatically adjusted availability_for($doctor)->update($availability->id, [ 'validity_end' => '2024-01-14', // Two weeks // Days remain consistent with the new period ]); // Reconciliation behavior configuration // In config/roster.php: 'reconciliation_warning' => env('ROSTER_RECONCILIATION_WARNING', false), // If true: PHP warning when days are outside the period // If false: silent reconciliation
Standardized days sorting
Utility functions always return days in standard week order (Monday โ Sunday):
$days = roster_days_in_period('2024-01-01', '2024-01-07'); // Returns: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'] // Automatically sorted in standard order
๐ฏ Exhaustive Business Validation
Roster includes 17 validation rules that guarantee system consistency:
Main rules:
- SchedulableValidationRule (110) - Checks for schedulable context presence
- RequiredFieldsRule (100) - Validates required fields per operation
- AvailabilityTemporalCoherenceRule (100) - Ensures temporal coherence
- TemporalConflictRule (80) - Prevents scheduling overlaps
- AvailabilityOverlapRule (80) - Prevents availability overlaps
- TimeRangeRule (85) - Validates time ranges (no multi-day spans)
- DurationRule (90) - Enforces minimum duration (with 10 minutes absolute minimum)
Rule visualization:
# List all available rules php artisan roster:debug-rules # See rules for a specific entity php artisan roster:debug-rules availability --operation=create
๐ Real-world Usage Examples
Medical clinic management
// Create availabilities for different specialists $cardiologist = Doctor::where('specialty', 'cardiology')->first(); $availability = availability_for($cardiologist)->create([ 'type' => 'consultation', 'daily_start' => '08:30:00', 'daily_end' => '12:30:00', 'days' => ['monday', 'wednesday', 'friday'], 'validity_start' => '2024-01-01', 'validity_end' => '2024-12-31', ]); // Patient booking $appointment = schedule_for($availability)->create([ 'title' => 'Cardiac Consultation', 'start_datetime' => '2024-06-10 10:00:00', 'end_datetime' => '2024-06-10 11:00:00', 'status' => ScheduleStatus::BOOKED, 'metadata' => [ 'patient_id' => 'CARD001', 'priority' => 'medium', 'tests_required' => ['echocardiogram', 'stress_test'] ], ]); // Quick search for next availability $nextAvailability = availability_for($cardiologist) ->setFilter('validity_start', '>', now()) ->first(); // Manage unavailability (training) impediment_for($availability)->create([ 'reason' => 'Continuing education', 'start_datetime' => '2024-06-15 09:00:00', 'end_datetime' => '2024-06-15 12:00:00', 'metadata' => ['mandatory' => true, 'location' => 'Auditorium'], ]);
Room booking system
// Two doctors sharing a room $room = Room::find(1); // First doctor uses the room on Monday $doctor1Availability = availability_for($doctor1)->create([ 'type' => 'room_a', 'daily_start' => '09:00:00', 'daily_end' => '17:00:00', 'days' => ['monday', 'wednesday', 'friday'], 'validity_start' => '2024-01-01', 'validity_end' => '2024-12-31', ]); // Second doctor uses the room on Tuesday $doctor2Availability = availability_for($doctor2)->create([ 'type' => 'room_a', 'daily_start' => '09:00:00', 'daily_end' => '17:00:00', 'days' => ['tuesday', 'thursday'], 'validity_start' => '2024-01-01', 'validity_end' => '2024-12-31', ]); // Search for first availability for urgent slot $urgentSlot = schedule_for($doctor1Availability) ->setFilter('status', ScheduleStatus::AVAILABLE) ->first(); // System automatically prevents conflicts schedule_for($doctor1Availability)->create([ 'title' => 'Room A usage - Dr. Smith', 'start_datetime' => '2024-06-10 10:00:00', // Monday 'end_datetime' => '2024-06-10 12:00:00', ]); // โ This booking will fail (inter-doctor conflict) schedule_for($doctor2Availability)->create([ 'title' => 'Room A usage - Dr. Jones', 'start_datetime' => '2024-06-10 11:00:00', // Same day as Dr. Smith 'end_datetime' => '2024-06-10 13:00:00', ]);
Recurrent impediment management
// Create weekly availability $weeklyAvailability = availability_for($doctor)->create([ 'type' => 'consultation', 'daily_start' => '08:00:00', 'daily_end' => '18:00:00', 'days' => ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], 'validity_start' => '2024-01-01', 'validity_end' => '2024-12-31', ]); // Recurrent impediments (lunch break) $weekdays = ['2024-01-08', '2024-01-09', '2024-01-10', '2024-01-11', '2024-01-12']; foreach ($weekdays as $weekday) { impediment_for($weeklyAvailability)->create([ 'reason' => 'Lunch break', 'start_datetime' => Carbon::parse($weekday)->setTime(12, 0, 0), 'end_datetime' => Carbon::parse($weekday)->setTime(13, 0, 0), 'metadata' => ['type' => 'lunch', 'recurring' => true], ]); } // Find first available slot after impediments $firstAvailableSlot = schedule_for($weeklyAvailability) ->setFilter('start_datetime', '>', now()) ->first(); // Find available slots despite impediments $availableSlots = schedule_for($weeklyAvailability)->findAvailableSlots( startDate: '2024-01-08', endDate: '2024-01-12', durationMinutes: 60, type: 'consultation' );
๐ง Complete API
Availability Service
// CRUD availability_for($schedulable)->create($data); availability_for($schedulable)->find($id); availability_for($schedulable)->update($id, $data); availability_for($schedulable)->delete($id); // Search availability_for($schedulable)->all(); availability_for($schedulable)->setFilter('type', 'consultation')->all(); availability_for($schedulable)->first(); // New method // Checks availability_for($schedulable)->isAvailableOnDate($date, $type); availability_for($schedulable)->getAvailabilityForTimeSlot($start, $end, $type);
Schedule Service
// Booking schedule_for($availability)->create($data); schedule_for($availability)->update($id, $data); schedule_for($availability)->delete($id); // Slot search schedule_for($availability)->findNextSlot($durationMinutes, $type, $startFrom); schedule_for($availability)->findAvailableSlots($startDate, $endDate, $durationMinutes, $type); schedule_for($availability)->first(); // New method // Checks schedule_for($availability)->isTimeSlotAvailable($start, $end, $type); schedule_for($availability)->isPeriodAvailable($start, $end, $type); // Polymorphic link management schedule_for($availability)->schedule($scheduleModel); // Set context schedule_for($availability)->schedule($scheduleModel)->attach($model, $metadata); schedule_for($availability)->schedule($scheduleModel)->detach($model); schedule_for($availability)->schedule($scheduleModel)->getAttached(); schedule_for($availability)->schedule($scheduleModel)->sync($models, $metadata);
Impediment Service
// Impediment management impediment_for($availability)->create($data); impediment_for($availability)->update($id, $data); impediment_for($availability)->delete($id); // Search impediment_for($availability)->first(); // New method // Checks impediment_for($availability)->isTimeSlotBlocked($start, $end); impediment_for($availability)->getAvailableTimeSlots($start, $end, $type);
โ๏ธ Configuration
Configuration file (config/roster.php)
return [ // Allowed activity types 'allowed_types' => [ 'consultation', 'surgery', 'emergency', 'training', 'room_a', 'echography', 'scan', ], // Minimum durations (in minutes) // IMPORTANT: The system enforces an absolute minimum of 10 minutes // for ALL entity types to prevent infinite loops and performance issues. // Any value below 10 will be automatically forced to 10. 'durations' => [ 'minimum_availability_minutes' => 15, // Will be enforced to >= 10 'minimum_schedule_minutes' => 15, // Will be enforced to >= 10 'minimum_impediment_minutes' => 5, // Will be enforced to >= 10 'max_search_period_days' => 365, 'max_availability_days' => 365, ], // Validation rule cache 'cache' => [ 'enabled' => env('ROSTER_CACHE_ENABLED', true), 'cache_file' => storage_path('framework/cache/roster_rules.php'), 'cache_max_age_hours' => 24, ], // Days reconciliation 'reconciliation_warning' => env('ROSTER_RECONCILIATION_WARNING', false), // Controls behavior during updates when days are // outside the validity period: // - true: triggers a PHP warning (E_USER_WARNING) // - false: silent reconciliation ];
Environment variables
ROSTER_TIMEZONE=Europe/Paris ROSTER_CACHE_ENABLED=true ROSTER_RECONCILIATION_WARNING=false
๐งช Comprehensive Tests
The package includes 2300 tests covering all scenarios:
# Run all tests php artisan test # Integration tests php artisan test --group=integration # Performance tests php artisan test --filter=test_performance_and_load_scenario # Complex scenario tests php artisan test --filter=test_real_world_complex_scenario
Tested scenarios:
- โ Full availability lifecycle
- โ Impediment management with conflicts
- โ Intelligent booking system
- โ Complex interactions (availabilities + impediments + schedules)
- โ Multi-user conflicts with shared resources
- โ Error handling and edge cases
- โ Performance testing with massive data
- โ Recovery after errors
- โ Realistic complex scenario (hospital with multiple specialists)
- โ Data consistency with automatic reconciliation
- โ
first()method for targeted search - โ Polymorphic link system with metadata
- โ Attached resource management (rooms, vehicles, equipment)
- โ Synchronization and detachment tests
- โ Minimum duration enforcement (10 minutes absolute minimum)
- โ Protection against infinite loops in slot generation
๐จ Error Handling
use Roster\Validation\Exceptions\ValidationFailedException; try { $schedule = schedule_for($availability)->create($data); } catch (ValidationFailedException $e) { // Get detailed violations with rule information $violations = $e->getViolations(); // Array of ViolationData objects containing: // - field name // - error message // - rule that triggered the violation // - rule description for context $detailedReport = $e->toDetailedArray(); // Includes rule descriptions for better debugging return response()->json([ 'error' => 'validation_failed', 'message' => $e->getFormattedMessage(), 'violations' => $detailedReport['violations'], ], 422); }
Duration validation error example
try { schedule_for($availability)->create([ 'start_datetime' => '2024-06-10 09:00:00', 'end_datetime' => '2024-06-10 09:05:00', // 5 minutes ]); } catch (ValidationFailedException $e) { // Error message: // "Minimum duration of 10 minutes required for Schedule. Got 5 minutes" // The system automatically prevents durations below 10 minutes // to protect against infinite loops in slot generation }
Reconciliation warning handling
// Configuration to enable warnings config()->set('roster.reconciliation_warning', true); // Capture warnings set_error_handler(function ($errno, $errstr) { if ($errno === E_USER_WARNING && str_contains($errstr, 'outside the validity period')) { // Log or handle the warning Log::warning('Days reconciliation detected', ['message' => $errstr]); return true; // Prevents propagation } return false; }); // During an update with days outside the period: availability_for($doctor)->update($availability->id, [ 'validity_end' => '2024-01-10', 'days' => ['monday', 'saturday'], // 'saturday' will be filtered with warning ]); restore_error_handler();
๐ Development Tools
Validation rule debugging
# Display all rules php artisan roster:debug-rules # Filter by entity php artisan roster:debug-rules availability # Filter by operation php artisan roster:debug-rules availability --operation=create # Display methods php artisan roster:debug-rules availability --show-methods # Display sources php artisan roster:debug-rules availability --show-source
Cache management
# Generate rule cache php artisan roster:cache-rules # Display cache statistics php artisan roster:cache-rules --show # Clear cache php artisan roster:cache-rules --clear # Force regeneration php artisan roster:cache-rules --force
๐ค Contribution
- Fork the repository
- Create a branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Run tests
# All tests composer test # With code coverage composer test-coverage # Check code style composer lint
๐ License
This package is open-source and available under the MIT license.
๐ Useful Links
Roster - A professional solution for advanced scheduling management, designed for critical applications where every minute counts. โ๏ธโฐโจ
With advanced search features, data consistency, exhaustive business validation, automatic protection against infinite loops (10 minutes absolute minimum duration), and a comprehensive polymorphic link system, Roster ensures the integrity of your scheduling systems in the most demanding environments.