Benutzer-Werkzeuge

Webseiten-Werkzeuge


kunden_anlegen

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

kunden_anlegen [2026/01/26 14:24] – angelegt pldokukunden_anlegen [2026/01/29 19:33] (aktuell) – gelöscht pldoku
Zeile 1: Zeile 1:
-Vorschlag: Umstrukturierung der Käufererstellung nach 3-Schichten-Architektur 
-Analyse der aktuellen Implementierung 
-Aktuelle Struktur (Buyer::Create()) 
-Die aktuelle Buyer::Create() Funktion enthält alle Schichten vermischt: 
  
-Validierung (ValidateData, ValidateVaucher, CheckDuplicate, CheckLager, CheckCountry) 
-Geschäftslogik (setAgentByEmail, Generierung von Passwort/Kundennummer/Username, Bestimmung von frominternet) 
-Datenbankzugriffe (InsertBuyer, SaveAdresses, SaveBank, direkte SQL-Queries) 
-Cache-Operationen (CacheBuyers, CacheAddress) 
-Externe Integrationen (E-Mail-Versand, CMP-Export, Letter Package) 
-Orchestrierung (implizit durch die Reihenfolge der Aufrufe) 
-Vorschlag: Neue Struktur 
-Übersicht der Schichten 
-API (CustomerController) 
-  ↓ 
-Orchestrierung (BuyerCreationOrchestration) 
-  ↓ 
-  ├─→ Business-Funktionen (Fachfunktionen) 
-  │   ├─→ ValidateBuyerDataFunction 
-  │   ├─→ DetermineSponsorFunction 
-  │   ├─→ GenerateBuyerIdentifiersFunction 
-  │   ├─→ ValidateAddressesFunction 
-  │   ├─→ DetermineRegistrationSourceFunction 
-  │   └─→ ValidateVoucherFunction 
-  ↓ 
-Zwischenschicht (Repositories) 
-  ├─→ BuyerRepository 
-  ├─→ AddressRepository 
-  ├─→ BankRepository 
-  ├─→ UserRepository 
-  ├─→ SponsorRepository 
-  ├─→ VoucherRepository 
-  └─→ InterestingRepository 
-  ↓ 
-Framework (Yii2) 
-Detaillierte Aufteilung 
-1. API Layer 
-Datei: backend/modules/api/controllers/CustomerController.php 
- 
-Verantwortlichkeiten: 
- 
-Empfang von HTTP-Requests 
-Validierung des Request-Formats 
-Aufruf der Orchestrierung 
-Formatierung der Response 
-Fehlerbehandlung auf API-Ebene 
-Code-Struktur: 
- 
-class CustomerController extends Controller 
-{ 
-    public function actionCreate($request) 
-    { 
-        try { 
-            // 1. Request-Validierung (Format, nicht Inhalt!) 
-            $this->validateRequestFormat($request); 
-             
-            // 2. Aufruf Orchestrierung 
-            $orchestration = Yii::$container->get(BuyerCreationOrchestration::class); 
-            $result = $orchestration->createBuyer($request); 
-             
-            // 3. Response formatieren 
-            return $this->formatResponse($result); 
-             
-        } catch (ValidationException $e) { 
-            return $this->formatErrorResponse($e); 
-        } catch (\Exception $e) { 
-            return $this->formatErrorResponse($e); 
-        } 
-    } 
-} 
-Was NICHT hier: 
- 
-❌ Geschäftslogik 
-❌ Validierung von Geschäftsdaten 
-❌ Entscheidungen über Prozessablauf 
-2. Orchestrierung (BuyerCreationOrchestration) 
-Datei: backend/modules/core/orchestration/BuyerCreationOrchestration.php 
- 
-Verantwortlichkeiten: 
- 
-Koordination des gesamten Erstellungsprozesses 
-Aufruf der Business-Funktionen in richtiger Reihenfolge 
-Datenbeschaffung über Zwischenschicht 
-Zusammenführung der Ergebnisse 
-KEINE Geschäftsentscheidungen 
-Code-Struktur: 
- 
-class BuyerCreationOrchestration 
-{ 
-    private $buyerRepository; 
-    private $validateBuyerDataFunction; 
-    private $determineSponsorFunction; 
-    private $generateIdentifiersFunction; 
-    private $validateAddressesFunction; 
-    private $determineRegistrationSourceFunction; 
-    private $validateVoucherFunction; 
-    private $saveBuyerFunction; 
-    private $saveAddressesFunction; 
-    private $saveBankFunction; 
-    private $sendEmailFunction; 
-    private $cacheService; 
-    private $cmpService; 
-     
-    public function createBuyer($request) 
-    { 
-        // 1. Daten aus Request extrahieren 
-        $buyerData = $this->extractBuyerData($request); 
-         
-        // 2. Validierung der Eingabedaten 
-        $validationResult = $this->validateBuyerDataFunction->validate($buyerData); 
-        if (!$validationResult->isValid()) { 
-            return $this->createErrorResponse($validationResult); 
-        } 
-         
-        // 3. Bestimmung des Sponsors (agentid) 
-        $sponsorData = $this->determineSponsorFunction->determine($buyerData); 
-        $buyerData->merge($sponsorData); 
-         
-        // 4. Validierung Gutschein (wenn aktiviert) 
-        if ($this->isVoucherValidationEnabled()) { 
-            $voucherResult = $this->validateVoucherFunction->validate($buyerData); 
-            $buyerData->merge($voucherResult); 
-        } 
-         
-        // 5. Prüfung auf Duplikate (wenn nicht Guest) 
-        if (!$buyerData->isGuest() && !$buyerData->skipDuplicateCheck()) { 
-            $duplicateCheck = $this->checkDuplicates($buyerData); 
-            if ($duplicateCheck->hasDuplicates()) { 
-                return $this->createDuplicateResponse($duplicateCheck); 
-            } 
-        } 
-         
-        // 6. Automatische Erstellung aus Interessent (wenn aktiviert) 
-        if ($this->isAutoCreateFromInterestingEnabled() && $buyerData->hasEmail()) { 
-            $interestingData = $this->buyerRepository->findInterestingByEmail($buyerData->getEmail()); 
-            if ($interestingData) { 
-                return $this->createFromInteresting($interestingData, $buyerData); 
-            } 
-        } 
-         
-        // 7. Bestimmung der Registrierungsquelle (frominternet) 
-        $registrationSource = $this->determineRegistrationSourceFunction->determine($buyerData); 
-        $buyerData->setRegistrationSource($registrationSource); 
-         
-        // 8. Generierung von Identifikatoren 
-        $identifiers = $this->generateIdentifiersFunction->generate($buyerData); 
-        $buyerData->merge($identifiers); 
-         
-        // 9. Validierung von Adressen 
-        if ($buyerData->hasAddresses()) { 
-            $addressValidation = $this->validateAddressesFunction->validate($buyerData->getAddresses()); 
-            if (!$addressValidation->isValid()) { 
-                return $this->createErrorResponse($addressValidation); 
-            } 
-        } 
-         
-        // 10. Validierung Lager (wenn angegeben) 
-        if ($buyerData->hasWarehouse()) { 
-            $warehouseValidation = $this->validateWarehouse($buyerData); 
-            if (!$warehouseValidation->isValid()) { 
-                return $this->createErrorResponse($warehouseValidation); 
-            } 
-        } 
-         
-        // 11. Speicherung des Käufers 
-        $savedBuyer = $this->saveBuyerFunction->save($buyerData); 
-         
-        // 12. Speicherung der Adressen 
-        if ($buyerData->hasAddresses()) { 
-            $this->saveAddressesFunction->save($savedBuyer->getId(), $buyerData->getAddresses()); 
-        } 
-         
-        // 13. Speicherung der Bankdaten 
-        if ($buyerData->hasBankData()) { 
-            $this->saveBankFunction->save($savedBuyer->getId(), $buyerData->getBankData()); 
-        } 
-         
-        // 14. Erstellung des Benutzers (WboUser) 
-        $user = $this->createUser($savedBuyer, $buyerData); 
-         
-        // 15. Zusätzliche Aktionen 
-        $this->performPostCreationActions($savedBuyer, $buyerData); 
-         
-        // 16. Cache aktualisieren 
-        $this->cacheService->cacheBuyer($savedBuyer->getId()); 
-        $this->cacheService->cacheAddresses($savedBuyer->getId()); 
-         
-        // 17. Response zusammenstellen 
-        return $this->createSuccessResponse($savedBuyer, $user); 
-    } 
-     
-    private function checkDuplicates($buyerData) 
-    { 
-        // Aufruf über Repository 
-        return $this->buyerRepository->checkDuplicates($buyerData); 
-    } 
-     
-    private function validateWarehouse($buyerData) 
-    { 
-        // Business-Funktion für Lager-Validierung 
-        return $this->validateWarehouseFunction->validate($buyerData); 
-    } 
-     
-    private function createUser($buyer, $buyerData) 
-    { 
-        // Orchestrierung für User-Erstellung (kann auch eigene Orchestrierung haben) 
-        $userOrchestration = Yii::$container->get(UserCreationOrchestration::class); 
-        return $userOrchestration->createUser($buyer, $buyerData); 
-    } 
-     
-    private function performPostCreationActions($buyer, $buyerData) 
-    { 
-        // E-Mail-Versand (wenn nicht Guest) 
-        if (!$buyerData->isGuest()) { 
-            $this->sendEmailFunction->sendBuyerRegistrationEmail($buyer); 
-        } 
-         
-        // Letter Package 
-        $this->letterPackageService->checkAndAdd($buyer, $buyerData); 
-         
-        // Starter zu VP konvertieren (wenn starterid vorhanden) 
-        if ($buyerData->hasStarterId()) { 
-            $this->convertStarterToVP($buyerData->getStarterId()); 
-        } 
-         
-        // CMP-Export (wenn aktiviert) 
-        if ($this->isCmpExportEnabled()) { 
-            $this->cmpService->addToQueue($buyer->getId(), 1); 
-        } 
-    } 
-} 
-Was NICHT hier: 
- 
-❌ Geschäftslogik (z.B. "wenn agentid fehlt, dann...") 
-❌ Direkte Datenbankzugriffe 
-❌ SQL-Queries 
-❌ Validierungsregeln 
-3. Business-Funktionen (Fachfunktionen) 
-3.1. ValidateBuyerDataFunction 
-Datei: backend/modules/core/functions/ValidateBuyerDataFunction.php 
- 
-Verantwortlichkeiten: 
- 
-Validierung aller Käuferdaten nach Geschäftsregeln 
-Prüfung von Pflichtfeldern 
-Format-Validierung (E-Mail, IBAN, Steuernummer, etc.) 
-Rückgabe von Validierungsergebnissen 
-Code-Struktur: 
- 
-class ValidateBuyerDataFunction 
-{ 
-    private $emailValidator; 
-    private $ibanValidator; 
-    private $taxIdValidator; 
-    private $usernameValidator; 
-     
-    public function validate(BuyerData $buyerData): ValidationResult 
-    { 
-        $result = new ValidationResult(); 
-         
-        // 1. E-Mail-Validierung 
-        if ($buyerData->hasEmail()) { 
-            if (!$this->emailValidator->isValid($buyerData->getEmail())) { 
-                $result->addError('email', 'Invalid email format'); 
-            } 
-        } else { 
-            $result->addError('email', 'Email is required'); 
-        } 
-         
-        // 2. Username-Validierung (wenn angegeben) 
-        if ($buyerData->hasUsername()) { 
-            if (!$this->usernameValidator->isUnique($buyerData->getUsername())) { 
-                $result->addError('username', 'Username already exists'); 
-            } 
-        } 
-         
-        // 3. Steuernummer-Validierung (wenn angegeben) 
-        if ($buyerData->hasTaxId()) { 
-            if (!$this->taxIdValidator->isValid($buyerData->getTaxId())) { 
-                $result->addError('taxId', 'Invalid tax ID'); 
-            } 
-        } 
-         
-        // 4. IBAN-Validierung (wenn Bankdaten vorhanden) 
-        if ($buyerData->hasBankData()) { 
-            $ibanValidation = $this->ibanValidator->validate($buyerData->getBankData()); 
-            if (!$ibanValidation->isValid()) { 
-                $result->mergeErrors($ibanValidation); 
-            } 
-        } 
-         
-        // 5. PLZ-Validierung (wenn Adressen vorhanden) 
-        if ($buyerData->hasAddresses()) { 
-            $plzValidation = $this->validatePostalCodes($buyerData->getAddresses()); 
-            if (!$plzValidation->isValid()) { 
-                $result->mergeErrors($plzValidation); 
-            } 
-        } 
-         
-        return $result; 
-    } 
-     
-    private function validatePostalCodes(array $addresses): ValidationResult 
-    { 
-        $result = new ValidationResult(); 
-        foreach ($addresses as $address) { 
-            if (!$this->postalCodeValidator->isValid($address->getPostCode(), $address->getCountry())) { 
-                $result->addError('postCode', 'Invalid postal code for country'); 
-            } 
-        } 
-        return $result; 
-    } 
-} 
-Was NICHT hier: 
- 
-❌ Datenbankzugriffe (nur über Interfaces/Repositories) 
-❌ SQL-Queries 
-❌ Framework-spezifischer Code 
-3.2. DetermineSponsorFunction 
-Datei: backend/modules/core/functions/DetermineSponsorFunction.php 
- 
-Verantwortlichkeiten: 
- 
-Bestimmung des Sponsors (agentid) nach Geschäftsregeln 
-Automatische Bestimmung über E-Mail (wenn aktiviert) 
-Rückgabe des Sponsor-Ergebnisses 
-Code-Struktur: 
- 
-class DetermineSponsorFunction 
-{ 
-    private $sponsorRepository; 
-    private $buyerRepository; 
-    private $config; 
-     
-    public function determine(BuyerData $buyerData): SponsorResult 
-    { 
-        $result = new SponsorResult(); 
-         
-        // 1. Wenn agentid bereits vorhanden → validieren 
-        if ($buyerData->hasAgentId()) { 
-            $sponsor = $this->sponsorRepository->findByAgentId($buyerData->getAgentId()); 
-            if (!$sponsor || !$sponsor->isActive()) { 
-                throw new InvalidSponsorException('Sponsor not found or inactive'); 
-            } 
-            $result->setAgentId($sponsor->getAgentId()); 
-            $result->setAgentInternalId($sponsor->getInternalId()); 
-            return $result; 
-        } 
-         
-        // 2. Automatische Bestimmung über E-Mail (wenn aktiviert) 
-        if ($this->config->isAgentByEmailEnabled() && $buyerData->hasEmail()) { 
-            $sponsor = $this->determineByEmail($buyerData->getEmail()); 
-            if ($sponsor) { 
-                $result->setAgentId($sponsor->getAgentId()); 
-                $result->setAgentInternalId($sponsor->getInternalId()); 
-                return $result; 
-            } 
-        } 
-         
-        // 3. Fallback auf Master-VP (Geschäftsregel) 
-        $masterVP = $this->sponsorRepository->findMasterVP(); 
-        $result->setAgentId($masterVP->getAgentId()); 
-        $result->setAgentInternalId($masterVP->getInternalId()); 
-         
-        return $result; 
-    } 
-     
-    private function determineByEmail(string $email): ?Sponsor 
-    { 
-        // Geschäftsregel: Suche nach Käufer mit dieser E-Mail 
-        $buyer = $this->buyerRepository->findByEmail($email); 
-        if (!$buyer) { 
-            return null; 
-        } 
-         
-        // Geschäftsregel: Bestimme Sponsor des gefundenen Käufers 
-        $sponsor = $this->sponsorRepository->findByBuyerId($buyer->getId()); 
-         
-        // Geschäftsregel: Sortierung (neueste/älteste) 
-        if ($this->config->getAgentByEmailMode() === 'newest') { 
-            // Neueste zuerst 
-            return $sponsor; 
-        } else { 
-            // Älteste zuerst 
-            return $sponsor; 
-        } 
-    } 
-} 
-3.3. GenerateBuyerIdentifiersFunction 
-Datei: backend/modules/core/functions/GenerateBuyerIdentifiersFunction.php 
- 
-Verantwortlichkeiten: 
- 
-Generierung von Kundennummer (kundennr) 
-Generierung von Username 
-Generierung von Passwort (wenn nicht vorhanden) 
-Bestimmung von accounting-Nummer 
-Rückgabe aller generierten Identifikatoren 
-Code-Struktur: 
- 
-class GenerateBuyerIdentifiersFunction 
-{ 
-    private $buyerRepository; 
-    private $passwordGenerator; 
-    private $usernameGenerator; 
-    private $customerNumberGenerator; 
-     
-    public function generate(BuyerData $buyerData): IdentifierResult 
-    { 
-        $result = new IdentifierResult(); 
-         
-        // 1. Kundennummer generieren 
-        $customerNumber = $this->generateCustomerNumber($buyerData); 
-        $result->setCustomerNumber($customerNumber); 
-         
-        // 2. Username generieren 
-        $username = $this->generateUsername($buyerData, $customerNumber); 
-        $result->setUsername($username); 
-         
-        // 3. Passwort generieren (wenn nicht vorhanden) 
-        if (!$buyerData->hasPassword()) { 
-            $password = $this->passwordGenerator->generate(); 
-            $result->setPassword($password); 
-        } else { 
-            $result->setPassword($buyerData->getPassword()); 
-        } 
-         
-        // 4. Accounting-Nummer bestimmen 
-        $accounting = $this->determineAccountingNumber($buyerData, $customerNumber); 
-        $result->setAccounting($accounting); 
-         
-        return $result; 
-    } 
-     
-    private function generateCustomerNumber(BuyerData $buyerData): string 
-    { 
-        // Geschäftsregel 1: Wenn number angegeben → verwenden (nach Prüfung) 
-        if ($buyerData->hasNumber() && !$buyerData->hasImportNumber()) { 
-            // Prüfung auf Eindeutigkeit über Repository 
-            if ($this->buyerRepository->isCustomerNumberAvailable($buyerData->getNumber())) { 
-                return $buyerData->getNumber(); 
-            } 
-            throw new CustomerNumberAlreadyExistsException(); 
-        } 
-         
-        // Geschäftsregel 2: Wenn mitarbeiternr vorhanden → verwenden 
-        if ($buyerData->hasMitarbeiternr()) { 
-            return $buyerData->getMitarbeiternr(); 
-        } 
-         
-        // Geschäftsregel 3: Automatische Generierung 
-        return $this->customerNumberGenerator->generate(); 
-    } 
-     
-    private function generateUsername(BuyerData $buyerData, string $customerNumber): string 
-    { 
-        // Geschäftsregel: Wenn username vorhanden → verwenden (bereits validiert) 
-        if ($buyerData->hasUsername()) { 
-            return $buyerData->getUsername(); 
-        } 
-         
-        // Geschäftsregel: Generierung basierend auf Kundennummer 
-        return $this->usernameGenerator->generateFromCustomerNumber($customerNumber); 
-    } 
-     
-    private function determineAccountingNumber(BuyerData $buyerData, string $customerNumber): string 
-    { 
-        // Geschäftsregel 1: Wenn emplaccounting vorhanden → verwenden 
-        if ($buyerData->hasEmplAccounting()) { 
-            return $buyerData->getEmplAccounting(); 
-        } 
-         
-        // Geschäftsregel 2: Wenn accounting vorhanden → verwenden 
-        if ($buyerData->hasAccounting()) { 
-            return $buyerData->getAccounting(); 
-        } 
-         
-        // Geschäftsregel 3: Fallback auf Kundennummer 
-        return $customerNumber; 
-    } 
-} 
-3.4. ValidateAddressesFunction 
-Datei: backend/modules/core/functions/ValidateAddressesFunction.php 
- 
-Verantwortlichkeiten: 
- 
-Validierung aller Adressen 
-Prüfung auf Pflichtfelder 
-Validierung von Ländern 
-Prüfung auf Konsistenz 
-Code-Struktur: 
- 
-class ValidateAddressesFunction 
-{ 
-    private $countryRepository; 
-    private $postalCodeValidator; 
-     
-    public function validate(array $addresses): ValidationResult 
-    { 
-        $result = new ValidationResult(); 
-         
-        foreach ($addresses as $index => $address) { 
-            // 1. Land-Validierung 
-            if (!$address->hasCountry()) { 
-                $result->addError("addresses[$index].country", 'Country is required'); 
-                continue; 
-            } 
-             
-            $country = $this->countryRepository->findByCode($address->getCountry()); 
-            if (!$country) { 
-                $result->addError("addresses[$index].country", 'Invalid country code'); 
-                continue; 
-            } 
-             
-            // 2. PLZ-Validierung 
-            if ($address->hasPostCode()) { 
-                if (!$this->postalCodeValidator->isValid($address->getPostCode(), $country)) { 
-                    $result->addError("addresses[$index].postCode", 'Invalid postal code'); 
-                } 
-            } 
-             
-            // 3. Weitere Validierungen... 
-        } 
-         
-        return $result; 
-    } 
-} 
-3.5. DetermineRegistrationSourceFunction 
-Datei: backend/modules/core/functions/DetermineRegistrationSourceFunction.php 
- 
-Verantwortlichkeiten: 
- 
-Bestimmung von frominternet basierend auf autonumbertype 
-Rückgabe der Registrierungsquelle 
-Code-Struktur: 
- 
-class DetermineRegistrationSourceFunction 
-{ 
-    public function determine(BuyerData $buyerData): RegistrationSource 
-    { 
-        // Geschäftsregel: Mapping von autonumbertype zu frominternet 
-        if ($buyerData->hasAutonumbertype()) { 
-            $type = $buyerData->getAutonumbertype(); 
-             
-            if ($type === 3) { 
-                return new RegistrationSource(107, 'Internet registration'); 
-            } 
-             
-            if ($type === 5) { 
-                return new RegistrationSource(111, 'Special registration'); 
-            } 
-             
-            // Standard für andere autonumbertype 
-            return new RegistrationSource(103, 'Internet registration with auto number'); 
-        } 
-         
-        // Standard-Registrierungsquelle 
-        return new RegistrationSource(102, 'Standard registration'); 
-    } 
-} 
-3.6. ValidateVoucherFunction 
-Datei: backend/modules/core/functions/ValidateVoucherFunction.php 
- 
-Verantwortlichkeiten: 
- 
-Validierung von Gutschein-Code 
-Bestimmung des zugehörigen Sponsors 
-Rückgabe des Sponsor-Ergebnisses 
-Code-Struktur: 
- 
-class ValidateVoucherFunction 
-{ 
-    private $voucherRepository; 
-    private $sponsorRepository; 
-     
-    public function validate(BuyerData $buyerData): VoucherResult 
-    { 
-        $result = new VoucherResult(); 
-         
-        // Wenn kein agentid vorhanden → kein Gutschein-Check 
-        if (!$buyerData->hasAgentId()) { 
-            return $result; 
-        } 
-         
-        // Prüfung ob agentid ein Gutschein-Code ist 
-        $voucher = $this->voucherRepository->findByCode($buyerData->getAgentId()); 
-        if (!$voucher) { 
-            // Kein Gutschein → agentid bleibt unverändert 
-            return $result; 
-        } 
-         
-        // Geschäftsregel: Sponsor aus Gutschein bestimmen 
-        $sponsor = $this->sponsorRepository->findByInternalId($voucher->getForAgent()); 
-        if (!$sponsor) { 
-            throw new InvalidVoucherException('Sponsor for voucher not found'); 
-        } 
-         
-        // Ergebnis: agentid ersetzen, Gutschein-Code speichern 
-        $result->setAgentId($sponsor->getAgentId()); 
-        $result->setAgentInternalId($sponsor->getInternalId()); 
-        $result->setVoucherCode($buyerData->getAgentId()); 
-         
-        return $result; 
-    } 
-} 
-4. Zwischenschicht (Repositories) 
-4.1. BuyerRepository 
-Datei: backend/modules/core/repositories/BuyerRepository.php 
- 
-Verantwortlichkeiten: 
- 
-Alle SQL-Queries für Käufer 
-CRUD-Operationen 
-Suche nach Käufern 
-Duplikat-Prüfung 
-Code-Struktur: 
- 
-class BuyerRepository implements BuyerRepositoryInterface 
-{ 
-    private $db; 
-     
-    public function create(BuyerEntity $buyer): BuyerEntity 
-    { 
-        // SQL-Query hier 
-        $sql = "INSERT INTO tb_kundendaten (...) VALUES (...)"; 
-        $this->db->createCommand($sql, [...])->execute(); 
-         
-        // Stored Procedure aufrufen 
-        $this->db->createCommand("CALL kd_adresse(?)", [$buyer->getInternekdnr()])->execute(); 
-         
-        return $buyer; 
-    } 
-     
-    public function findById(int $id): ?BuyerEntity 
-    { 
-        $sql = "SELECT * FROM tb_kundendaten WHERE internekdnr = ?"; 
-        $row = $this->db->createCommand($sql, [$id])->queryOne(); 
-         
-        if (!$row) { 
-            return null; 
-        } 
-         
-        return $this->mapToEntity($row); 
-    } 
-     
-    public function findByEmail(string $email): ?BuyerEntity 
-    { 
-        $sql = "SELECT * FROM tb_kundendaten WHERE kdemail = ?"; 
-        $row = $this->db->createCommand($sql, [$email])->queryOne(); 
-         
-        if (!$row) { 
-            return null; 
-        } 
-         
-        return $this->mapToEntity($row); 
-    } 
-     
-    public function isCustomerNumberAvailable(string $customerNumber): bool 
-    { 
-        $sql = "SELECT COUNT(*) FROM tb_kundendaten WHERE kundennr = ?"; 
-        $count = $this->db->createCommand($sql, [$customerNumber])->queryScalar(); 
-        return $count === 0; 
-    } 
-     
-    public function checkDuplicates(BuyerData $buyerData): DuplicateCheckResult 
-    { 
-        // Komplexe SQL-Query für Duplikat-Prüfung 
-        $sql = "..."; // Aus ValidateDuplicate 
-        $rows = $this->db->createCommand($sql, [...])->queryAll(); 
-         
-        return $this->mapToDuplicateResult($rows); 
-    } 
-     
-    public function generateNewCustomerNumber(): string 
-    { 
-        $sql = "SELECT getNewBuyerOutID() as newkdnr"; 
-        return $this->db->createCommand($sql)->queryScalar(); 
-    } 
-     
-    private function mapToEntity(array $row): BuyerEntity 
-    { 
-        // Mapping von DB-Row zu Entity 
-        return BuyerEntity::fromArray($row); 
-    } 
-} 
-4.2. AddressRepository 
-Datei: backend/modules/core/repositories/AddressRepository.php 
- 
-Code-Struktur: 
- 
-class AddressRepository implements AddressRepositoryInterface 
-{ 
-    private $db; 
-     
-    public function create(int $buyerId, AddressEntity $address): AddressEntity 
-    { 
-        $sql = "INSERT INTO tb_kundendaten_zusatz (...) VALUES (...)"; 
-        $this->db->createCommand($sql, [...])->execute(); 
-         
-        // Stored Procedure 
-        $this->db->createCommand("CALL kd_adresse_z(?)", [$address->getId()])->execute(); 
-         
-        return $address; 
-    } 
-     
-    public function update(int $addressId, AddressEntity $address): void 
-    { 
-        $sql = "UPDATE tb_kundendaten_zusatz SET ... WHERE id = ?"; 
-        $this->db->createCommand($sql, [...])->execute(); 
-         
-        $this->db->createCommand("CALL kd_adresse_z(?)", [$addressId])->execute(); 
-    } 
-     
-    public function findMainAddress(int $buyerId): ?AddressEntity 
-    { 
-        $sql = "SELECT * FROM tb_kundendaten_zusatz WHERE internekdnr = ? AND maindata = 1"; 
-        $row = $this->db->createCommand($sql, [$buyerId])->queryOne(); 
-         
-        return $row ? $this->mapToEntity($row) : null; 
-    } 
-     
-    public function findByReference(int $buyerId, string $reference): ?AddressEntity 
-    { 
-        $sql = "SELECT * FROM tb_kundendaten_zusatz WHERE internekdnr = ? AND reference = ?"; 
-        $row = $this->db->createCommand($sql, [$buyerId, $reference])->queryOne(); 
-         
-        return $row ? $this->mapToEntity($row) : null; 
-    } 
-     
-    public function generateNewAddressId(): int 
-    { 
-        // Generierung neuer ID 
-        $sql = "SELECT ..."; 
-        return $this->db->createCommand($sql)->queryScalar(); 
-    } 
-} 
-4.3. Weitere Repositories 
-BankRepository - für Bankdaten 
-UserRepository - für mlm_users und mlm_wbo_users 
-SponsorRepository - für Partner/Sponsor-Daten 
-VoucherRepository - für Gutscheine 
-InterestingRepository - für Interessenten 
-CountryRepository - für Länder 
-WarehouseRepository - für Lager 
-5. Framework Layer 
-Bleibt unverändert: 
- 
-Yii2 Framework 
-Datenbankverbindungen 
-Sicherheit 
-Routing 
-Dependency Injection Container 
-Mapping: Alte → Neue Struktur 
-Aktuelle Funktion Neue Struktur 
-Buyer::Create() BuyerCreationOrchestration::createBuyer() 
-Buyer::ValidateData() ValidateBuyerDataFunction::validate() 
-Buyer::ValidateVaucher() ValidateVoucherFunction::validate() 
-Buyer::setAgentByEmail() DetermineSponsorFunction::determineByEmail() 
-Buyer::CheckDuplicate() BuyerRepository::checkDuplicates() 
-Buyer::getNewBuyerOutID() BuyerRepository::generateNewCustomerNumber() 
-Buyer::CreateUserName() GenerateBuyerIdentifiersFunction::generateUsername() 
-Buyer::InsertBuyer() BuyerRepository::create() 
-Buyer::SaveAdresses() AddressRepository::create() / AddressRepository::update() 
-Buyer::SaveBank() BankRepository::create() 
-Buyer::SendBuyerMail() SendEmailFunction::sendBuyerRegistrationEmail() 
-Cache::CacheBuyers() CacheService::cacheBuyer() 
-Direkte SQL-Queries Alle in Repositories 
-Vorteile der neuen Struktur 
-Trennung der Verantwortlichkeiten: 
- 
-Geschäftslogik ist klar getrennt von Datenzugriff 
-Jede Funktion hat eine einzige Verantwortlichkeit 
-Testbarkeit: 
- 
-Business-Funktionen können ohne Datenbank getestet werden 
-Repositories können isoliert getestet werden 
-Orchestrierung kann mit Mocks getestet werden 
-Wartbarkeit: 
- 
-Änderungen an Geschäftsregeln → nur Business-Funktionen 
-Änderungen an Datenbankstruktur → nur Repositories 
-Framework-Wechsel → nur Repositories und API-Layer 
-Erweiterbarkeit: 
- 
-Neue Geschäftsregeln → neue Business-Funktionen 
-Neue Datenquellen → neue Repository-Implementierungen 
-Framework-Unabhängigkeit: 
- 
-Core (Business-Funktionen, Orchestrierung) kann 1:1 auf Yii3 portiert werden 
-Nur Repositories und API-Layer müssen angepasst werden 
-Migrationsstrategie 
-Phase 1: Vorbereitung 
-Interfaces für alle Repositories definieren 
-Entity-Klassen erstellen 
-DTOs (Data Transfer Objects) für Request/Response 
-Phase 2: Repositories extrahieren 
-SQL-Queries aus Buyer::Create() in Repositories verschieben 
-Buyer::Create() verwendet Repositories (noch alte Struktur) 
-Phase 3: Business-Funktionen extrahieren 
-Geschäftslogik in separate Funktionen auslagern 
-Buyer::Create() wird zur Orchestrierung 
-Phase 4: API-Layer anpassen 
-Controller ruft Orchestrierung auf 
-Alte Buyer::Create() wird deprecated 
-Phase 5: Cleanup 
-Alte Implementierung entfernen 
-Tests schreiben 
-Dokumentation aktualisieren 
-Beispiel: Vollständiger Ablauf 
-1. API Request 
-   POST /api/v1/customers 
-   { 
-     "email": "test@example.com", 
-     "agentid": "10038", 
-     "adresses": [...] 
-   } 
- 
-2. CustomerController::actionCreate() 
-   - Request formatieren 
-   - BuyerCreationOrchestration aufrufen 
- 
-3. BuyerCreationOrchestration::createBuyer() 
-   - ValidateBuyerDataFunction aufrufen 
-   - DetermineSponsorFunction aufrufen 
-   - GenerateBuyerIdentifiersFunction aufrufen 
-   - BuyerRepository::create() aufrufen 
-   - AddressRepository::create() aufrufen 
-   - UserCreationOrchestration aufrufen 
-   - CacheService aufrufen 
-   - Response zusammenstellen 
- 
-4. Business-Funktionen 
-   - Führen Geschäftsregeln aus 
-   - Rufen Repositories auf (nur lesend) 
-   - Keine direkten DB-Zugriffe 
- 
-5. Repositories 
-   - Führen SQL-Queries aus 
-   - Mappen Daten zu Entities 
-   - Keine Geschäftslogik 
- 
-6. Response 
-   { 
-     "status": "success", 
-     "data": { 
-       "internekdnr": 12345, 
-       "kundennr": "10038", 
-       "username": "user10038" 
-     } 
-   } 
-Zusammenfassung 
-Die neue Struktur trennt klar: 
- 
-API: Request/Response-Handling 
-Orchestrierung: Koordination des Prozesses 
-Business-Funktionen: Geschäftsregeln 
-Repositories: Datenzugriff 
-Framework: Technische Infrastruktur 
-Alle aktuellen Funktionen bleiben erhalten, sind aber klar strukturiert und testbar. 
kunden_anlegen.1769433845.txt.gz · Zuletzt geändert: von pldoku

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki