src/StartPlatz/Bundle/EventBundle/Controller/EventRegistrationController.php line 862

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace App\StartPlatz\Bundle\EventBundle\Controller;
  3. /**
  4.  * CRITICAL DEPENDENCY WARNING - APPLICATION SYSTEM INTEGRATION
  5.  *
  6.  * This controller handles the event/signup REGISTRATION flow.
  7.  * It has HEAVY DEPENDENCIES on the Application system from StartupBundle.
  8.  *
  9.  * DO NOT modify Application-related code without testing ALL event registration scenarios:
  10.  * - Anonymous (non-member) event registration
  11.  * - Member event registration
  12.  * - Paid event registration with Stripe
  13.  * - Free event registration
  14.  * - Multi-batch event registration
  15.  * - Event registration with validation workflows
  16.  *
  17.  * Extracted from DefaultController (B-60 Phase 2). Uses shared services for dependencies.
  18.  */
  19. use App\StartPlatz\Bundle\EventBundle\Entity\Event;
  20. use App\StartPlatz\Bundle\EventBundle\Entity\EventRepository;
  21. use App\StartPlatz\Bundle\EventBundle\Service\EventNotificationService;
  22. use App\StartPlatz\Bundle\EventBundle\Service\EventPaymentService;
  23. use App\StartPlatz\Bundle\EventBundle\Service\EventRegistrationService;
  24. use App\StartPlatz\Bundle\EventBundle\Service\EventViewHelper;
  25. use App\StartPlatz\Bundle\MailBundle\Service\MailService;
  26. use App\StartPlatz\Bundle\MemberBundle\Entity\Member;
  27. use App\StartPlatz\Bundle\MemberBundle\Entity\Option;
  28. use App\StartPlatz\Bundle\MemberBundle\Entity\Product;
  29. use App\StartPlatz\Bundle\MemberBundle\Entity\Service;
  30. use App\StartPlatz\Bundle\MonsumBundle\Entity\Customer;
  31. use App\StartPlatz\Bundle\StartupBundle\Entity\Application;
  32. use App\StartPlatz\Bundle\StartupBundle\Entity\Batch;
  33. use App\StartPlatz\Bundle\StartupBundle\Entity\Program;
  34. use App\StartPlatz\Bundle\StartupBundle\Form\ApplicationType;
  35. use App\StartPlatz\Bundle\UserBundle\Entity\User;
  36. use App\StartPlatz\Bundle\UserBundle\LoginService;
  37. use App\StartPlatz\Bundle\MemberBundle\Entity\Team;
  38. use App\StartPlatz\Bundle\WebsiteBundle\MenuTranslationService;
  39. use App\StartPlatz\Bundle\WebsiteBundle\Utility\Utility;
  40. use DateTime;
  41. use Doctrine\ORM\EntityManagerInterface;
  42. use Psr\Log\LoggerInterface;
  43. use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
  44. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  45. use Symfony\Component\HttpFoundation\Request;
  46. use Symfony\Component\HttpFoundation\Response;
  47. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  48. use Symfony\Component\Routing\Annotation\Route;
  49. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  50. class EventRegistrationController extends AbstractController
  51. {
  52.     public function __construct(
  53.         private readonly SessionInterface $session,
  54.         private readonly MailService $mailService,
  55.         private readonly LoginService $loginService,
  56.         private readonly LoggerInterface $logger,
  57.         private readonly EventNotificationService $eventNotificationService,
  58.         private readonly EventPaymentService $eventPaymentService,
  59.         private readonly EventRegistrationService $eventRegistrationService,
  60.         private readonly EventViewHelper $eventViewHelper,
  61.         private readonly EntityManagerInterface $entityManager,
  62.         private readonly MenuTranslationService $menuTranslationService,
  63.     ) {
  64.     }
  65.     #[Route('/signup/{slug}'name'signup_show_single')]
  66.     public function signupSingleAction(Request $request$slug)
  67.     {
  68.         $em $this->entityManager;
  69.         /** @var Program $program */
  70.         if (!$program $em->getRepository(Program::class /*Startup Program*/)->findOneBy(['slug' => 'sign-up'])) {
  71.             $this->session->getFlashBag()->add('notice''ERROR Sorry, no program found');
  72.             return $this->redirect($this->generateUrl('apply_home', []));
  73.         }
  74.         /** @var Batch $batch */
  75.         if (!$batch $em->getRepository(Batch::class)->findOneBy(['slug' => $slug'program' => $program])) {
  76.             $this->session->getFlashBag()->add('notice''ERROR Sorry, no batch found');
  77.             return $this->redirect($this->generateUrl('apply_home', []));
  78.         }
  79.         // Retrieve the UTM parameters from the URL
  80.         $utmParameters = [
  81.             'utmSource'   => $request->query->get('utm_source'),
  82.             'utmMedium'   => $request->query->get('utm_medium'),
  83.             'utmCampaign' => $request->query->get('utm_campaign'),
  84.             'utmTerm'     => $request->query->get('utm_term'),
  85.             'utmContent'  => $request->query->get('utm_content'),
  86.         ];
  87.         $lang 'DE';
  88.         if ($targetPath $request->server->get('REDIRECT_URL')) {
  89.             $lang $this->menuTranslationService->getLang($targetPath);
  90.         }
  91.         $batchIsOpen $batch && $em->getRepository(Batch::class)->isBatchApplicationOpen($batch);
  92.         $form    null;
  93.         $program null;
  94.         $member  null;
  95.         $application null;
  96.         $product     null;
  97.         $editFeedback "";
  98.         $settings     = [];
  99.         $action       "";
  100.         $customersArr = [];
  101.         if ($batchIsOpen) {
  102.             $program $batch->getProgram();
  103.             $settings $this->eventViewHelper->getProgramSettings($program$batch);
  104.             $batch $em->getRepository(Batch::class)->updateNumbers($batch);
  105.             /** @var User $user */
  106.             if ($user $this->getUser()) {
  107.                 $member $em->getRepository(Member::class)->find($user->getMemberId());
  108.             }
  109.             $action   $request->get('action');
  110.             $application $this->eventRegistrationService->getOrCreateApplication($request$batch$program$member$lang$settings$utmParametersnull);
  111.             if ($batch->getPriceInEuroCent() > 0) {
  112.                 $route                   "signup_show_single";
  113.                 $routeParameters["slug"] = $batch->getSlug();
  114.                 $application $this->handleAction($request$member$application$batch$lang$route$routeParameters$settings);
  115.                 if (is_string($application)) {
  116.                     // Handle redirect
  117.                     return $this->redirect($application);
  118.                 }
  119.             }
  120.             if ($user && in_array($action, ["needsloginvalidation""needsemailverification"])) {
  121.                 $response $this->validateEventApplication($application$member$settings$batch$lang);
  122.                 $application $response['data']['application'];
  123.             }
  124.             if ($batch->getPriceInEuroCent() > && $batch->getProductId()) {
  125.                 if (!$product $em->getRepository(Product::class /*Product Member*/)->find($batch->getProductId())) {
  126.                     $this->session->getFlashBag()->add('notice''ERROR configuration not found');
  127.                     return $this->redirect($this->generateUrl('events_list'));
  128.                 }
  129.                 if ($application->getId() && isset($member) && $member->getId()) {
  130.                     $customersArr $em->getRepository(Customer::class)->findValidMonsumCustomersByMemberAndAccount($member$product->getAccount(), false);
  131.                 }
  132.             }
  133.             $form $this->createForm(ApplicationType::class, $application, ['settings' => $settings'program' => $program'batch' => $batch]);
  134.             $form->handleRequest($request);
  135.             ##############################################################
  136.             ##   form validation starts here                            ##
  137.             ##############################################################
  138.             if ($form->isSubmitted() && $form->isValid()) {
  139.                 $application $em->getRepository(Application::class)->cleanApplicationData($application);
  140.                 $adminEmail  $application->getEmail();
  141.                 if ($member) {
  142.                     $memberToBeRegistered $member;
  143.                     $isExistingMember     true;
  144.                 } else {
  145.                     $response $this->eventRegistrationService->resolveOrCreateMember($application);
  146.                     $isExistingMember     $response['data']['isExistingMember'];
  147.                     $memberToBeRegistered $response['data']['memberToBeRegistered'];
  148.                 }
  149.                 $memberToBeRegistered->setIsExistingMember($isExistingMember);
  150.                 // fill application with memberId and teamId and discount information
  151.                 $application $this->eventRegistrationService->bindMemberToApplication($application$memberToBeRegistered$batch);
  152.                 if (!$batch->isMultipleApplicationsPerMember()) {
  153.                     // check if there is already an application to this event == batch with that batchId and memberId
  154.                     if ($existingApplication $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $memberToBeRegistered->getId()])) {
  155.                         // there are two applications - the latest application will always win
  156.                         $fields     Utility::getEntityFieldsArray($application, ['id','assessments''votesRegistered''applicationStatus''editStatus''editMessage''lastChangeUser','history','extraFields','lastModified','createdAt']);
  157.                         $storedData Utility::fillDataByObject($existingApplication$fields);
  158.                         $updatedData Utility::fillDataByObject($application$fields);
  159.                         $differences array_diff_assoc($updatedData$storedData);
  160.                         $application $existingApplication;
  161.                         $application $this->updateApplication($application$differences);
  162.                         $application->setIsExistingApplication(true);
  163.                     }
  164.                 }
  165.                 if (isset($settings['extraFields'])) {
  166.                     $application $this->eventViewHelper->getExtraFieldsData($application$settings$form);
  167.                 }
  168.                 $dummyEvent = new Event();
  169.                 $isPaidEvent $this->eventPaymentService->isPaidEvent($application$batch$dummyEvent);
  170.                 $hasToPay    $this->eventPaymentService->isPaymentRequired($application$memberToBeRegistered$dummyEvent$settings$batch);
  171.                 if ($isPaidEvent && $hasToPay) {
  172.                     // falls es bereits einen zugänglichen Monsum Account für diese Person gibt
  173.                     $customersArr $em->getRepository(Customer::class)->findValidMonsumCustomersByMemberAndAccount($memberToBeRegistered$product->getAccount(), false);
  174.                     $dummyEvent = new Event();
  175.                     $application  $this->eventPaymentService->updateApplicationWithStepOneInfo($application$memberToBeRegistered$dummyEvent);
  176.                     // Application in Datenbank schreiben, sonst kann im nächsten Schritt keine appId übergeben werden
  177.                     $application $em->getRepository(Application::class)->setApplication($application);
  178.                 } else {
  179.                     $route 'signup_show_single';
  180.                     $routeParameters $request->get('_route_params');
  181.                     $response $this->eventRegistrationService->processRegistrationWithoutPayment($isExistingMember$user$memberToBeRegistered$application$settings$lang$adminEmail$batch$route$routeParameters);
  182.                     // Bei SignUps: Prüfe ob goToPage gesetzt ist
  183.                     if ($batch->getGoToPage()) {
  184.                         // Weiterleitung zu goToPage (kann URL oder interner Pfad sein)
  185.                         return $this->redirect($batch->getGoToPage());
  186.                     }
  187.                     // Ansonsten auf SignUp-Seite bleiben - keine Weiterleitung
  188.                     $application  $response['data']['application'];
  189.                     $member       $response['data']['member'];
  190.                 }
  191.             }
  192.         }
  193.         // Default to legacy template for backward compatibility
  194.         $view '@StartPlatzEventBundle/Default/signup-single.html.twig';
  195.         // Check for pageTemplate field (new approach)
  196.         if ($batch->getPageTemplate()) {
  197.             switch ($batch->getPageTemplate()) {
  198.                 case 'modern':
  199.                 case 'flexible':
  200.                     $view '@StartPlatzEventBundle/Default/signup-single.modern.html.twig';
  201.                     break;
  202.                 case 'petition':
  203.                     $view '@StartPlatzEventBundle/Default/signup-single.deepSeekPetition.html.twig';
  204.                     break;
  205.                 case 'legacy':
  206.                 default:
  207.                     $view '@StartPlatzEventBundle/Default/signup-single.html.twig';
  208.                     break;
  209.             }
  210.         }
  211.         // Keep backward compatibility for existing petition SignUps using JSON settings
  212.         elseif (array_key_exists('signupTemplate'$settings)) {
  213.             $view '@StartPlatzEventBundle/Default/signup-single.deepSeekPetition.html.twig';
  214.         }
  215. // Build the metaData array using only the $batch object (landing page fields)
  216.         $metaData = [
  217.             // SEO Tag: Description (using landingPageDescription)
  218.             'description'       => $batch->getLandingPageDescription(),
  219.             // Canonical URL (the domain remains lowercase)
  220.             'canonical'         => 'https://www.startplatz.de/signup/' urlencode($batch->getSlug()),
  221.             // Open Graph Tags for Social Sharing (using landingPage fields)
  222.             'ogTitle'           => $batch->getLandingPageTitle(),
  223.             'ogDescription'     => $batch->getLandingPageDescription(),
  224.             'ogImage'           => $batch->getLandingPageSocialImage(),
  225.             'ogUrl'             => 'https://www.startplatz.de/signup/' urlencode($batch->getSlug()),
  226.             'ogType'            => 'website',
  227.             'ogSiteName'        => 'STARTPLATZ'// Protected trademark must be uppercase
  228.             // Twitter Card Tags (using landingPage fields)
  229.             'twitterCard'       => 'summary'// or "summary_large_image" if preferred
  230.             'twitterTitle'      => $batch->getLandingPageTitle(),
  231.             'twitterDescription'=> $batch->getLandingPageDescription(),
  232.             'twitterImage'      => $batch->getLandingPageSocialImage(),
  233.             'twitterSite'       => '@STARTPLATZ'// Protected trademark must be uppercase
  234.             // Title for the page (taken from landingPageTitle)
  235.             'title'             => $batch->getLandingPageTitle(),
  236.             // Additional Landing Page Fields
  237.             'landingPageCss'            => $batch->getLandingPageCss(),
  238.             'landingPageContent'        => $batch->getLandingPageContent(),
  239.             'landingPageJs'             => $batch->getLandingPageJs(),
  240.             'landingPageFooterTemplate' => $batch->getLandingPageFooterTemplate(),
  241.         ];
  242.         return $this->render($view, [
  243.             'batch'          => $batch,
  244.             'program'        => $program,
  245.             'settings'       => $settings,
  246.             'metaData'       => $metaData,
  247.             'form'           => $form $form->createView() : $form,
  248.             'application'    => $application,
  249.             'action'         => $action,
  250.             'batchIsOpen'    => $batchIsOpen,
  251.             'customersArr'   => $customersArr,
  252.             'member'         => $member,
  253.             'isMultiBatchEvent' => false,  // Signups have no event, so no multi-batch
  254.             'utmParameters'  => $utmParameters,
  255.             'phrases'        => $em->getRepository(Option::class)->getApplicationPhrases($program$batchfalse$lang),
  256.             'editFeedback'   => $editFeedback,
  257.             'templateVars'   => [],
  258.             'preview'             => $request->get('preview'),
  259.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  260.             'lang'            => $lang,
  261.             'targetRoute'     => "signup_show_single",
  262.             'targetRouteSlug' => $batch->getSlug(),
  263.             'targetPath'      => $targetPath,
  264.         ]);
  265.     }
  266.     ### section signup ends here ###
  267.     #[Route('/allmeda/event/application/send-confirmation-mail/{applicationId}'name'allmeda_event_send-confirmation-mail')]
  268.     /**
  269.      * @IsGranted("ROLE_ADMIN")
  270.      */
  271.     public function sendConfirmationMailViaPublicFunction($applicationId)
  272.     {
  273.         $em $this->entityManager;
  274.         $application $em->getRepository(Application::class)->find($applicationId);
  275.         $member $em->getRepository(Member::class)->find($application->getMemberId());
  276.         $batch $em->getRepository(Batch::class)->find($application->getBatchId());
  277.         $feedback $this->eventNotificationService->sendConfirmationMail(null$member, [], $application$batch);
  278.         $response "SUCCESS Confirmation Mail has been sent";
  279.         return new Response($response);
  280.     }
  281.     #[Route('/event-confirmation/{slug}/'name'event_confirmation')]
  282.     public function eventConfirmationAction(Request $request$slug)
  283.     {
  284.         $em $this->entityManager;
  285.         $event $this->findEventBySlug($slug);
  286.         if (!$event) {
  287.             $this->addFlash('notice''ERROR Event not found');
  288.             return $this->redirectToRoute('events_list');
  289.         }
  290.         $seriesEvents = [];
  291.         if ($seriesTag $event->getSeriesTag()) {
  292.             /** @var EventRepository $eventRepository */
  293.             $eventRepository $em->getRepository(Event::class);
  294.             $seriesEvents $eventRepository->findBySeriesTag($seriesTag'', ['startDate' => 'DESC']);
  295.         }
  296.         $embed $request->get('embed');
  297.         $batch $em->getRepository(Batch::class)->findOneBy(['programId'=>27'eventId'=>$event->getId(), 'status'=>'active''access' =>'anonymous']);
  298.         if (!$batch) {
  299.             $this->addFlash('notice''ERROR No active batch found for this event');
  300.             return $this->redirectToRoute('events_list');
  301.         }
  302.         $program $batch->getProgram();
  303.         $action   $request->get('action');
  304.         $applicationId $request->get('app');
  305.         $route                   "event_confirmation";
  306.         $routeParameters["slug"] = $event->getSlug();
  307.         $settings = [];
  308.         $member   false;
  309.         $application = new Application();
  310.         // Load application first, then determine language
  311.         if ($applicationId){
  312.             if (!$application $em->getRepository(Application::class)->find($applicationId)){
  313.                 $this->addFlash('notice''ERROR Application with ID '.$applicationId.' not found');
  314.                 return $this->redirectToRoute('events_list');
  315.             }
  316.         }
  317.         // Language detection: Priority is Application > Event > Default DE
  318.         // This ensures user's language choice persists through Stripe redirect
  319.         $lang = ($application && $application->getId() && $application->getLang())
  320.             ? $application->getLang()
  321.             : ($event->getLang() ?? 'DE');
  322.         // Handle action after language is determined
  323.         if ($applicationId && $action){
  324.             $application $this->handleAction($request$member$application$batch$lang$route$routeParameters$settings);
  325.             if (is_string($application)) {
  326.                 // Handle redirect
  327.                 return $this->redirect($application);
  328.             }
  329.         }
  330.         // Generate the absolute base URL based on the route and parameters
  331.         $baseURL $this->generateUrl('event_show_single', ['slug' => $slug], UrlGeneratorInterface::ABSOLUTE_URL);
  332.         // UTM parameters for the promotion URL
  333.         $promotionUtmParameters = [
  334.             'utm_source'   => 'affiliate',
  335.             'utm_medium'   => is_numeric($application->getMemberId()) ? $application->getMemberId() : '',
  336.             'utm_campaign' => 'event:' $event->getId(),
  337.             'utm_term'     => '',
  338.             'utm_content'  => '',
  339.         ];
  340.         $promotionUrl $em->getRepository(Batch::class)->generatePromotionUrl($baseURL$promotionUtmParameters);
  341.         $view '@StartPlatzEventBundle/Default/event-confirmation.html.twig';
  342.         if ($embed){
  343.             $view '@StartPlatzEventBundle/Default/event-confirmation.embed.html.twig';
  344.         }
  345.         if ($event->getTwigFile() == 'winter-2025' ){
  346.             $view '@StartPlatzEventBundle/Default/event-confirmation.winter-2025.html.twig';
  347.         }
  348.         if ($event->getTwigFile() == 'style-ecodynamics' ){
  349.             $view '@StartPlatzEventBundle/Default/event-confirmation.style-ecodynamics.html.twig';
  350.         }
  351.         if ($event->getTwigFile() == 'style-ai-hub' ){
  352.             $view '@StartPlatzEventBundle/Default/event-confirmation.style-ai-hub.html.twig';
  353.         }
  354.         if ($event->getTwigFile() == 'style-startplatz' ){
  355.             $view '@StartPlatzEventBundle/Default/event-confirmation.style-ai-hub.html.twig';
  356.         }
  357.         return $this->render($view, [
  358.             'event'         => $event,
  359.             'seriesEvents'  => $seriesEvents,
  360.             'promotionUrl'   => $promotionUrl,
  361.             'batch'          => $batch,
  362.             'settings'       => $settings,
  363.             'application'    => $application,
  364.             'action'         => $action,
  365.             'member'         => $member,
  366.             'speakers'       => $this->eventViewHelper->getSpeakers($event),
  367.             'phrases'        => $em->getRepository(Option::class)->getApplicationPhrases($program$batchfalse$lang),
  368.             'templateVars'   => [],
  369.             'embed'          => $embed,
  370.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  371.             'lang'            => $lang,
  372.             'isMultiBatchEvent' => $event->isMultiBatchEvent(),
  373.         ]);
  374.     }
  375.     #[Route('/event/{slug}/'name'event_show_single')]
  376.     #[Route('/event/{slug}/{date}'name'event_show_single_by_date'requirements: [])]
  377.     public function eventSingleAction(Request $request$slug$date null)
  378.     {
  379.         ##event-single
  380.         if ($slug == 'ai-bootcamp-koeln-2024-01-18') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-bootcamp-koeln-2024-01-29')) );
  381.         if ($slug == 'ai-summer-school-koeln-2208') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-bootcamp-koeln-1109')) );
  382.         if ($slug == 'ai-summer-school-duesseldorf-2908') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-bootcamp-duesseldorf-2609')) );
  383.         if ($slug == 'ai-disruptme-marketing-in-unternehmen-koeln-2023-10-19') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-disruptme-marketing-in-unternehmen-koeln-2024-01-31')) );
  384.         if ($slug == 'ai-disruptme-publisher-koeln-2023-10-18') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-disruptme-publisher-duesseldorf-2024-03-20')) );
  385.         if ($slug == 'ai-disruptme-travel-tourismus-duesseldorf-2023-11-09') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-disruptme-travel-tourismus-duesseldorf-2024-02-22')) );
  386.         if ($slug == 'genai-ki-app-no-code-backend-fuer-ki-anwendung-2024-05-28') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'genai-ki-anwendung-bauen-frontend-ux-ui-2024-05-21')) );
  387.         if ($slug == 'ai-barcamp-generative-ki') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ai-barcamp-logistik')) );
  388.         if ($slug == 'genai-generative-ki-zertifikat-ki-kompetenz-erwerben-2025-05-20') return $this->redirect$this->generateUrl('event_show_single', array('slug' => 'ki-kompetenzpflicht-zertifikat-artikel-4-2025-05-20')) );
  389.         // Retrieve the UTM parameters from the URL
  390.         $utmParameters = [
  391.             'utmSource'   => $request->query->get('utm_source') ?? 'homepage',
  392.             'utmMedium'   => $request->query->get('utm_medium'),
  393.             'utmCampaign' => $request->query->get('utm_campaign'),
  394.             'utmTerm'     => $request->query->get('utm_term'),
  395.             'utmContent'  => $request->query->get('utm_content'),
  396.         ];
  397.         $em $this->entityManager;
  398.         /** @var Event $event */
  399.         $event $this->findEventBySlug($slug);
  400.         if (!$event) {
  401.             $this->addFlash('notice''ERROR Veranstaltung nicht gefunden');
  402.             return $this->redirectToRoute('events_list');
  403.         }
  404.         // Handle event visibility: redirect if not published or previewed without user session.
  405.         $response $this->handleEventStatus($event$request);
  406.         if ($response) {
  407.             return $response;
  408.         }
  409.         $seriesEvents = [];
  410.         if ($seriesTag $event->getSeriesTag()) {
  411.             /** @var EventRepository $eventRepository */
  412.             $eventRepository $em->getRepository(Event::class);
  413.             $seriesEvents $eventRepository->findBySeriesTag($seriesTag'', ['startDate' => 'DESC']);
  414.         }
  415.         // Language detection with priority: URL parameter > Event default
  416.         // This ensures ?lang=en parameter works for bilingual events
  417.         $lang strtoupper($request->query->get('lang''')) ?: ($event->getLang() ?? 'DE');
  418.         $embed $request->get('embed');
  419.         $eventbriteId   $event->getEventbriteId();
  420.         $isEventbriteOnly $event->getTicketing() > '';
  421.         $applicationUrl $event->getApplicationUrl();
  422.         // Multi-batch aware logic
  423.         $futureBatches = [];
  424.         $allBatches = [];
  425.         if ($event->isMultiBatchEvent()) {
  426.             $allBatches $em->getRepository(Batch::class)->findAllBatchesByEvent($event);
  427.             if ($date) {
  428.                 // User selected a specific date
  429.                 $batch $em->getRepository(Batch::class)->findBatchByEventAndDate($event$date);
  430.                 if (!$batch) {
  431.                     // Invalid date, redirect to main event page
  432.                     $this->addFlash('notice''Der gewählte Termin ist nicht verfügbar.');
  433.                     return $this->redirectToRoute('event_show_single', ['slug' => $slug]);
  434.                 }
  435.             } else {
  436.                 // No date selected, show next upcoming batch
  437.                 $batch $em->getRepository(Batch::class)->findNextOpenBatchByEvent($event);
  438.             }
  439.             // Get all future batches for the selector
  440.             $futureBatches $em->getRepository(Batch::class)->findFutureBatchesByEvent($event10);
  441.             // Get past batches for archive display
  442.             $pastBatches $em->getRepository(Batch::class)->findPastBatchesByEvent($event10);
  443.         } else {
  444.             // Single batch event - keep existing logic
  445.             // Try to find active batch first, then fall back to completed batch for display
  446.             $batch $em->getRepository(Batch::class)->findOneBy(['programId'=>27'eventId'=>$event->getId(), 'status'=>'active''access' =>'anonymous']);
  447.             if (!$batch) {
  448.                 // No active batch found, look for completed batch (for past events that need to display)
  449.                 $batch $em->getRepository(Batch::class)->findOneBy(['programId'=>27'eventId'=>$event->getId(), 'status'=>'completed''access' =>'anonymous']);
  450.             }
  451.             if ($batch) {
  452.                 $allBatches = [$batch];
  453.             }
  454.         }
  455.         $batchIsOpen $batch && $em->getRepository(Batch::class)->isBatchApplicationOpen($batch);
  456.         $form    null;
  457.         $program null;
  458.         $application null;
  459.         $product     null;
  460.         $editFeedback "";
  461.         $settings     = [];
  462.         $action       "";
  463.         $customersArr = [];
  464.         $member null;
  465.         /** @var User $user */
  466.         if ($user $this->getUser()) {
  467.             $member $em->getRepository(Member::class)->find($user->getMemberId());
  468.         }
  469.         if ($batch && !$isEventbriteOnly) {
  470.             $program $batch->getProgram();
  471.         }
  472.         if ($batchIsOpen && !$isEventbriteOnly) {
  473.             $settings $this->eventViewHelper->getProgramSettings($program$batch);
  474.             $batch $em->getRepository(Batch::class)->updateNumbers($batch);
  475.             $action   $request->get('action');
  476.             $referral $request->get('referral');
  477.             // do some exceptions
  478.             if ($member && isset($settings['batchType'])) {
  479.                 if ($settings['batchType'] == 'memberDocumentation'){
  480.                     // check if member
  481.                     if ($em->getRepository(Application::class)->findApplicationsByAiFridayBatchSlugAndMemberId($member->getId()))
  482.                     {
  483.                         if (!$em->getRepository(Application::class)->findOneBy(['memberId'=>$member->getId(), 'batchId' => $batch->getId()])){
  484.                             $memberTeam  $em->getRepository(Team::class)->find($member->getTeamId());
  485.                             $adminEmail  $member->getEmail();
  486.                             $application $em->getRepository(Application::class)->createApplicationIfNotExists($program$batch$memberTeam$member$adminEmail);
  487.                             $application->setBatchStatus('applicant');
  488.                             $application->setApplicationStatus('applied');
  489.                             $em->persist($application);
  490.                             $em->flush();
  491.                             $batch $em->getRepository(Batch::class)->updateNumbers($batch);
  492.                         }
  493.                     }
  494.                 }
  495.             }
  496.             $application $this->eventRegistrationService->getOrCreateApplication($request$batch$program$member$lang$settings$utmParameters$event);
  497.             if ($referral && $application->getRealPriceinEuroCent() > 0) {
  498.                 if ($recommendedByMember $em->getRepository(Member::class)->find($referral)) {
  499.                     if ($neededTag $settings['isRecommendedBy'] ?? '') {
  500.                         if (Utility::contains($recommendedByMember->getTags(), $neededTag)) {
  501.                             $application->setRealPriceinEuroCent(0);
  502.                             $application->setDiscountReason("geworben durch {$recommendedByMember->getName()}");
  503.                             $application->setDiscountPercent(100);
  504.                         }
  505.                     }
  506.                 }
  507.             }
  508.             if ($user && in_array($action, ["needsloginvalidation""needsemailverification"])) {
  509.                 $response $this->validateEventApplication($application$member$settings$batch$lang);
  510.                 $application $response['data']['application'];
  511.             }
  512.             // Only check for Monsum product if we're not using Stripe
  513.             $hasStripeConfig = ($batch && $batch->getStripePriceId()) || ($event && $event->getStripePriceId());
  514.             if ($batch->getPriceInEuroCent() > && $batch->getProductId() && !$hasStripeConfig) {
  515.                 if (!$product $em->getRepository(Product::class /*Product Member*/)->find($batch->getProductId())) {
  516.                     $this->session->getFlashBag()->add('notice''ERROR configuration not found');
  517.                     return $this->redirect($this->generateUrl('events_list'));
  518.                 }
  519.             }
  520.             $form $this->createForm(ApplicationType::class, $application, [
  521.                 'settings' => $settings,
  522.                 'program' => $program,
  523.                 'batch' => $batch,
  524.                 'csrf_protection' => $embed false true,
  525.                 ]);
  526.             $form->handleRequest($request);
  527.             ##############################################################
  528.             ##   form validation starts here                            ##
  529.             ##############################################################
  530.             if ($form->isSubmitted() && $form->isValid()) {
  531.                 $application $em->getRepository(Application::class)->cleanApplicationData($application);
  532.                 $adminEmail $application->getEmail();
  533.                 $email $application->getEmail();
  534.                 if (!Utility::validateEmail($email)){
  535.                     $this->addFlash('notice''ERROR Incorrect email adress. Please check your email adress!');
  536.                 } else {
  537.                     // maxApplicants
  538.                     if (isset($settings['maxApplicants'])) {
  539.                         $applicants $em->getRepository(Application::class)->findBy(['batchId' => $batch->getId(), 'applicationStatus' => 'applied']);
  540.                         $currentApplicantsCount count($applicants);
  541.                         if ($currentApplicantsCount >= $settings['maxApplicants']) {
  542.                             $this->addFlash('notice''ERROR Leider sind alle verfügbaren Plätze bereits vergeben.');
  543.                             return $this->redirectToRoute('event_show_single', ['slug' => $slug]);
  544.                         }
  545.                     }
  546.                     // isErrorDetectionOn
  547.                     if (isset($settings['isErrorDetectionOn']) and $settings['isErrorDetectionOn']) {
  548.                         $this->addFlash('notice''ERROR There was a problem in processing your application. Please contact support@startplatz.de');
  549.                         return $this->redirectToRoute('event_show_single', ['slug' => $slug]);
  550.                     }
  551.                     if ($member) {
  552.                         $memberToBeRegistered $member;
  553.                         $isExistingMember true;
  554.                     } else {
  555.                         $response $this->eventRegistrationService->resolveOrCreateMember($application);
  556.                         $isExistingMember $response['data']['isExistingMember'];
  557.                         $memberToBeRegistered $response['data']['memberToBeRegistered'];
  558.                     }
  559.                     $memberToBeRegistered->setIsExistingMember($isExistingMember);
  560.                     // fill application with memberId and teamId and discount information
  561.                     $application $this->eventRegistrationService->bindMemberToApplication($application$memberToBeRegistered$batch);
  562.                     if (!$batch->isMultipleApplicationsPerMember()) {
  563.                         // check if there is already an application to this event == batch with that batchId and memberId
  564.                         if ($existingApplication $em->getRepository(Application::class)->findOneBy(['batchId' => $batch->getId(), 'memberId' => $memberToBeRegistered->getId()])) {
  565.                             // there are two applications - the latest application will always win
  566.                             $fields Utility::getEntityFieldsArray($application, ['id''assessments''votesRegistered''applicationStatus''editStatus''editMessage''lastChangeUser''history''extraFields''lastModified''createdAt']);
  567.                             $storedData Utility::fillDataByObject($existingApplication$fields);
  568.                             $updatedData Utility::fillDataByObject($application$fields);
  569.                             $differences array_diff_assoc($updatedData$storedData);
  570.                             $application $existingApplication;
  571.                             $application $this->updateApplication($application$differences);
  572.                             $application->setIsExistingApplication(true);
  573.                         }
  574.                     }
  575.                     if (isset($settings['extraFields'])) {
  576.                         $application $this->eventViewHelper->getExtraFieldsData($application$settings$form);
  577.                     }
  578.                     $isPaidEvent $this->eventPaymentService->isPaidEvent($application$batch$event);
  579.                     // Debug logging for ki-campus payment exception
  580.                     $this->logger->info('Payment exception check', [
  581.                         'member_exists' => $member true false,
  582.                         'member_id' => $member $member->getId() : null,
  583.                         'member_email' => $member $member->getEmail() : null,
  584.                         'member_tags' => $member $member->getTags() : null,
  585.                         'memberToBeRegistered_id' => $memberToBeRegistered->getId(),
  586.                         'memberToBeRegistered_email' => $memberToBeRegistered->getEmail(),
  587.                         'memberToBeRegistered_tags' => $memberToBeRegistered->getTags(),
  588.                         'batch_isFreeForKiCampus' => $batch $batch->getIsFreeForKiCampus() : null,
  589.                         'event_isFreeForKiCampus' => $event $event->getIsFreeForKiCampus() : null,
  590.                     ]);
  591.                     // Always use memberToBeRegistered for payment exception check
  592.                     $hasToPay $this->eventPaymentService->isPaymentRequired($application$memberToBeRegistered$event$settings$batch);
  593.                     if ($isPaidEvent && $hasToPay) {
  594.                         $application $this->eventPaymentService->updateApplicationWithStepOneInfo($application$memberToBeRegistered$event);
  595.                         $isAdmin $user && $user->getIsAdmin();
  596.                         $paymentLink $this->eventPaymentService->createStripeCheckoutUrl($application$memberToBeRegistered$event$batch$embed$isAdmin);
  597.                         if (is_string($paymentLink)) {
  598.                             // call stripe and let applicant pay for the ticket
  599.                             if ($embed){
  600.                                 return $this->render('@StartPlatzEventBundle/Default/iframe_redirect.html.twig', [
  601.                                     'targetUrl' => $paymentLink
  602.                                 ]);
  603.                             } else {
  604.                                 return $this->redirect($paymentLink);
  605.                             }
  606.                         } elseif ($paymentLink === null) {
  607.                             // No Stripe config — check Monsum
  608.                             if ($product) {
  609.                                 $customersArr $em->getRepository(Customer::class)->findValidMonsumCustomersByMemberAndAccount($memberToBeRegistered$product->getAccount(), false);
  610.                             }
  611.                         }
  612.                         // paymentLink === false: Stripe was configured but failed — fall through to render page
  613.                         // Application in Datenbank schreiben, sonst kann im nächsten Schritt keine appId übergeben werden
  614.                         $application $em->getRepository(Application::class)->setApplication($application);
  615.                     } else {
  616.                         $route 'event_show_single';
  617.                         $routeParameters $request->get('_route_params');
  618.                         if (!array_key_exists('slug'$routeParameters)) {
  619.                             $routeParameters['slug'] = $event->getSlug();
  620.                         }
  621.                         $response $this->eventRegistrationService->processRegistrationWithoutPayment($isExistingMember$user$memberToBeRegistered$application$settings$lang$adminEmail$batch$route$routeParameters);
  622.                         if ($response['status'] == 'error') {
  623.                             $targetPath $response['data']['targetPath'];
  624.                             return $this->redirect($targetPath);
  625.                         }
  626.                         $application $response['data']['application'];
  627.                         $member $response['data']['member'];
  628.                         if ($this->shouldSetAiHubMembership($settings$application->getApplicationStatus())) {
  629.                             $aiHubStatus $settings["setAiHubMembership"];
  630.                             $service $this->configureServiceForAiHub($aiHubStatus$event->getStartDate());
  631.                             $reason "participated in event with id={$event->getId()} and title={$event->getTitle()}";
  632.                             $em->getRepository(Member::class)->setAiHubMembership($service$member$reason);
  633.                         }
  634.                     }
  635.                 }
  636.             }
  637.         }
  638.         $promotionUrl null;
  639.         if ($user && $batch) {
  640.             // Generate the absolute base URL based on the route and parameters
  641.             $baseURL $this->generateUrl('event_show_single', ['slug' => $slug], UrlGeneratorInterface::ABSOLUTE_URL);
  642.             // UTM parameters for the promotion URL
  643.             $promotionUtmParameters = [
  644.                 'utm_source' => 'affiliate',
  645.                 'utm_medium' => $member->getId(),
  646.                 'utm_campaign' => 'event:' $event->getId(),
  647.                 'utm_term'     => '',
  648.                 'utm_content'  => '',
  649.             ];
  650.             $promotionUrl $em->getRepository(Batch::class)->generatePromotionUrl($baseURL$promotionUtmParameters);
  651.         }
  652.         // Überprüfen Sie den Wert des Teaserfeldes, um showTeaser zu setzen
  653.         $showTeaser = !empty($event->getTeaser());
  654.         $paymentLink false;
  655.         $view '@StartPlatzEventBundle/Default/event-single.html.twig';
  656.         if ($event->isSinglePage() or $embed){
  657.             $view '@StartPlatzEventBundle/Default/event-single.lp.html.twig';
  658.         }
  659.         if ($event->getTwigFile() == 'winter-2025' ){
  660.             $view '@StartPlatzEventBundle/Default/event-single.winter-2025.html.twig';
  661.         }
  662.         if ($event->getTwigFile() == 'style-ecodynamics' ){
  663.             $view '@StartPlatzEventBundle/Default/event-single.style-ecodynamics.html.twig';
  664.         }
  665.         if ($event->getTwigFile() == 'style-ai-hub' ){
  666.             $view '@StartPlatzEventBundle/Default/event-single.style-ai-hub.html.twig';
  667.         }
  668.         if ($event->getTwigFile() == 'style-startplatz' ){
  669.             $view '@StartPlatzEventBundle/Default/event-single.style-startplatz.html.twig';
  670.         }
  671.         if ($event->getTwigFile() == 'tailwind' ){
  672.             $view '@StartPlatzEventBundle/Default/event-single.tailwind.html.twig';
  673.         }
  674.         return $this->render($view, [
  675.             'event'         => $event,
  676.             'seriesEvents'  => $seriesEvents,
  677.             'showTeaser'    => $showTeaser,
  678.             'utmParameters'  => $utmParameters,
  679.             'promotionUrl'   => $promotionUrl,
  680.             'eventbriteId'   => $eventbriteId,
  681.             'isEventbriteOnly' => $isEventbriteOnly,
  682.             'applicationUrl' => $applicationUrl,
  683.             'batch'          => $batch,
  684.             'settings'       => $settings,
  685.             'form'           => $form $form->createView() : $form,
  686.             'application'    => $application,
  687.             'action'         => $action,
  688.             'batchIsOpen'    => $batchIsOpen,
  689.             'customersArr'   => $customersArr,
  690.             'member'         => $member,
  691.             'paymentLink'    => $paymentLink,
  692.             // Multi-batch event support
  693.             'futureBatches'  => $futureBatches,
  694.             'pastBatches'    => $pastBatches ?? [],
  695.             'allBatches'     => $allBatches,
  696.             'selectedDate'   => $date,
  697.             'isMultiBatchEvent' => $event->isMultiBatchEvent(),
  698.             'speakers'       => $this->eventViewHelper->getSpeakers($event),
  699.             'phrases'        => $em->getRepository(Option::class)->getApplicationPhrases($program$batchfalse$lang),
  700.             'editFeedback'   => $editFeedback,
  701.             'templateVars'   => [],
  702.             'preview'             => $request->get('preview'),
  703.             'menuLinksAndPhrases' => $this->menuTranslationService->getMenuLinksAndPhrases($request->server->get('REQUEST_URI')),
  704.             'lang'           => $lang,
  705.             'isEnglish'      => $lang === 'EN',
  706.             'embed'          => $embed,
  707.             'targetRoute'     => "event_show_single",
  708.             'targetRouteSlug' => $event->getSlug(),
  709.         ]);
  710.     }
  711.     // ── Private Helpers ──────────────────────────────────────────────
  712.     private function findEventBySlug($slug)
  713.     {
  714.         /** @var EventRepository $eventRepository */
  715.         $eventRepository $this->entityManager->getRepository(Event::class);
  716.         $event $eventRepository->findOneBy(['slug' => $slug]);
  717.         if (!$event) {
  718.             $querySlug '/' $slug;
  719.             $event $eventRepository->findOneBy(['slug' => $querySlug]);
  720.             if (!$event) {
  721.                 return false;
  722.             } else {
  723.                 $em $this->entityManager;
  724.                 $event->setSlug($slug);
  725.                 $em->persist($event);
  726.                 $em->flush();
  727.             }
  728.         }
  729.         return $event;
  730.     }
  731.     private function handleEventStatus(Event $eventRequest $request)
  732.     {
  733.         // SEO-optimized handling: Archive events are displayed normally (no redirect)
  734.         // Template will show archive banner and hide registration forms
  735.         // This preserves 100% SEO juice and backlinks
  736.         if ($event->getStatus() != 'publish' and $event->getStatus() != 'archive' and !$request->get('preview')) {
  737.             // Draft and trash events require authentication
  738.             if (!$this->getUser()) {
  739.                 $this->addFlash('notice''ERROR Veranstaltung ist nicht verfügbar');
  740.                 return $this->redirectToRoute('events_list');
  741.             }
  742.         }
  743.         return null;
  744.     }
  745.     /**
  746.      * handleAction - Handles various actions for the application
  747.      *
  748.      * This function is called in the following contexts:
  749.      * 1. During Signup (signupSingleAction)
  750.      *    - With route "signup_show_single"
  751.      *    - Parameters include batch slug, language, etc.
  752.      *
  753.      * 2. During Event Handling (eventSingleAction / eventConfirmationAction)
  754.      *    - With route "event_show_single" or "event_confirmation"
  755.      *    - Parameters include event slug, language, etc.
  756.      */
  757.     private function handleAction(Request $request$memberApplication $applicationBatch $batch$lang$route$routeParameters$settings)
  758.     {
  759.         $action $request->get("action");
  760.         $em $this->entityManager;
  761.         switch ($action) {
  762.             case "checkSuccess":
  763.                 $provider $request->get('provider');
  764.                 if ($provider == 'stripe'){
  765.                     $application->setEditStatus("success:stripePaid");
  766.                 }
  767.                 $application->setApplicationStatus('applied');
  768.                 $application->setHasPaid(true);
  769.                 $application $em->getRepository(Application::class)->setApplication($application);
  770.                 switch ($lang) {
  771.                     case "DE":
  772.                         $message "Danke für Deine Anmeldung.<br>Wir freuen uns auf Dich!";
  773.                         break;
  774.                     case "EN":
  775.                         $message "Thanks, Your application is now approved.<br>We are looking forward to having you!";
  776.                         break;
  777.                 }
  778.                 if ($productNumber $request->get('productnumber') or $provider == 'stripe'){
  779.                     ## productNumber is only given in case of sign up
  780.                     switch ($lang) {
  781.                         case "DE":
  782.                             $message "Danke für Deine Buchung.<br>Wir freuen uns auf Dich!";
  783.                             break;
  784.                         case "EN":
  785.                             $message "Thanks, Your booking was successful.<br>We are looking forward to having you!";
  786.                             break;
  787.                     }
  788.                     $account      $request->get('account');
  789.                     $customerHash $request->get('customerId');
  790.                     $subscriptionHash $request->get('subscriptionId');
  791.                     if (!$member) {
  792.                         $member $em->getRepository(Member::class)->find($application->getMemberId());
  793.                     }
  794.                     if (array_key_exists("setAiHubMembership"$settings)) {
  795.                         // Hole den Status aus den Einstellungen
  796.                         $aiMembershipStatus $settings["setAiHubMembership"];
  797.                         // Setze das Start- und Enddatum basierend auf dem aktuellen Datum und dem Status
  798.                         $aiMembershipStartDate = new DateTime();  // Setze auf aktuelles Datum und Uhrzeit
  799.                         $aiMembershipEndDate = clone $aiMembershipStartDate;
  800.                         if ($aiMembershipStatus === 'trial') {
  801.                             $aiMembershipEndDate->modify('+1 month');
  802.                         } elseif ($aiMembershipStatus === 'active') {
  803.                             $aiMembershipEndDate->modify('+3 months');
  804.                         }
  805.                         // Setze den Grund für die Mitgliedschaft
  806.                         $reason "Buy of {$productNumber}";
  807.                         // Erstelle oder aktualisiere das Service-Objekt mit den neuen Daten
  808.                         // (Du müsstest diese Logik entsprechend deiner Anwendung implementieren)
  809.                         $service = new Service();  // oder hole ein existierendes Service-Objekt
  810.                         $service->setStatus($aiMembershipStatus);
  811.                         $service->setStartDate($aiMembershipStartDate);
  812.                         $service->setEndDate($aiMembershipEndDate);
  813.                         // Rufe die setAiHubMembership Funktion auf
  814.                         $successContent[] = $em->getRepository(Member::class)->setAiHubMembership($service$member$reason);
  815.                     }
  816.                     $loginLink null;
  817.                     $targetPath $this->generateUrl('x_membership_first-steps', ['productNumber' => $productNumber'account' => $account]);
  818.                     $this->session->getFlashBag()->add('notice'$message);
  819.                     if (!$user $this->getUser()) {
  820.                         $loginLink $this->loginService->getLoginLinkByTargetPath($member->getEmail(), $targetPath);
  821.                         $user $em->getRepository(User::class)->findOneBy(['memberId'=>$member->getId()]);
  822.                     }
  823.                     $feedback $this->eventNotificationService->sendConfirmationMail($usernull$settings$application$batch);
  824.                     if ($provider == 'stripe'){
  825.                         $mailContent = [
  826.                             'applicationId' => $application->getId(),
  827.                             'memberId' => $application->getMemberId(),
  828.                             'editStatus' => $application->getEditStatus(),
  829.                             'applicationStatus' => $application->getApplicationStatus(),
  830.                             'person' => $application->getPerson(),
  831.                             'email' => $application->getEmail(),
  832.                             'utmSource' => $application->getUtmSource(),
  833.                             'batchSlug' => $application->getBatchSlug(),
  834.                         ];
  835.                         $response $this->mailService->send("stripe step two {$application->getBatchId()} {$application->getEmail()}"'support@startplatz.de''Monitoring Stripe''lorenz.graef@startplatz.de''Lorenz'print_r($mailContent,true), false);
  836.                         return $application;
  837.                     } else {
  838.                         return isset($loginLink) ? $loginLink $targetPath;
  839.                     }
  840.                 } else { // if productNumber is not defined
  841.                     if (!$user $this->getUser()) {
  842.                         $user $em->getRepository(User::class)->findOneBy(['memberId'=>$member->getId()]);
  843.                     }
  844.                     $feedback $this->eventNotificationService->sendConfirmationMail($usernull$settings$application$batch);
  845.                 }
  846.                 break;
  847.             case "sendMagicLinkForPayment":
  848.                 // create LoginLink and send email
  849.                 $customerId  $request->get('customerId');
  850.                 $feedbackUrl $this->eventPaymentService->sendMagicLinkForPayment($application$batch$customerId$route$routeParameters);
  851.                 return $feedbackUrl;
  852.             case "createConsumerMonsumAccount":
  853.             case "useExistingCustomerAccount":
  854.                 $customerId  $request->get('customerId');
  855.                 $checkoutUrl $this->eventPaymentService->prepareMonsumCheckout($application$batch$action$route$routeParameters$customerId);
  856.                 return $checkoutUrl;
  857.         }
  858.         // Return the modified $application object to the main controller
  859.         return $application;
  860.     }
  861.     private function validateEventApplication(Application $applicationMember $member$settings$batchstring $lang): array
  862.     {
  863.         $em $this->entityManager;
  864.         $message "";
  865.         $status "";
  866.         // Check if a confirmation email needs to be sent
  867.         $shouldSendConfirmationMail false;
  868.         $feedback null;
  869.         switch ($application->getEditStatus()) {
  870.             case "error:validateLogin":
  871.                 $application->setEditStatus('success:validatedLogin');
  872.                 $application->setEditMessage("Application has been validated by {$member->getEmail()}");
  873.                 $application $em->getRepository(Application::class)->setHistory($application$application->getEditMessage());
  874.                 $application $em->getRepository(Application::class)->setApplication($application$member);
  875.                 $status "success";
  876.                 $shouldSendConfirmationMail true;
  877.                 break;
  878.             case "error:validateEmail":
  879.                 $application->setEditStatus('success:validatedEmail');
  880.                 $application->setEditMessage("Email of this Application has been validated by {$member->getEmail()}");
  881.                 $application $em->getRepository(Application::class)->setHistory($application$application->getEditMessage());
  882.                 if ($application->getApplicationStatus() == 'started') {
  883.                     $application->setApplicationStatus('applied');
  884.                 }
  885.                 $application $em->getRepository(Application::class)->setApplication($application$member);
  886.                 $status "success";
  887.                 $shouldSendConfirmationMail true;
  888.                 break;
  889.         }
  890.         if ($shouldSendConfirmationMail) {
  891.             // Logic to send confirmation mail
  892.             $feedback $this->eventNotificationService->sendConfirmationMail(null$member$settings$application$batch);
  893.         }
  894.         if ($status == "success") {
  895.             $application $em->getRepository(Application::class)->setApplicationApplied($application$member);
  896.             switch ($lang) {
  897.                 case "DE":
  898.                     $message "SUCCESS Danke, Deine Anmeldung ist jetzt bestätigt. Wir freuen uns auf Dich!";
  899.                     if ($feedback){
  900.                         $message .= "<br>Wir haben Dir eine Bestätigungsmail geschickt. Bitte prüfe Deinen Posteingang.";
  901.                     }
  902.                     break;
  903.                 case "EN":
  904.                     $message "SUCCESS Thanks, Your application is now approved. We are looking forward to having you!";
  905.                     if ($feedback){
  906.                         $message .= "<br>We have sent you a confirmation mail. Please check your inbox!";
  907.                     }
  908.                     break;
  909.             }
  910.             $this->session->getFlashBag()->add('notice'$message);
  911.         }
  912.         return [
  913.             'data' => [
  914.                 'application' => $application,
  915.             ],
  916.             'message' => $message,
  917.             'status' => $status,
  918.         ];
  919.     }
  920.     private function shouldSetAiHubMembership($settings$applicationStatus)
  921.     {
  922.         return array_key_exists("setAiHubMembership"$settings) && $applicationStatus === 'applied';
  923.     }
  924.     private function configureServiceForAiHub($statusDateTime $startDate)
  925.     {
  926.         $service = new Service();  // oder hole ein existierendes Service-Objekt
  927.         $endDate = clone $startDate;
  928.         if ($status === 'trial') {
  929.             $endDate->modify('+1 month');
  930.         } elseif ($status === 'active') {
  931.             $endDate->modify('+3 months');
  932.         }
  933.         $service->setStatus($status);
  934.         $service->setStartDate($startDate);
  935.         $service->setEndDate($endDate);
  936.         return $service;
  937.     }
  938.     private function updateApplication(Application $application$differences)
  939.     {
  940.         foreach ($differences as $diff => $value) {
  941.             $method 'set' ucfirst((string) $diff);
  942.             if (method_exists($application$method)) {
  943.                 $application->$method($value);
  944.             }
  945.         }
  946.         return $application;
  947.     }
  948. }