feat: implement repository architecture with smart caching
- Add base repository interfaces and abstract classes - Implement separated read/write repositories for Domain and Username models - Add intelligent query caching with automatic invalidation - Include cache management service and CLI commands - Add comprehensive configuration for cache TTL and monitoring - Enhance performance through optimized data access patterns
This commit is contained in:
224
app/Repositories/Domain/Read/DomainReadRepository.php
Normal file
224
app/Repositories/Domain/Read/DomainReadRepository.php
Normal file
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repositories\Domain\Read;
|
||||
|
||||
use App\enum\DomainType;
|
||||
use App\enum\ProviderType;
|
||||
use App\Models\Domain;
|
||||
use App\Repositories\BaseRepository;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class DomainReadRepository extends BaseRepository
|
||||
{
|
||||
protected function getCacheTag(): string
|
||||
{
|
||||
return 'domains';
|
||||
}
|
||||
|
||||
public function getModel(): string
|
||||
{
|
||||
return Domain::class;
|
||||
}
|
||||
|
||||
public function findActive(): Collection
|
||||
{
|
||||
$cacheKey = $this->getCacheKey('active');
|
||||
|
||||
return $this->executeQuery(function () {
|
||||
$result = $this->query
|
||||
->where('is_active', true)
|
||||
->where(function ($query) {
|
||||
$query->whereNull('starts_at')
|
||||
->orWhere('starts_at', '<=', now());
|
||||
})
|
||||
->where(function ($query) {
|
||||
$query->whereNull('ends_at')
|
||||
->orWhere('ends_at', '>=', now());
|
||||
})
|
||||
->get();
|
||||
$this->resetQuery();
|
||||
|
||||
return $result;
|
||||
}, $cacheKey, 1800); // 30 minutes
|
||||
}
|
||||
|
||||
public function findActiveByType(?DomainType $domainType = null, ?ProviderType $providerType = null): array
|
||||
{
|
||||
$params = [
|
||||
'domain_type' => $domainType?->value,
|
||||
'provider_type' => $providerType?->value,
|
||||
];
|
||||
$cacheKey = $this->getCacheKey('active_by_type', $params);
|
||||
|
||||
return $this->executeQuery(function () use ($domainType, $providerType) {
|
||||
$query = $this->query
|
||||
->where('is_active', true)
|
||||
->where(function ($query) {
|
||||
$query->whereNull('starts_at')
|
||||
->orWhere('starts_at', '<=', now());
|
||||
})
|
||||
->where(function ($query) {
|
||||
$query->whereNull('ends_at')
|
||||
->orWhere('ends_at', '>=', now());
|
||||
});
|
||||
|
||||
if ($domainType) {
|
||||
$query->where('domain_type', $domainType->value);
|
||||
}
|
||||
|
||||
if ($providerType) {
|
||||
$query->where('provider_type', $providerType->value);
|
||||
}
|
||||
|
||||
$result = $query->pluck('name')->toArray();
|
||||
$this->resetQuery();
|
||||
|
||||
return $result;
|
||||
}, $cacheKey, 1800); // 30 minutes
|
||||
}
|
||||
|
||||
public function findActiveByProvider(ProviderType $providerType): Collection
|
||||
{
|
||||
$cacheKey = $this->getCacheKey('active_by_provider', ['provider' => $providerType->value]);
|
||||
|
||||
return $this->executeQuery(function () use ($providerType) {
|
||||
$result = $this->query
|
||||
->where('is_active', true)
|
||||
->where('provider_type', $providerType->value)
|
||||
->where(function ($query) {
|
||||
$query->whereNull('starts_at')
|
||||
->orWhere('starts_at', '<=', now());
|
||||
})
|
||||
->where(function ($query) {
|
||||
$query->whereNull('ends_at')
|
||||
->orWhere('ends_at', '>=', now());
|
||||
})
|
||||
->get();
|
||||
$this->resetQuery();
|
||||
|
||||
return $result;
|
||||
}, $cacheKey, 1800);
|
||||
}
|
||||
|
||||
public function findExpiringSoon(int $days = 7): Collection
|
||||
{
|
||||
$cacheKey = $this->getCacheKey('expiring_soon', ['days' => $days]);
|
||||
|
||||
return $this->executeQuery(function () use ($days) {
|
||||
$result = $this->query
|
||||
->where('is_active', true)
|
||||
->whereNotNull('ends_at')
|
||||
->where('ends_at', '<=', now()->addDays($days))
|
||||
->where('ends_at', '>', now())
|
||||
->orderBy('ends_at', 'asc')
|
||||
->get();
|
||||
$this->resetQuery();
|
||||
|
||||
return $result;
|
||||
}, $cacheKey, 900); // 15 minutes
|
||||
}
|
||||
|
||||
public function findInactive(): Collection
|
||||
{
|
||||
$cacheKey = $this->getCacheKey('inactive');
|
||||
|
||||
return $this->executeQuery(function () {
|
||||
$result = $this->query
|
||||
->where('is_active', false)
|
||||
->orWhere(function ($query) {
|
||||
$query->whereNotNull('starts_at')
|
||||
->where('starts_at', '>', now());
|
||||
})
|
||||
->orWhere(function ($query) {
|
||||
$query->whereNotNull('ends_at')
|
||||
->where('ends_at', '<', now());
|
||||
})
|
||||
->get();
|
||||
$this->resetQuery();
|
||||
|
||||
return $result;
|
||||
}, $cacheKey, 1800);
|
||||
}
|
||||
|
||||
public function findRecentlyUsed(int $hours = 24): Collection
|
||||
{
|
||||
$cacheKey = $this->getCacheKey('recently_used', ['hours' => $hours]);
|
||||
|
||||
return $this->executeQuery(function () use ($hours) {
|
||||
$result = $this->query
|
||||
->whereNotNull('last_used_at')
|
||||
->where('last_used_at', '>=', now()->subHours($hours))
|
||||
->orderBy('last_used_at', 'desc')
|
||||
->get();
|
||||
$this->resetQuery();
|
||||
|
||||
return $result;
|
||||
}, $cacheKey, 900); // 15 minutes
|
||||
}
|
||||
|
||||
public function findByName(string $name): ?Domain
|
||||
{
|
||||
$cacheKey = $this->getCacheKey('find_by_name', ['name' => $name]);
|
||||
|
||||
return $this->executeQuery(function () use ($name) {
|
||||
$result = $this->query->where('name', $name)->first();
|
||||
$this->resetQuery();
|
||||
|
||||
return $result;
|
||||
}, $cacheKey, 3600); // 1 hour
|
||||
}
|
||||
|
||||
public function countActive(): int
|
||||
{
|
||||
$cacheKey = $this->getCacheKey('count_active');
|
||||
|
||||
return $this->executeQuery(function () {
|
||||
$result = $this->query
|
||||
->where('is_active', true)
|
||||
->where(function ($query) {
|
||||
$query->whereNull('starts_at')
|
||||
->orWhere('starts_at', '<=', now());
|
||||
})
|
||||
->where(function ($query) {
|
||||
$query->whereNull('ends_at')
|
||||
->orWhere('ends_at', '>=', now());
|
||||
})
|
||||
->count();
|
||||
$this->resetQuery();
|
||||
|
||||
return $result;
|
||||
}, $cacheKey, 600); // 10 minutes
|
||||
}
|
||||
|
||||
public function countByProvider(): array
|
||||
{
|
||||
$cacheKey = $this->getCacheKey('count_by_provider');
|
||||
|
||||
return $this->executeQuery(function () {
|
||||
$result = $this->query
|
||||
->selectRaw('provider_type, COUNT(*) as count')
|
||||
->groupBy('provider_type')
|
||||
->pluck('count', 'provider_type')
|
||||
->toArray();
|
||||
$this->resetQuery();
|
||||
|
||||
return $result;
|
||||
}, $cacheKey, 1800);
|
||||
}
|
||||
|
||||
public function countByType(): array
|
||||
{
|
||||
$cacheKey = $this->getCacheKey('count_by_type');
|
||||
|
||||
return $this->executeQuery(function () {
|
||||
$result = $this->query
|
||||
->selectRaw('domain_type, COUNT(*) as count')
|
||||
->groupBy('domain_type')
|
||||
->pluck('count', 'domain_type')
|
||||
->toArray();
|
||||
$this->resetQuery();
|
||||
|
||||
return $result;
|
||||
}, $cacheKey, 1800);
|
||||
}
|
||||
}
|
||||
103
app/Repositories/Domain/Write/DomainWriteRepository.php
Normal file
103
app/Repositories/Domain/Write/DomainWriteRepository.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repositories\Domain\Write;
|
||||
|
||||
use App\Models\Domain;
|
||||
use App\Repositories\WriteRepository;
|
||||
|
||||
class DomainWriteRepository extends WriteRepository
|
||||
{
|
||||
protected function getCacheTag(): string
|
||||
{
|
||||
return 'domains';
|
||||
}
|
||||
|
||||
protected function getReadRepositoryClass(): string
|
||||
{
|
||||
return \App\Repositories\Domain\Read\DomainReadRepository::class;
|
||||
}
|
||||
|
||||
public function activateDomain(Domain $domain): bool
|
||||
{
|
||||
$domain->is_active = true;
|
||||
$result = $domain->save();
|
||||
$this->clearRelatedCache($domain);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function deactivateDomain(Domain $domain): bool
|
||||
{
|
||||
$domain->is_active = false;
|
||||
$result = $domain->save();
|
||||
$this->clearRelatedCache($domain);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function updateUsage(Domain $domain): bool
|
||||
{
|
||||
$domain->last_used_at = now();
|
||||
$domain->checked_at = now();
|
||||
$result = $domain->save();
|
||||
$this->clearRelatedCache($domain);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function updateCheckedAt(Domain $domain): bool
|
||||
{
|
||||
$domain->checked_at = now();
|
||||
$result = $domain->save();
|
||||
$this->clearRelatedCache($domain);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function setExpiration(Domain $domain, ?\DateTime $endsAt = null): bool
|
||||
{
|
||||
$domain->ends_at = $endsAt;
|
||||
$result = $domain->save();
|
||||
$this->clearRelatedCache($domain);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function bulkActivate(array $domainIds): int
|
||||
{
|
||||
$updated = Domain::whereIn('id', $domainIds)->update(['is_active' => true]);
|
||||
$this->clearCache();
|
||||
|
||||
return $updated;
|
||||
}
|
||||
|
||||
public function bulkDeactivate(array $domainIds): int
|
||||
{
|
||||
$updated = Domain::whereIn('id', $domainIds)->update(['is_active' => false]);
|
||||
$this->clearCache();
|
||||
|
||||
return $updated;
|
||||
}
|
||||
|
||||
public function updateDailyMailboxLimit(Domain $domain, int $limit): bool
|
||||
{
|
||||
$domain->daily_mailbox_limit = $limit;
|
||||
$result = $domain->save();
|
||||
$this->clearRelatedCache($domain);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function createWithDefaults(array $data): Domain
|
||||
{
|
||||
$defaults = [
|
||||
'is_active' => true,
|
||||
'daily_mailbox_limit' => 100,
|
||||
'checked_at' => now(),
|
||||
];
|
||||
|
||||
$domainData = array_merge($defaults, $data);
|
||||
|
||||
return $this->create($domainData);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user