<?php
 namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Cache; class BlockedDomainPattern extends Model { use HasFactory; public const CACHE_KEY = 'blocked_domain_patterns:active'; public const CACHE_TTL = 300; public const TYPE_EXACT = 'exact'; public const TYPE_PREFIX = 'prefix'; public const TYPE_SUFFIX = 'suffix'; public const TYPE_CONTAINS = 'contains'; public const TYPE_REGEX = 'regex'; public const ALL_TYPES = [self::TYPE_EXACT, self::TYPE_PREFIX, self::TYPE_SUFFIX, self::TYPE_CONTAINS, self::TYPE_REGEX]; protected $fillable = [ 'pattern', 'match_type', 'reason', 'is_active', 'created_by', ]; protected $casts = [ 'is_active' => 'boolean', ]; public function scopeActive($query) { return $query->where('is_active', true); } public function matches(string $domain): bool { $domain = mb_strtolower($domain); $pattern = mb_strtolower($this->pattern); return match ($this->match_type) { self::TYPE_EXACT => $domain === $pattern, self::TYPE_PREFIX => str_starts_with($domain, $pattern), self::TYPE_SUFFIX => str_ends_with($domain, $pattern), self::TYPE_CONTAINS => str_contains($domain, $pattern), self::TYPE_REGEX => $this->regexMatches($domain, $pattern), default => false, }; } private function regexMatches(string $domain, string $pattern): bool { if (! preg_match('/^[\/#~%].*[\/#~%][a-z]*$/i', $pattern)) { $pattern = '/'.str_replace('/', '\/', $pattern).'/i'; } $result = @preg_match($pattern, $domain); return $result === 1; } protected static function booted(): void { static::saved(fn () => Cache::forget(self::CACHE_KEY)); static::deleted(fn () => Cache::forget(self::CACHE_KEY)); } } 