PNG IHDR x sBIT|d pHYs + tEXtSoftware www.inkscape.org< ,tEXtComment
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
class Coin extends Model
{
use HasFactory;
protected $fillable = [
'symbol',
'name',
'network',
'display_symbol',
'price_symbol',
'image',
'decimals',
'default_fee',
'default_swap_fee',
'address_validation_regex',
'sort_order',
'is_active',
'supported_exchanges',
'manual_price_usd',
'use_manual_price',
'last_price_check',
'price_check_status'
];
protected $casts = [
'default_fee' => 'decimal:8',
'default_swap_fee' => 'decimal:8',
'manual_price_usd' => 'decimal:8',
'is_active' => 'boolean',
'use_manual_price' => 'boolean',
'supported_exchanges' => 'array',
'last_price_check' => 'datetime',
'decimals' => 'integer',
'sort_order' => 'integer',
];
protected $appends = [
'image_url',
'display_name',
'display_symbol'
];
/**
* Relationships
*/
public function userBalances()
{
return $this->hasMany(UserCoinBalance::class);
}
/**
* Accessors
*/
public function getDisplayNameAttribute(): string
{
return $this->network ? "{$this->name} ({$this->network})" : $this->name;
}
/**
* Get the full URL for the coin image
*/
public function getImageUrlAttribute(): string
{
if (!$this->image) {
return asset('images/coins/default.png'); // Default fallback image
}
// If it's already a full URL (starts with http/https)
if (str_starts_with($this->image, 'http')) {
return $this->image;
}
// If it's a file path, make it a full URL
return asset('storage/coins/' . $this->image);
}
/**
* Get display symbol for UI
*/
public function getDisplaySymbolAttribute($value): string
{
// If display_symbol is set, use it; otherwise generate from symbol and network
if ($value) {
return $value;
}
return $this->network ? "{$this->symbol}-{$this->network}" : $this->symbol;
}
/**
* Methods
*/
public function validateAddress(string $address): bool
{
if (!$this->address_validation_regex) {
return true; // If no regex is set, consider all addresses valid
}
return (bool) preg_match('/' . $this->address_validation_regex . '/', $address);
}
/**
* Check if coin is supported by any exchange
*/
public function isSupportedByExchanges(): bool
{
return !empty($this->supported_exchanges) || $this->use_manual_price;
}
/**
* Get supported exchange names
*/
public function getSupportedExchangeNames(): array
{
return $this->supported_exchanges ? array_keys($this->supported_exchanges) : [];
}
/**
* Check if price data is available
*/
public function hasPriceData(): bool
{
return in_array($this->price_check_status, ['supported', 'manual']);
}
/**
* Get effective price (manual override or from exchanges)
*/
public function getEffectivePrice(): float
{
if ($this->use_manual_price && $this->manual_price_usd) {
return (float) $this->manual_price_usd;
}
// Get from CryptoService
$cryptoService = app(\App\Services\CryptoService::class);
$prices = $cryptoService->getPrices();
return $prices[$this->price_symbol]['usd'] ?? 0;
}
/**
* Check if the image is a URL or file path
*/
public function isImageUrl(): bool
{
return $this->image && str_starts_with($this->image, 'http');
}
/**
* Delete the uploaded image file if it exists
*/
public function deleteImage(): void
{
if ($this->image && !$this->isImageUrl()) {
$path = 'public/coins/' . $this->image;
if (Storage::exists($path)) {
Storage::delete($path);
}
}
}
/**
* Scopes
*/
public function scopeActive($query)
{
return $query->where('is_active', true);
}
public function scopeBySymbol($query, string $symbol)
{
return $query->where('symbol', strtoupper($symbol));
}
public function scopeByNetwork($query, ?string $network = null)
{
return $query->where('network', $network);
}
public function scopeOrdered($query)
{
return $query->orderBy('sort_order')->orderBy('symbol');
}
/**
* Boot method to handle file deletion and other model events
*/
protected static function boot()
{
parent::boot();
static::updating(function ($coin) {
// Delete old image if it's being changed
if ($coin->isDirty('image')) {
$original = $coin->getOriginal('image');
if ($original && !str_starts_with($original, 'http')) {
$path = 'public/coins/' . $original;
if (Storage::exists($path)) {
Storage::delete($path);
}
}
}
});
static::deleting(function ($coin) {
// Delete image when coin is deleted
$coin->deleteImage();
});
static::creating(function ($coin) {
// Auto-generate display_symbol if not provided
if (!$coin->display_symbol) {
$coin->display_symbol = $coin->network
? "{$coin->symbol}-{$coin->network}"
: $coin->symbol;
}
});
}
}
b IDATxytVսϓ22 A@IR:hCiZ[v*E:WũZA ^dQeQ @ !jZ'>gsV仿$|?g)&x-E