Files
fotospiel-app/app/Models/TenantAnnouncement.php
Codex Agent 8f13465415
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled
Implement tenant announcements and audit log fixes
2026-01-02 14:19:46 +01:00

122 lines
3.4 KiB
PHP

<?php
namespace App\Models;
use App\Enums\TenantAnnouncementAudience;
use App\Enums\TenantAnnouncementSegment;
use App\Enums\TenantAnnouncementStatus;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Carbon;
/**
* @property array<int, string>|null $segments
*/
class TenantAnnouncement extends Model
{
/** @use HasFactory<\Database\Factories\TenantAnnouncementFactory> */
use HasFactory;
protected $fillable = [
'title',
'body',
'cta_label',
'cta_url',
'status',
'audience',
'segments',
'starts_at',
'ends_at',
'email_enabled',
'created_by',
'updated_by',
];
protected function casts(): array
{
return [
'status' => TenantAnnouncementStatus::class,
'audience' => TenantAnnouncementAudience::class,
'segments' => 'array',
'email_enabled' => 'boolean',
'starts_at' => 'datetime',
'ends_at' => 'datetime',
];
}
public function tenants(): BelongsToMany
{
return $this->belongsToMany(Tenant::class, 'tenant_announcement_targets')
->withTimestamps();
}
public function deliveries(): HasMany
{
return $this->hasMany(TenantAnnouncementDelivery::class);
}
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
public function updater(): BelongsTo
{
return $this->belongsTo(User::class, 'updated_by');
}
public function scopeActive(Builder $query): void
{
$query->where('status', TenantAnnouncementStatus::ACTIVE->value)
->where(function (Builder $builder) {
$builder->whereNull('starts_at')
->orWhere('starts_at', '<=', Carbon::now());
})
->where(function (Builder $builder) {
$builder->whereNull('ends_at')
->orWhere('ends_at', '>', Carbon::now());
});
}
/**
* @param array<int, TenantAnnouncementSegment|string> $segments
*/
public function matchesSegments(Tenant $tenant, array $segments): bool
{
if ($segments === []) {
return true;
}
foreach ($segments as $segment) {
$normalized = $segment instanceof TenantAnnouncementSegment
? $segment
: TenantAnnouncementSegment::tryFrom((string) $segment);
if (! $normalized) {
continue;
}
if (! $this->matchesSegment($tenant, $normalized)) {
return false;
}
}
return true;
}
private function matchesSegment(Tenant $tenant, TenantAnnouncementSegment $segment): bool
{
return match ($segment) {
TenantAnnouncementSegment::ACTIVE_PACKAGE => $tenant->activeResellerPackage()->exists(),
TenantAnnouncementSegment::ACTIVE_STATUS => (bool) $tenant->is_active
&& ! (bool) $tenant->is_suspended
&& $tenant->pending_deletion_at === null
&& $tenant->anonymized_at === null,
};
}
}