File: /home/xedaptot/ai.naniguide.com/app/Model/Attachment.php
<?php
namespace App\Model;
use App\Library\Traits\HasUid;
use Illuminate\Support\Facades\Redis;
use Symfony\Component\HttpFoundation\File\File;
use App\Library\Storage\StoragePathResolver;
class Attachment extends Asset
{
protected $table = 'assets';
public const TYPE_ATTACHMENT = 'attachment';
public static function makeForCampaignOrEmail(
Campaign|AutomationEmail $campaignOrEmail,
File $file,
): static {
$attachment = new static();
$attachment->customer()->associate($campaignOrEmail->customer);
$attachment->original_name = ($file instanceof \Illuminate\Http\UploadedFile) ? $file->getClientOriginalName() : $file->getFilename();
$attachment->size = $file->getSize();
$attachment->mime_type = $file->getMimeType();
$attachment->type = static::TYPE_ATTACHMENT;
$attachment->visibility = static::VISIBILITY_PRIVATE;
$attachment->path = app(StoragePathResolver::class)->attachmentPath($campaignOrEmail->customer);
if ($campaignOrEmail instanceof Campaign) {
$attachment->campaign()->associate($campaignOrEmail);
} else {
$attachment->email()->associate($campaignOrEmail);
}
return $attachment;
}
/**
* Association with campaign through campaign_id column.
*/
public function campaign()
{
return $this->belongsTo(Campaign::class);
}
/**
* Association with email through email_id column.
*/
public function email()
{
return $this->belongsTo(AutomationEmail::class);
}
/**
* Store this file in Redis (as cache).
*
* @param int $ttl Time to live in seconds (default 5 min).
* @return string The cache ID used to retrieve the file.
*/
public function cacheToRedis(int $ttl = 1800): string
{
$content = $this->getFileContent();
$id = $this->getFileCacheId();
$keyBody = "tmp:file:{$id}:body";
$keyMeta = "tmp:file:{$id}:meta";
$meta = [
'mime' => $this->mime_type,
'size' => strlen($content),
];
// Store compressed body + metadata
Redis::multi()
->setex($keyBody, $ttl, gzencode($content, 6))
->setex($keyMeta, $ttl, json_encode($meta))
->exec();
return $id;
}
/**
* Fetch a cached file from Redis.
*
* @param string $id The cache ID returned by cacheToRedis().
* @return array|null [ 'bytes' => string, 'meta' => array ] or null if expired.
*/
public function fetchFromRedis(): ?array
{
$id = $this->getFileCacheId();
$keyBody = "tmp:file:{$id}:body";
$keyMeta = "tmp:file:{$id}:meta";
$body = Redis::get($keyBody);
$meta = json_decode(Redis::get($keyMeta) ?? '{}', true);
if (!$body) {
return null; // expired or not found
}
return [
'bytes' => gzdecode($body) ?: $body,
'meta' => $meta,
];
}
/**
* Check if a cached file still exists in Redis.
*
* @param string $id
* @return bool
*/
public function cacheExistsInRedis(): bool
{
$id = $this->getFileCacheId();
$keyBody = "tmp:file:{$id}:body";
return Redis::exists($keyBody) > 0;
}
public function getFileCacheId()
{
$id = "attachment-{$this->uid}";
return $id;
}
public function makePartFromRedis(): ?\Symfony\Component\Mime\Part\DataPart
{
$data = $this->fetchFromRedis();
if (!$data) {
return null;
}
$bytes = $data['bytes']; // already decoded in fetchFromRedis()
$mime = $data['meta']['mime'];
return new \Symfony\Component\Mime\Part\DataPart($bytes, $this->name, $mime);
}
public function makePartFromStorage(): \Symfony\Component\Mime\Part\DataPart
{
$fileContent = $this->getFileContent();
return new \Symfony\Component\Mime\Part\DataPart($fileContent, $this->name, $this->mime_type);
}
public function clearCacheFromRedis(): int
{
$id = $this->getFileCacheId();
$keyBody = "tmp:file:{$id}:body";
$keyMeta = "tmp:file:{$id}:meta";
return Redis::del([$keyBody, $keyMeta]);
}
}