File: /home/xedaptot/be.naniguide.com/app/Model/AdProductCatalog.php
<?php
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use App\Library\Traits\HasUid;
/**
* @property int $id
* @property string $uid
* @property int $customer_id
* @property int|null $ad_platform_id
* @property string $name
* @property string $source_type
* @property string|null $source_url
* @property string $sync_status
* @property \Illuminate\Support\Carbon|null $last_synced_at
* @property int $product_count
* @property string|null $platform_catalog_id
* @property string|null $last_idempotency_key
* @property array<int,array<string,string>>|null $error_log
*/
class AdProductCatalog extends Model
{
use HasFactory;
use HasUid;
const STATUS_ACTIVE = 'active'; // for HasUid trait compatibility
const SOURCE_MANUAL = 'manual';
const SOURCE_FEED_URL = 'feed_url';
const SOURCE_SHOPIFY = 'shopify';
const SOURCE_WOOCOMMERCE = 'woocommerce';
// R15.6 — CSV upload (admin uploads a file from disk OR points
// source_url at a CSV-serving HTTPS URL).
const SOURCE_CSV = 'csv';
const SOURCES = [
self::SOURCE_MANUAL,
self::SOURCE_FEED_URL,
self::SOURCE_SHOPIFY,
self::SOURCE_WOOCOMMERCE,
self::SOURCE_CSV,
];
const SYNC_PENDING = 'pending';
const SYNC_IN_PROGRESS = 'in_progress';
const SYNC_COMPLETED = 'completed';
const SYNC_FAILED = 'failed';
const SYNC_PARTIAL = 'partial';
protected $table = 'ad_product_catalogs';
protected $fillable = [
'uid',
'customer_id',
'ad_platform_id',
'name',
'source_type',
'source_url',
'source_credentials',
'sync_status',
'last_synced_at',
'last_source_pulled_at',
'product_count',
'platform_catalog_id',
'last_idempotency_key',
'error_log',
];
protected $casts = [
'last_synced_at' => 'datetime',
'last_source_pulled_at' => 'datetime',
'error_log' => 'json',
// R15.5 — encrypted JSON for WooCommerce / Shopify / feed-URL
// creds. Never plaintext at rest.
'source_credentials' => 'encrypted:json',
];
/**
* @return BelongsTo<Customer, $this>
*/
public function customer(): BelongsTo
{
return $this->belongsTo(Customer::class, 'customer_id');
}
/**
* @return BelongsTo<AdPlatform, $this>
*/
public function adPlatform(): BelongsTo
{
return $this->belongsTo(AdPlatform::class, 'ad_platform_id');
}
/**
* @return HasMany<AdProduct, $this>
*/
public function products(): HasMany
{
return $this->hasMany(AdProduct::class, 'ad_product_catalog_id');
}
public function scopeOfCustomer($query, $customerId)
{
return $query->where('customer_id', $customerId);
}
public function scopeSearch($query, $keyword)
{
return $keyword ? $query->where('name', 'like', '%' . $keyword . '%') : $query;
}
public function getSyncBadgeAttribute(): string
{
return match ($this->sync_status) {
self::SYNC_COMPLETED => 'green',
self::SYNC_IN_PROGRESS => 'blue',
self::SYNC_FAILED => 'red',
default => 'orange',
};
}
protected static function newFactory()
{
return \Database\Factories\AdProductCatalogFactory::new();
}
}