File: /home/xedaptot/be.naniguide.com/app/Model/AdAudience.php
<?php
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Library\Traits\HasUid;
class AdAudience extends Model
{
use HasFactory;
use HasUid;
use SoftDeletes;
// Types
const TYPE_CUSTOM = 'custom';
const TYPE_LOOKALIKE = 'lookalike';
const TYPE_SAVED = 'saved';
const TYPES = [
self::TYPE_CUSTOM,
self::TYPE_LOOKALIKE,
self::TYPE_SAVED,
];
const TYPE_LABELS = [
self::TYPE_CUSTOM => 'Custom Audience',
self::TYPE_LOOKALIKE => 'Lookalike Audience',
self::TYPE_SAVED => 'Saved Audience',
];
// Source types
const SOURCE_MAIL_LIST = 'mail_list';
const SOURCE_SEGMENT = 'segment';
const SOURCE_MANUAL = 'manual';
// Statuses
const STATUS_DRAFT = 'draft';
const STATUS_SYNCING = 'syncing';
const STATUS_SYNCED = 'synced';
const STATUS_ERROR = 'error';
const STATUS_STALE = 'stale';
const STATUS_ACTIVE = 'synced'; // alias for HasUid trait compatibility
const STATUSES = [
self::STATUS_DRAFT,
self::STATUS_SYNCING,
self::STATUS_SYNCED,
self::STATUS_ERROR,
self::STATUS_STALE,
];
const STATUS_BADGE_MAP = [
self::STATUS_DRAFT => 'orange',
self::STATUS_SYNCING => 'blue',
self::STATUS_SYNCED => 'green',
self::STATUS_ERROR => 'red',
self::STATUS_STALE => 'orange',
];
// Sync statuses
const SYNC_PENDING = 'pending';
const SYNC_IN_PROGRESS = 'in_progress';
const SYNC_COMPLETED = 'completed';
const SYNC_FAILED = 'failed';
protected $table = 'ad_audiences';
protected $fillable = [
'uid',
'customer_id',
'name',
'type',
'source_type',
'source_id',
'platform',
'platform_audience_id',
'size_estimate',
'status',
'sync_status',
'last_synced_at',
'settings',
'sync_error',
];
protected $casts = [
'settings' => 'json',
'last_synced_at' => 'datetime',
'size_estimate' => 'integer',
];
// ── Relations ──────────────────────────────────────────────────────────
public function customer()
{
return $this->belongsTo(Customer::class, 'customer_id');
}
public function mailList()
{
return $this->belongsTo(MailList::class, 'source_id');
}
public function activityLogs()
{
return $this->morphMany(AdActivityLog::class, 'loggable');
}
// ── Scopes ─────────────────────────────────────────────────────────────
public function scopeOfCustomer($query, $customerId)
{
return $query->where('customer_id', $customerId);
}
public function scopeSearch($query, $keyword)
{
if (!$keyword) {
return $query;
}
return $query->where('name', 'like', '%' . $keyword . '%');
}
public function scopeOfType($query, $type)
{
if (!$type) {
return $query;
}
return $query->where('type', $type);
}
public function scopeOfPlatform($query, $platform)
{
if (!$platform) {
return $query;
}
return $query->where('platform', $platform);
}
public function scopeSynced($query)
{
return $query->where('status', self::STATUS_SYNCED);
}
// ── Accessors ──────────────────────────────────────────────────────────
public function getTypeLabelAttribute(): string
{
return self::TYPE_LABELS[$this->type] ?? ucfirst($this->type);
}
public function getStatusBadgeAttribute(): string
{
return self::STATUS_BADGE_MAP[$this->status] ?? 'blue';
}
public function getSizeDisplayAttribute(): string
{
if (!$this->size_estimate) {
return '-';
}
if ($this->size_estimate >= 1000000) {
return round($this->size_estimate / 1000000, 1) . 'M';
}
if ($this->size_estimate >= 1000) {
return round($this->size_estimate / 1000, 1) . 'K';
}
return number_format($this->size_estimate);
}
public function getSourceNameAttribute(): string
{
if ($this->source_type === self::SOURCE_MAIL_LIST && $this->source_id) {
$list = MailList::find($this->source_id);
return $list ? $list->name : 'Deleted List';
}
return ucfirst($this->source_type ?? 'manual');
}
// ── Helpers ─────────────────────────────────────────────────────────────
public function isSynced(): bool
{
return $this->status === self::STATUS_SYNCED;
}
public function isSyncing(): bool
{
return $this->status === self::STATUS_SYNCING;
}
public function hasError(): bool
{
return $this->status === self::STATUS_ERROR;
}
// ── Factory ─────────────────────────────────────────────────────────────
protected static function newFactory()
{
return \Database\Factories\AdAudienceFactory::new();
}
}