-
Notifications
You must be signed in to change notification settings - Fork 0
Models and Traits
Reference guide for models, traits, and interfaces in the Romega Software - Availability package.
The main trait to add availability support to your models.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use RomegaSoftware\Availability\Traits\HasAvailability;
class Resource extends Model
{
use HasAvailability;
}trait HasAvailability
{
// Polymorphic relationship to availability rules
public function availabilityRules(): MorphMany
// Get timezone for evaluation (override as needed)
public function getAvailabilityTimezone(): string
// Get default effect when no rules match (override as needed)
public function getAvailabilityDefaultEffect(): Effect
}class Resource extends Model
{
use HasAvailability;
// Custom timezone logic
public function getAvailabilityTimezone(): string
{
return $this->timezone
?? $this->location->timezone
?? config('app.timezone');
}
// Dynamic default effect
public function getAvailabilityDefaultEffect(): Effect
{
if ($this->is_premium) {
return Effect::Allow;
}
return Effect::Deny;
}
// Custom relationship query
public function availabilityRules()
{
return $this->morphMany(
config('availability.models.rule'),
'subject'
)->where('expires_at', '>', now())
->orWhereNull('expires_at');
}
}For complete control, implement the interface directly.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use RomegaSoftware\Availability\Contracts\AvailabilitySubject;
use RomegaSoftware\Availability\Support\Effect;
class CustomResource extends Model implements AvailabilitySubject
{
public function availabilityRules()
{
return $this->morphMany(
\RomegaSoftware\Availability\Models\AvailabilityRule::class,
'subject'
);
}
public function getAvailabilityTimezone(): string
{
return 'UTC';
}
public function getAvailabilityDefaultEffect(): Effect
{
return Effect::Allow;
}
}The model that stores availability rules.
namespace RomegaSoftware\Availability\Models;
class AvailabilityRule extends Model
{
protected $fillable = [
'type',
'config',
'effect',
'priority',
'enabled',
];
protected $casts = [
'config' => 'array',
'effect' => Effect::class,
'enabled' => 'boolean',
];
public function subject(): MorphTo
{
return $this->morphTo();
}
}namespace App\Models;
use RomegaSoftware\Availability\Models\AvailabilityRule as BaseRule;
class CustomAvailabilityRule extends BaseRule
{
// Add custom attributes
protected $fillable = [
'type',
'config',
'effect',
'priority',
'enabled',
'name', // Custom
'description', // Custom
'created_by', // Custom
'expires_at', // Custom
];
// Add custom casts
protected $casts = [
'config' => 'array',
'effect' => Effect::class,
'enabled' => 'boolean',
'expires_at' => 'datetime',
];
// Add custom scopes
public function scopeActive($query)
{
return $query->where('enabled', true)
->where(function ($q) {
$q->whereNull('expires_at')
->orWhere('expires_at', '>', now());
});
}
public function scopeByType($query, $type)
{
return $query->where('type', $type);
}
// Add custom methods
public function isExpired(): bool
{
return $this->expires_at && $this->expires_at->isPast();
}
public function getRemainingDays(): ?int
{
if (!$this->expires_at) {
return null;
}
return now()->diffInDays($this->expires_at, false);
}
// Add relationships
public function creator()
{
return $this->belongsTo(User::class, 'created_by');
}
}In config/availability.php:
'models' => [
'rule' => \App\Models\CustomAvailabilityRule::class,
],The Effect enum represents allow/deny states.
namespace RomegaSoftware\Availability\Support;
enum Effect: string
{
case Allow = 'allow';
case Deny = 'deny';
public function isAllow(): bool
{
return $this === self::Allow;
}
public function isDeny(): bool
{
return $this === self::Deny;
}
}use RomegaSoftware\Availability\Support\Effect;
// Creating rules
$rule = $resource->availabilityRules()->create([
'type' => 'weekdays',
'config' => ['days' => [1,2,3,4,5]],
'effect' => Effect::Allow,
'priority' => 10,
]);
// Checking effect
if ($rule->effect->isAllow()) {
// Handle allow
}
// Comparing
if ($rule->effect === Effect::Deny) {
// Handle deny
}
// In database queries
$allowRules = AvailabilityRule::where('effect', Effect::Allow)->get();// Different models with different availability behavior
class Product extends Model
{
use HasAvailability;
public function getAvailabilityDefaultEffect(): Effect
{
return $this->in_stock ? Effect::Allow : Effect::Deny;
}
}
class Service extends Model
{
use HasAvailability;
public function getAvailabilityDefaultEffect(): Effect
{
return Effect::Allow; // Services default to available
}
}
class Room extends Model
{
use HasAvailability;
public function getAvailabilityDefaultEffect(): Effect
{
return Effect::Deny; // Rooms must be explicitly made available
}
}class Hotel extends Model
{
use HasAvailability;
public function rooms()
{
return $this->hasMany(Room::class);
}
// Check if any room is available
public function hasAvailableRoom($moment)
{
$engine = app(AvailabilityEngine::class);
return $this->rooms->contains(function ($room) use ($engine, $moment) {
return $engine->isAvailable($room, $moment);
});
}
}
class Room extends Model
{
use HasAvailability;
public function hotel()
{
return $this->belongsTo(Hotel::class);
}
// Inherit hotel's timezone
public function getAvailabilityTimezone(): string
{
return $this->hotel->timezone ?? config('app.timezone');
}
}class Event extends Model
{
use HasAvailability;
public function venue()
{
return $this->belongsTo(Venue::class);
}
public function equipment()
{
return $this->belongsToMany(Equipment::class);
}
// Event is available only if venue and all equipment are available
public function isFullyAvailable($moment)
{
$engine = app(AvailabilityEngine::class);
// Check venue
if (!$engine->isAvailable($this->venue, $moment)) {
return false;
}
// Check all equipment
foreach ($this->equipment as $item) {
if (!$engine->isAvailable($item, $moment)) {
return false;
}
}
// Check event itself
return $engine->isAvailable($this, $moment);
}
}namespace App\Observers;
use App\Models\Resource;
use RomegaSoftware\Availability\Support\Effect;
class ResourceObserver
{
public function created(Resource $resource)
{
// Automatically create default rules
if ($resource->type === 'meeting_room') {
$resource->availabilityRules()->createMany([
[
'type' => 'weekdays',
'config' => ['days' => [1,2,3,4,5]],
'effect' => Effect::Allow,
'priority' => 10,
],
[
'type' => 'time_of_day',
'config' => ['from' => '08:00', 'to' => '18:00'],
'effect' => Effect::Allow,
'priority' => 20,
],
]);
}
}
public function deleting(Resource $resource)
{
// Clean up availability rules
$resource->availabilityRules()->delete();
}
}class AvailabilityRuleObserver
{
public function saved(AvailabilityRule $rule)
{
$this->invalidateCache($rule);
}
public function deleted(AvailabilityRule $rule)
{
$this->invalidateCache($rule);
}
private function invalidateCache(AvailabilityRule $rule)
{
Cache::tags([
'availability',
"subject-{$rule->subject_type}-{$rule->subject_id}",
])->flush();
}
}Schema::create('availability_rules', function (Blueprint $table) {
$table->id();
$table->morphs('subject'); // subject_type, subject_id
$table->string('type', 50);
$table->json('config')->nullable();
$table->string('effect', 10);
$table->integer('priority')->default(50);
$table->boolean('enabled')->default(true);
$table->timestamps();
$table->index(['subject_type', 'subject_id', 'enabled', 'priority']);
});Schema::create('availability_rules', function (Blueprint $table) {
$table->id();
$table->morphs('subject');
$table->string('type', 50);
$table->json('config')->nullable();
$table->string('effect', 10);
$table->integer('priority')->default(50);
$table->boolean('enabled')->default(true);
// Additional fields
$table->string('name')->nullable();
$table->text('description')->nullable();
$table->timestamp('starts_at')->nullable();
$table->timestamp('expires_at')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users');
$table->foreignId('updated_by')->nullable()->constrained('users');
$table->json('metadata')->nullable();
$table->timestamps();
$table->softDeletes();
// Indexes
$table->index(['subject_type', 'subject_id', 'enabled', 'priority']);
$table->index(['starts_at', 'expires_at']);
$table->index('type');
});- Configuration - Configure models and behaviors
- Testing - Testing models and traits
- Custom Evaluators - Extend with custom 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