197 lines
No EOL
5.9 KiB
PHP
197 lines
No EOL
5.9 KiB
PHP
<?php
|
|
|
|
namespace App\Services\BusinessPlan;
|
|
|
|
use App\User;
|
|
use App\Models\UserBusinessStructure;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
/**
|
|
* Repository für effiziente Datenbankabfragen im Business-Kontext
|
|
* Löst N+1 Probleme durch optimierte Eager Loading Strategien
|
|
*/
|
|
class BusinessUserRepository
|
|
{
|
|
private $startDate;
|
|
private $endDate;
|
|
private $month;
|
|
private $year;
|
|
|
|
public function __construct(int $month, int $year)
|
|
{
|
|
$this->month = $month;
|
|
$this->year = $year;
|
|
|
|
$date = Carbon::parse($year.'-'.$month.'-1');
|
|
$this->startDate = $date->format('Y-m-d H:i:s');
|
|
$this->endDate = $date->endOfMonth()->format('Y-m-d H:i:s');
|
|
}
|
|
|
|
/**
|
|
* Lädt Root-User mit optimiertem Eager Loading und Caching
|
|
*/
|
|
public function getRootUsers(): Collection
|
|
{
|
|
$cacheKey = "root_users_{$this->month}_{$this->year}";
|
|
|
|
return cache()->remember($cacheKey, 3600, function() {
|
|
\Log::info("BusinessUserRepository: Loading root users from database (cache miss)");
|
|
|
|
return User::with([
|
|
'account',
|
|
'userLevel',
|
|
'userBusiness' => function($query) {
|
|
$query->where('month', $this->month)
|
|
->where('year', $this->year);
|
|
}
|
|
])
|
|
->select('users.*')
|
|
->where('users.deleted_at', '=', null)
|
|
->where('users.id', '!=', 1)
|
|
->where('users.admin', '<', 4)
|
|
->where('users.m_level', '!=', null)
|
|
->where('users.m_sponsor', '=', null)
|
|
->where('users.payment_account', '!=', null)
|
|
->where('users.active_date', '<=', $this->endDate)
|
|
->get();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Lädt User ohne Parent-Zuordnung (Lazy Loading für Memory-Effizienz)
|
|
*/
|
|
public function getParentlessUsers(array $excludeUserIds = []): \Generator
|
|
{
|
|
$query = User::with([
|
|
'account',
|
|
'userLevel',
|
|
'userBusiness' => function($query) {
|
|
$query->where('month', $this->month)
|
|
->where('year', $this->year);
|
|
}
|
|
])
|
|
->select('users.*')
|
|
->where('users.deleted_at', '=', null)
|
|
->where('users.id', '!=', 1)
|
|
->where('users.admin', '<', 4)
|
|
->where('users.payment_account', '!=', null)
|
|
->where('users.active_date', '<=', $this->endDate);
|
|
|
|
if (!empty($excludeUserIds)) {
|
|
$query->whereNotIn('users.id', $excludeUserIds);
|
|
}
|
|
|
|
return $query->lazy(100);
|
|
}
|
|
|
|
/**
|
|
* Lädt einen einzelnen User mit Relations und Caching
|
|
*/
|
|
public function getUserWithRelations(int $userId): ?User
|
|
{
|
|
$cacheKey = "user_relations_{$userId}_{$this->month}_{$this->year}";
|
|
|
|
return cache()->remember($cacheKey, 1800, function() use ($userId) {
|
|
\Log::debug("BusinessUserRepository: Loading user {$userId} with relations (cache miss)");
|
|
|
|
return User::with([
|
|
'account',
|
|
'userLevel',
|
|
'userBusiness' => function($query) {
|
|
$query->where('month', $this->month)
|
|
->where('year', $this->year);
|
|
}
|
|
])->find($userId);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Lädt Sponsor für einen User
|
|
*/
|
|
public function getSponsorForUser(int $userId): ?User
|
|
{
|
|
$user = $this->getUserWithRelations($userId);
|
|
|
|
if (!$user || !$user->m_sponsor) {
|
|
return null;
|
|
}
|
|
|
|
return $this->getUserWithRelations($user->m_sponsor);
|
|
}
|
|
|
|
/**
|
|
* Prüft ob gespeicherte Struktur existiert (mit Caching)
|
|
*/
|
|
public function getStoredStructure(): ?UserBusinessStructure
|
|
{
|
|
$cacheKey = "stored_structure_{$this->month}_{$this->year}";
|
|
|
|
return cache()->remember($cacheKey, 7200, function() {
|
|
\Log::debug("BusinessUserRepository: Loading stored structure (cache miss)");
|
|
|
|
$structure = UserBusinessStructure::where('year', $this->year)
|
|
->where('month', $this->month)
|
|
->first();
|
|
|
|
return ($structure && $structure->completed) ? $structure : null;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Lädt User-IDs aus gespeicherter Struktur
|
|
*/
|
|
public function getUserIdsFromStoredStructure(UserBusinessStructure $structure): array
|
|
{
|
|
$userIds = [];
|
|
|
|
if ($structure->structure) {
|
|
$this->extractUserIdsFromStructure($structure->structure, $userIds);
|
|
}
|
|
|
|
if ($structure->parentless) {
|
|
foreach ($structure->parentless as $item) {
|
|
$userIds[] = $item->user_id;
|
|
}
|
|
}
|
|
|
|
return array_unique($userIds);
|
|
}
|
|
|
|
/**
|
|
* Rekursive Extraktion von User-IDs aus Struktur
|
|
*/
|
|
private function extractUserIdsFromStructure(array $structure, array &$userIds): void
|
|
{
|
|
foreach ($structure as $item) {
|
|
$userIds[] = $item->user_id;
|
|
|
|
if (isset($item->parents) && is_array($item->parents)) {
|
|
$this->extractUserIdsFromStructure($item->parents, $userIds);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Batch-Loading für User-Kollektionen
|
|
*/
|
|
public function loadUsersInBatches(array $userIds, int $batchSize = 100): \Generator
|
|
{
|
|
$chunks = array_chunk($userIds, $batchSize);
|
|
|
|
foreach ($chunks as $chunk) {
|
|
yield User::with([
|
|
'account',
|
|
'userLevel',
|
|
'userBusiness' => function($query) {
|
|
$query->where('month', $this->month)
|
|
->where('year', $this->year);
|
|
}
|
|
])
|
|
->whereIn('id', $chunk)
|
|
->get()
|
|
->keyBy('id');
|
|
}
|
|
}
|
|
} |