src/StartPlatz/Bundle/StartupBundle/Repository/StartupRepository.php line 2167

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace App\StartPlatz\Bundle\StartupBundle\Repository;
  3. use App\StartPlatz\Bundle\GruendungsstipendiumBundle\Entity\GsJuryAssessment;
  4. use App\StartPlatz\Bundle\MemberBundle\Entity\Team;
  5. use App\StartPlatz\Bundle\MetaBundle\Entity\Attribute;
  6. use App\StartPlatz\Bundle\MetaBundle\Entity\Tag;
  7. use App\StartPlatz\Bundle\StartupBundle\Entity\Batch;
  8. use App\StartPlatz\Bundle\StartupBundle\Entity\Program;
  9. use App\StartPlatz\Bundle\StartupBundle\Entity\StartupRelevance;
  10. use DateTime;
  11. use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
  12. use Doctrine\ORM\QueryBuilder;
  13. use App\StartPlatz\Bundle\StartupBundle\Entity\Application;
  14. use App\StartPlatz\Bundle\StartupBundle\Entity\Startup;
  15. use App\StartPlatz\Bundle\StartupBundle\Entity\StartupAttribute;
  16. use App\StartPlatz\Bundle\UserBundle\Entity\User;
  17. use App\StartPlatz\Bundle\WebsiteBundle\Utility\Utility;
  18. use Doctrine\Persistence\ManagerRegistry;
  19. /**
  20.  * StartupRepository
  21.  *
  22.  * This class was generated by the Doctrine ORM. Add your own custom
  23.  * repository methods below.
  24.  */
  25. class StartupRepository extends ServiceEntityRepository
  26. {
  27.     public function __construct(ManagerRegistry $managerRegistry)
  28.     {
  29.         parent::__construct($managerRegistryStartup::class);
  30.     }
  31.     //Connect
  32.     public function find4SearchConnectStartup()
  33.     {
  34.         $connection $this->getEntityManager()->getConnection();
  35.         $sql ="SELECT t.name, t.previousName, t.status, t.shortName, t.id, s.tags, s.slug
  36.         FROM sp_startup_profiles s, sp_teams t
  37.         WHERE s.teamId = t.id and s.visibility IN ('sp-connect', 'public')
  38.         ";
  39.         return $connection->fetchAllAssociative($sql, []);
  40.     }
  41.     public function synchronizeStartupAndTeamEntities($criteria$order = [])
  42.     {
  43.         $qb $this->getEntityManager()->createQueryBuilder();
  44.         $responseContent = [];
  45.         $qb->select('s.id as startupId, s.teamId as startupTeamId, t.id as teamId')
  46.             ->from(Startup::class, 's')
  47.             ->innerJoin(Team::class, 't''with''s.teamId = t.id')
  48.             ->where('s.teamId != t.id')
  49.         ;
  50.         if ($criteria) {
  51.             foreach($criteria as $field =>$value) {
  52.                 if ($field == 'categories' or $field == 'tags') {
  53.                     $qb->andWhere('s.'$field " LIKE '%" $value "%'");
  54.                 } elseif ($value == 'IS NULL' or $value == null) {
  55.                     $qb->andWhere('s.'$field " IS NULL");
  56.                 } elseif (is_array($value)) {
  57.                     $qb->andWhere('s.'$field " IN (:{$field})");
  58.                     $qb->setParameter($fieldarray_keys($value));
  59.                 } else {
  60.                     $qb->andWhere('s.'$field ' = :' $field);
  61.                     $qb->setParameter($field$value);
  62.                 }
  63.             }
  64.         }
  65.         $items $qb->getQuery()->getResult();
  66.         foreach ($items as $item) {
  67.             $responseContent[] = $this->setField('teamId'$item['teamId'], $item['startupId']);
  68.         }
  69.         return $responseContent;
  70.     }
  71.     /**
  72.      * Check if a team is in the current batch.
  73.      *
  74.      * @deprecated UP-024: The 'startplatz' field was removed from Startup entity.
  75.      *             This method now returns false. The concept of "currentBatch" needs
  76.      *             to be reimplemented via Participation entity if still needed.
  77.      */
  78.     public function isCurrentBatch(Team $team): bool
  79.     {
  80.         // UP-024: The 'startplatz' field was removed from Startup.
  81.         // Previously this checked for startplatz='currentBatch'.
  82.         // TODO: Reimplement via Participation entity if this functionality is still needed.
  83.         return false;
  84.     }
  85.     public function findFilterMultiple($criteria$field$delimiter)
  86.     {
  87.         $qb $this->getEntityManager()->createQueryBuilder();
  88.         $qb->select(
  89.             's.'$field
  90.         );
  91.         $qb->from(Startup::class, 's')
  92.             ->where('s.'$field " > '' ")
  93.         ;
  94.         $qb $this->setCriteria($qb$criteria);
  95.         $qb->orderBy('s.name''ASC');
  96.         $entities $qb->getQuery()->getResult();
  97.         if ($field == 'todos') {
  98.             return Utility::countJsonTags($entities$field$delimiter);
  99.         } else {
  100.             return Utility::countTags($entities$field$delimiter);
  101.         }
  102.     }
  103.     public function transferStartupIdToWinner($winnerId$looserId)
  104.     {
  105.         $connection $this->getEntityManager()->getConnection();
  106.         $responseContent = [];
  107.         $tables = [
  108.             'StartupLifecycleEvent' => 'sp_startup_lifecycle_events',
  109.             'Application'           => 'sp_applications',
  110.         ];
  111.         foreach ($tables as $key => $name) {
  112.             $sql ="UPDATE {$name} s
  113.               SET s.startupId = "$winnerId ."
  114.               WHERE s.startupId = " $looserId;
  115.             $connection->executeQuery($sql);
  116.             $responseContent[] = "{$name} {$looserId} transfered to startup {$winnerId}";
  117.             $responseContent[] = "{$sql}";
  118.         }
  119.         $tables = [
  120.             'StartupAttribute' => 'sp_startup_attributes',
  121.             'StartupRelevance' => 'sp_startup_relevance',
  122.         ];
  123.         foreach ($tables as $key => $name) {
  124.             $sql ="DELETE FROM {$name} WHERE startupId = " $looserId;
  125.             $connection->executeQuery($sql);
  126.             $responseContent[] = "{$looserId} deleted from table {$name} ";
  127.             $responseContent[] = "{$sql}";
  128.         }
  129.         $sql "DELETE FROM sp_startup_profiles WHERE id = :looserId";
  130.         $connection->executeQuery($sql, ['looserId' => $looserId]);
  131.         $responseContent[] = "{$looserId} deleted form table sp_startup_profiles";
  132.         $responseContent[] = "{$sql}";
  133.         return $responseContent;
  134.     }
  135.     public function saveStartup(Startup $startup$adminEmail)
  136.     {
  137.         $em $this->getEntityManager();
  138.         $now = new DateTime();
  139.         $startup->setLastModified($now);
  140.         $startup->setLastChangedBy($adminEmail);
  141.         $em->persist($startup);
  142.         $em->flush();
  143.         return $startup;
  144.     }
  145.     public function updateTeamName($teamId$teamName)
  146.     {
  147.         $em $this->getEntityManager();
  148.         if ($startup $em->getRepository(Startup::class)->findOneBy(['teamId'=>$teamId])) {
  149.             $startup->setName($teamName);
  150.             $startup $this->updateSlugByStartupName($startup);
  151.             $em->persist($startup);
  152.             $em->flush();
  153.         }
  154.         return true;
  155.     }
  156.     public function checkConnectionWithTeams(Startup $startup$user)
  157.     {
  158.         $em $this->getEntityManager();
  159.         $now = new DateTime();
  160.         $response '';
  161.         if ($teamId $startup->getTeamId()) {
  162.             if ($team $em->getRepository(Team::class)->find($teamId)) {
  163.                 if ($team->getStartupId() != $startup->getId()) {
  164.                     $team->setStartupId($startup->getId());
  165.                     $team->setLastModified($now);
  166.                     $team->setLastChangeUser($user->getEmail());
  167.                     $em->persist($team);
  168.                     $em->flush();
  169.                     $response "Team updated with new startupId {$startup->getId()}";
  170.                 }
  171.             } else {
  172.                 $response "ERROR team with {$startup->getTeamId()} does not exist";
  173.             }
  174.         }
  175.         return $response;
  176.     }
  177.     public function setStartupCompletenessScore(Startup $startup)
  178.     {
  179.         $credits = [
  180.             "oneSentencePitch" => 200,
  181.             "logoUrl"          => 150,
  182.             "contactEmail"     =>  40,
  183.             "website"        =>  40,
  184.             "industry"       =>  20,
  185.             "foundationYear" => 20,
  186.             "customerFocus"  => 20,
  187.             "stage"       => 20,
  188.             "linkedin"    =>  30,
  189.             "twitter"     =>  15,
  190.             "instagram"   =>  15,
  191.             "xing"        =>  10,
  192.             "facebook"    =>  10,
  193.         ];
  194.         $completenessScore 0;
  195.         foreach ($credits as $field => $value) {
  196.             $method 'get' ucfirst($field);
  197.             if ($startup->$method()) {
  198.                 $completenessScore += $value;
  199.             }
  200.         }
  201.         $tags explode(','str_replace('#''', (string) $startup->getTags()));
  202.         if (in_array('accelerator'$tags)) {
  203.             $completenessScore += 50;
  204.         }
  205.         $tags explode(','str_replace('#''', (string) $startup->getTags()));
  206.         if (in_array('gruenderstipendium'$tags)) {
  207.             $completenessScore += 30;
  208.         }
  209.         // UP-024: batch field removed from Startup - participation count could be used instead
  210.         // For now, removed from score calculation
  211.         if ($startup->getStage() == 'closed') {
  212.             $completenessScore 0;
  213.         }
  214.         return $completenessScore;
  215.     }
  216.     public function updateScoreByEditTeam(Startup $startup)
  217.     {
  218.         $em $this->getEntityManager();
  219.         $storedCompletenessScore $startup->getScore();
  220.         $updatedCompletenessScore $this->setStartupCompletenessScore($startup);
  221.         if ($startup->getScore() != $updatedCompletenessScore) {
  222.             $startup->setScore($updatedCompletenessScore);
  223.         }
  224.         return $startup;
  225.     }
  226.     public function getLatestEntriesByTeamTags($tag$maxResults)
  227.     {
  228.         $qb $this->getEntityManager()->createQueryBuilder();
  229.         $qb->select(
  230.             's'
  231.         );
  232.         $qb->from(Startup::class, 's')
  233.             ->from(Team::class, 't')
  234.             ->where('s.teamId = t.id')
  235.             ->andWhere("t.tags LIKE :tag")
  236.             ->setMaxResults($maxResults)
  237.             ->setParameter('tag'"%{$tag}%")
  238.         ;
  239.         $qb->orderBy('s.lastModified''DESC');
  240.         $startups $qb->getQuery()->getResult();
  241.         $qb $this->getEntityManager()->createQueryBuilder();
  242.         $qb->select(
  243.             't'
  244.         );
  245.         $qb->from(Startup::class, 's')
  246.             ->from(Team::class, 't')
  247.             ->where('s.teamId = t.id')
  248.             ->andWhere("t.tags LIKE :tag")
  249.             ->setMaxResults($maxResults)
  250.             ->setParameter('tag'"%{$tag}%")
  251.         ;
  252.         $qb->orderBy('s.lastModified''DESC');
  253.         $teams $qb->getQuery()->getResult();
  254.         $startupIds array_map(fn (Team $entity) => $entity->getStartupId(), $teams);
  255.         $teams array_combine($startupIds$teams);
  256.         return ['startups' => $startups'teams' => $teams];
  257.     }
  258.     public function getLatestEntriesByTeamCategory($category$maxResults)
  259.     {
  260.         $qb $this->getEntityManager()->createQueryBuilder();
  261.         $qb->select(
  262.             's'
  263.         );
  264.         $qb->from(Startup::class, 's')
  265.             ->from(Team::class, 't')
  266.             ->where('s.teamId = t.id')
  267.             ->andWhere("t.categories LIKE :tag")
  268.             ->setMaxResults($maxResults)
  269.             ->setParameter('tag'"%{$category}%")
  270.         ;
  271.         $qb->orderBy('s.lastModified''DESC');
  272.         $startups $qb->getQuery()->getResult();
  273.         $qb $this->getEntityManager()->createQueryBuilder();
  274.         $qb->select(
  275.             't'
  276.         );
  277.         $qb->from(Startup::class, 's')
  278.             ->from(Team::class, 't')
  279.             ->where('s.teamId = t.id')
  280.             ->andWhere("t.categories LIKE :tag")
  281.             ->setMaxResults($maxResults)
  282.             ->setParameter('tag'"%{$category}%")
  283.         ;
  284.         $qb->orderBy('s.lastModified''DESC');
  285.         $teams $qb->getQuery()->getResult();
  286.         $startupIds array_map(fn (Team $entity) => $entity->getStartupId(), $teams);
  287.         $teams array_combine($startupIds$teams);
  288.         return ['startups' => $startups'teams' => $teams];
  289.     }
  290.     public function getLatestEntriesByStartupTags($tag$maxResults)
  291.     {
  292.         $qb $this->getEntityManager()->createQueryBuilder();
  293.         $qb->select(
  294.             's'
  295.         );
  296.         $qb->from(Startup::class, 's')
  297.             ->from(Team::class, 't')
  298.             ->where('s.teamId = t.id')
  299.             ->andWhere("s.tags LIKE :tag")
  300.             ->setMaxResults($maxResults)
  301.             ->setParameter('tag'"%{$tag}%")
  302.         ;
  303.         $qb->orderBy('s.lastModified''DESC');
  304.         $startups $qb->getQuery()->getResult();
  305.         $qb $this->getEntityManager()->createQueryBuilder();
  306.         $qb->select(
  307.             't'
  308.         );
  309.         $qb->from(Startup::class, 's')
  310.             ->from(Team::class, 't')
  311.             ->where('s.teamId = t.id')
  312.             ->andWhere("s.tags LIKE :tag")
  313.             ->setMaxResults($maxResults)
  314.             ->setParameter('tag'"%{$tag}%")
  315.         ;
  316.         $qb->orderBy('s.lastModified''DESC');
  317.         $teams $qb->getQuery()->getResult();
  318.         $startupIds array_map(fn (Team $entity) => $entity->getStartupId(), $teams);
  319.         $teams array_combine($startupIds$teams);
  320.         return ['startups' => $startups'teams' => $teams];
  321.     }
  322.     public function removeStartup(Startup $startup)
  323.     {
  324.         $em $this->getEntityManager();
  325.         $em->remove($startup);
  326.         $em->flush();
  327.         return "Startup removed";
  328.     }
  329.     public function setField($field$action$id$adminEmail "system@")
  330.     {
  331.         $em $this->getEntityManager();
  332.         if (!$startup $this->findOneBy(['id' => $id])) {
  333.             return 'ERROR: entity not found';
  334.         }
  335.         switch ($field) {
  336.             case "vorPitchOnly":
  337.                 $startup->setPitchPosition('99');
  338.                 $startup->setApplicationStatus('applied');
  339.                 $startup->setBatchStatus('pre pitch');
  340.                 break;
  341.             default:
  342.                 $method 'set' ucfirst((string) $field);
  343.                 $startup->$method($action);
  344.                 break;
  345.         }
  346.         $startup->setLastModified(new DateTime());
  347.         $logText "field  {$field}  set to {$action}";
  348.         $startup->setLastChangeUser($adminEmail);
  349.         $startup->setHistory(
  350.             "==== {$startup->getLastModified()->format('Y-m-d')} by {$startup->getLastChangeUser()}==="
  351.             PHP_EOL $logText   PHP_EOL .$startup->getHistory()
  352.         );
  353.         $em->persist($startup);
  354.         $em->flush();
  355.         return $logText;
  356.     }
  357.     public function setFieldByAjaxConnect($field$actionStartup $startup$user)
  358.     {
  359.         $em $this->getEntityManager();
  360.         $adminEmail $user->getEmail();
  361.         $method 'set' . ($field);
  362.         $startup->$method($action);
  363.         $startup->setLastModified(new DateTime());
  364.         $startup->setLastChangeUser($user->getEmail());
  365.         $logText "{$field}$action";
  366.         $userType $user->getIsAdmin() ? 'admin ' 'member ';
  367.         $startup->setHistory(
  368.             "==== {$startup->getLastModified()->format('Y-m-d')} by {$userType} {$startup->getLastChangeUser()}==="
  369.             PHP_EOL $logText   PHP_EOL .$startup->getHistory()
  370.         );
  371.         $em->persist($startup);
  372.         $em->flush();
  373.         //sync with team
  374.         if($team $em->getRepository(Team::class)->find($startup->getTeamId())) {
  375.             $em->getRepository(Team::class)->syncDataWithStartup($team$startup);
  376.         }
  377.         switch ($field) {
  378.             case 'logoUrl':
  379.                 $em->getRepository(Team::class)->unifyLogoUrlPartnerAndStartup($startup->getTeamId(), $action$adminEmail);
  380.                 $fieldName 'logo';
  381.                 break;
  382.             case 'imageLink':
  383.                 $fieldName 'banner';
  384.                 break;
  385.             default:
  386.                 $fieldName $field;
  387.         }
  388.         return 'Updated ' $fieldName;
  389.     }
  390.     public function find4Search($visibility)
  391.     {
  392.         $connection $this->getEntityManager()->getConnection();
  393.         $andWhere = match ($visibility) {
  394.             "public" => " WHERE s.visibility = 'public'",
  395.             "sp-connect" => " WHERE s.visibility IN ('public','sp-connect')",
  396.             default => '',
  397.         };
  398.         $sql ="SELECT s.id, s.name, s.city, s.industry
  399.         FROM sp_startup_profiles s {$andWhere}
  400.         ORDER BY s.name ASC
  401.         ";
  402.         return $connection->fetchAllAssociative($sql, []);
  403.     }
  404.     public function find4SearchWithBatch($batchIds)
  405.     {
  406.         $qb $this->getEntityManager()->createQueryBuilder();
  407.         $qb->select('s.id, s.name, s.city, s.industry')
  408.            ->from(Startup::class, 's')
  409.            ->join(Application::class, 'a''WITH''s.id = a.startupId')
  410.            ->where('a.batchId in (:batchIds)')
  411.            ->setParameter('batchIds'$batchIds)
  412.         ;
  413.         return $qb->getQuery()->getResult();
  414.     }
  415.     public function findIndustries4Search($visibility)
  416.     {
  417.         $connection $this->getEntityManager()->getConnection();
  418.         $andWhere = match ($visibility) {
  419.             "public" => " WHERE s.visibility = 'public'",
  420.             "sp-connect" => " WHERE s.visibility IN ('public','sp-connect')",
  421.             default => '',
  422.         };
  423.         $sql ="SELECT s.industry, count(s.id) as total
  424.         FROM sp_startup_profiles s {$andWhere}
  425.         GROUP BY s.industry
  426.         ";
  427.         return $connection->fetchAllAssociative($sql, []);
  428.     }
  429.     public function find4Merge($startupId)
  430.     {
  431.         $connection $this->getEntityManager()->getConnection();
  432.         $sql ="SELECT t.id, t.name, t.contactPerson, t.website, t.status, t.tags
  433.                     FROM sp_startup_profiles t
  434.                     WHERE t.id != :startupId
  435.                     ORDER BY t.id DESC
  436.                     ";
  437.         return $connection->fetchAllAssociative($sql, ['startupId' => $startupId]);
  438.     }
  439.     public function findTags($criteria$teamId)
  440.     {
  441.         $qb $this->getEntityManager()->createQueryBuilder();
  442.         $qb->select(
  443.             's.tags'
  444.         );
  445.         $qb->from(Startup::class, 's')
  446.             ->where("s.tags > '' ")
  447.         ;
  448.         $qb $this->setCriteria($qb$criteria$teamId);
  449.         $qb->orderBy('s.name''ASC');
  450.         $entities $qb->getQuery()->getResult();
  451.         return Utility::countHashTags($entities'tags');
  452.     }
  453.     public function getListOfIndustries()
  454.     {
  455.         $em $this->getEntityManager();
  456.         $attributes $em->getRepository(Attribute::class)->findBy(['name'=>'startup.industry'], ['label'=>'ASC']);
  457.         $labels     array_map(fn ($entity) => $entity->getLabel(), $attributes);
  458.         $industries array_combine($labels$labels);
  459.         /*
  460.         $industries = $this->findIndustries([], 0);
  461.         return array_combine(array_keys($industries), array_keys($industries));
  462.         */
  463.         return $industries;
  464.     }
  465.     public function findIndustries($criteria$teamId)
  466.     {
  467.         $qb $this->getEntityManager()->createQueryBuilder();
  468.         $qb->select(
  469.             's.industry'
  470.         );
  471.         $qb->from(Startup::class, 's')
  472.             ->where("s.industry > '' ")
  473.         ;
  474.         $qb $this->setCriteria($qb$criteria$teamId);
  475.         $qb->orderBy('s.name''ASC');
  476.         $entities $qb->getQuery()->getResult();
  477.         return Utility::countHashTags($entities'industry');
  478.     }
  479.     public function findAndExpandAggregatesByField($field$criteria$teamId$group='')
  480.     {
  481.         $qb $this->getEntityManager()->createQueryBuilder();
  482.         $qb->select(
  483.             "s.{$field}"
  484.         );
  485.         $qb->from(Startup::class, 's')
  486.             ->where("s.{$field} > '' ")
  487.         ;
  488.         if ($group) {
  489.             $qb->andWhere("s.{$group} > '' ");
  490.         }
  491.         $qb $this->setCriteria($qb$criteria$teamId);
  492.         $qb->orderBy('s.name''ASC');
  493.         $entities $qb->getQuery()->getResult();
  494.         return Utility::countHashTags($entities$fieldtrue);
  495.     }
  496.     public function findApplicationsByProgramId($programId)
  497.     {
  498.         $connection $this->getEntityManager()->getConnection();
  499.         $sqlRheinlandPitchApplicants "
  500. SELECT a.applicationStatus, a.batchStatus, count(a.id)
  501. FROM   sp_applications a, sp_batches b
  502. WHERE  a.batchId = b.id
  503.   AND a.programId = 1
  504. GROUP BY a.applicationStatus, a.batchStatus;
  505. Probleme macht der Batcht Status setBatchStatus(
  506. gefunden haben wir
  507. 1_winner
  508. 2_podium
  509. 3_pitch
  510. 5_applied
  511. 6_started
  512. vorgesehen sind:
  513. applicant
  514. pitch
  515. pre pitch
  516. SELECT b.id as batchId, b.batchNumber, b.name
  517. FROM   sp_applications a, sp_batches b
  518. WHERE  a.batchId = b.id
  519.   AND a.programId = 1 and a.batchStatus = 'pitch'
  520. GROUP BY b.batchNumber
  521.         ";
  522.         /*
  523.          */
  524.         $sql ="
  525.             SELECT b.id as batchId, b.batchNumber, b.name, count(s.id) as number
  526.             FROM   sp_startup_profiles s, sp_applications a, sp_batches b
  527.             WHERE  s.id = a.startupId and a.batchId = b.id and a.programId = :programId and a.batchStatus = 'pitch'
  528.             GROUP BY b.batchNumber
  529.                     ";
  530.         $result =  $connection->fetchAllAssociative($sql, ['programId'=>$programId]);
  531.         return $result;
  532.     }
  533.     public function findTeamTags($criteria$teamId)
  534.     {
  535.         $qb $this->getEntityManager()->createQueryBuilder();
  536.         $qb->select(
  537.             'a.teamTags'
  538.         );
  539.         $qb->from(StartupAttribute::class, 'a')
  540.             ->where('a.teamId = :teamId')
  541.             ->setParameter('teamId'$teamId)
  542.         ;
  543.         $qb->innerJoin(Startup::class, 's''WITH'"a.startupId = s.id and a.teamTags > ''")
  544.         ;
  545.         $qb $this->setCriteria($qb$criteria$teamId);
  546.         $qb->orderBy('a.id''ASC');
  547.         $entities $qb->getQuery()->getResult();
  548.         return Utility::countHashTags($entities'teamTags');
  549.     }
  550.     /**
  551.      * UP-024: Removed references to fields that no longer exist:
  552.      * - startplatz, accelerator, batch, batchFirst, batchLast, batchYear
  553.      * - rheinlandPitch, pitchNumber, pitchDate, pitchTopic
  554.      */
  555.     public function getFieldsByTemplate($template)
  556.     {
  557.         $fields = match ($template) {
  558.             "rheinland-pitch" => ['id''name''website''todos''status''tags'],
  559.             "contact" => ['id''name''website''todos''status''city''location''contactPerson''contactEmail''contactTelefon'],
  560.             "startplatz" => ['id''name''website''todos''status''teamId''kiAccelerator'],
  561.             "description" => ['id''name''website''tags''oneSentencePitch'],
  562.             "kpi" => ['id''name''website''employeesCount''funding''investors''revenue''tags'],
  563.             "industries" => ['id''name''website''todos''industry''industryCondensed''customerFocus''businessModel'],
  564.             "logoAndTags" => ['id''name''website''domain''logoUrl''tags''todos'],
  565.             "full" => ['id''extId''name''teamId''website''domain''slug''status''kiAccelerator''todos''industry''industryCondensed''customerFocus''businessModel''tags''foundationYear''city''location''employeesCount''funding''investors''revenue''contactPerson''contactEmail''contactTelefon''oneSentencePitch'],
  566.             "default" => ['id''teamId''name''website''linkedin''contactPerson''contactEmail''contactTelefon''tags''oneSentencePitch'],
  567.         };
  568.         return $fields;
  569.     }
  570.     public function countByCriteria($criteria$teamId)
  571.     {
  572.         $qb $this->getEntityManager()->createQueryBuilder();
  573.         $qb
  574.             ->select('COUNT(s)')
  575.             ->from(Startup::class, 's')
  576.         ;
  577.         /*
  578.             ->leftJoin(StartupAttribute::class, 'a', 'WITH', 's.id = a.startupId and a.teamId = :teamId')
  579.             ->setParameter('teamId', $teamId)
  580.          */
  581.         if ($criteria) {
  582.             $qb $this->setCriteria($qb$criteria$teamId);
  583.         }
  584.         return $qb->getQuery()->getSingleScalarResult();
  585.     }
  586.     public function findNotInRelevanceTable()
  587.     {
  588.         $qb $this->getEntityManager()->createQueryBuilder();
  589.         $qb
  590.             ->select('s')
  591.             ->from(Startup::class, 's')
  592.             ->leftJoin(StartupRelevance::class, 'r''WITH''s.id = r.startupId')
  593.             ->where("r.startupId IS NULL")
  594.         ;
  595.         $query $qb->getQuery();
  596.         $results $query->getResult();
  597.         return $results;
  598.     }
  599.     public function findAcceleratorAlumnis()
  600.     {
  601.         $qb $this->getEntityManager()->createQueryBuilder();
  602.         $qb->select('s')
  603.             ->from(Startup::class, 's')
  604.             ->innerJoin(Team::class, 't''WITH''s.teamId = t.id')
  605.             ->where("t.tags LIKE '%accelerator%'")
  606.             ->andWhere("t.tags LIKE '%badge-accelerator%'")
  607.             ->orderBy('s.name''ASC')
  608.         ;
  609.         $results $qb->getQuery()->getResult();
  610.         return $results;
  611.     }
  612.     /**
  613.      * Find all startups that are part of the KI Accelerator program
  614.      * (startups where kiAccelerator field is set)
  615.      *
  616.      * @return Startup[]
  617.      */
  618.     public function findKiAcceleratorStartups(): array
  619.     {
  620.         return $this->createQueryBuilder('s')
  621.             ->where('s.kiAccelerator IS NOT NULL')
  622.             ->orderBy('s.kiAccelerator''ASC')
  623.             ->addOrderBy('s.name''ASC')
  624.             ->getQuery()
  625.             ->getResult();
  626.     }
  627.     /**
  628.      * Find startups participating in the current Rheinland Pitch
  629.      *
  630.      * @param string $focusStatus The current focus status of the batch
  631.      * @return array Returns an array of Startup entities
  632.      */
  633.     public function findCurrentRheinlandPitchStartups(string $focusStatus): array
  634.     {
  635.         return $this->createQueryBuilder('s')
  636.             // s represents startup entity
  637.             ->select('s')
  638.             // Join with Application table connecting via startupId
  639.             ->innerJoin(Application::class, 'a''WITH''a.startupId = s.id')
  640.             // Join with Batch table connecting via batchId
  641.             ->innerJoin(Batch::class, 'b''WITH''a.batchId = b.id')
  642.             // Filter for Rheinland Pitch program (programId = 1)
  643.             ->where('b.programId = :programId')
  644.             // Filter for current batch status
  645.             ->andWhere('b.focusStatus = :focusStatus')
  646.             // Filter for pitch status in applications
  647.             ->andWhere('a.batchStatus = :batchStatus')
  648.             ->setParameter('programId'1)
  649.             ->setParameter('focusStatus'$focusStatus)
  650.             ->setParameter('batchStatus''pitch')
  651.             // Order by pitch presentation order
  652.             ->orderBy('a.pitchPosition''ASC')
  653.             ->getQuery()
  654.             ->getResult();
  655.     }
  656.     public function findPaginated($criteria$teamId$order = [], $page 1$limit 5)
  657.     {
  658.         $page = (int) $page;
  659.         $limit = (int) $limit;
  660.         $qb $this->getEntityManager()->createQueryBuilder();
  661.         $qb $this->setSelect($qb);
  662.         $qb
  663.             ->from(Startup::class, 's')
  664.             ->innerJoin(StartupRelevance::class, 'r''WITH''s.id = r.startupId')
  665.             ->setFirstResult(max(0$limit * ($page 1))) // Offset
  666.             ->setMaxResults($limit// Limit
  667.         ;
  668.         // get rid of
  669.         // ->leftJoin(StartupAttribute::class, 'a', 'WITH', 's.id = a.startupId and a.teamId = :teamId')
  670.         //             ->setParameter('teamId', $teamId)
  671.         if ($criteria) {
  672.             $qb $this->setCriteria($qb$criteria$teamId);
  673.         }
  674.         if ($order) {
  675.             $metadata $this->getClassMetadata();
  676.             foreach ($order as $key => $value) {
  677.                 if ($key == 'score') {
  678.                     $qb->addOrderBy('r.score'$value);
  679.                 } elseif ($metadata->hasField($key)) {
  680.                     $qb->addOrderBy('s.' $key$value);
  681.                 }
  682.                 // UP-024: Skip non-existent fields (removed: pitchDate, etc.)
  683.             }
  684.         } else {
  685.             $qb->addOrderBy('r.score''DESC');
  686.             $qb->addOrderBy('s.lastModified''DESC');
  687.         }
  688.         $query $qb->getQuery();
  689.         $results $query->getResult();
  690.         return $results;
  691.     }
  692.     /**
  693.      * Paginated startup search with relevance sorting for SP-Connect.
  694.      * Relevance: logo → pitch → industry → relevance score → recency.
  695.      */
  696.     public function findPaginatedWithSearch(array $criteria$teamId, ?string $searchint $page 1int $limit 72): array
  697.     {
  698.         $qb $this->getEntityManager()->createQueryBuilder();
  699.         $qb $this->setSelect($qb);
  700.         $qb->from(Startup::class, 's')
  701.             ->innerJoin(StartupRelevance::class, 'r''WITH''s.id = r.startupId')
  702.             ->setFirstResult(max(0$limit * ($page 1)))
  703.             ->setMaxResults($limit);
  704.         if ($criteria) {
  705.             $qb $this->setCriteria($qb$criteria$teamId);
  706.         }
  707.         if ($search) {
  708.             $qb->andWhere('(s.name LIKE :search OR s.industry LIKE :search OR s.oneSentencePitch LIKE :search)')
  709.                 ->setParameter('search''%' $search '%');
  710.             // Search relevance: name match first, then industry, then pitch
  711.             $qb->addSelect("CASE WHEN s.name LIKE :search THEN 0 WHEN s.industry LIKE :search THEN 1 ELSE 2 END AS HIDDEN sortSearchRelevance")
  712.                 ->addOrderBy('sortSearchRelevance''ASC');
  713.         }
  714.         // Profile completeness sort: logo > pitch > score > recency
  715.         $qb->addSelect("CASE WHEN s.logoUrl IS NOT NULL AND s.logoUrl != '' THEN 0 ELSE 1 END AS HIDDEN sortLogo")
  716.             ->addSelect("CASE WHEN s.oneSentencePitch IS NOT NULL AND s.oneSentencePitch != '' THEN 0 ELSE 1 END AS HIDDEN sortPitch")
  717.             ->addOrderBy('sortLogo''ASC')
  718.             ->addOrderBy('sortPitch''ASC')
  719.             ->addOrderBy('r.score''DESC')
  720.             ->addOrderBy('s.lastModified''DESC');
  721.         return $qb->getQuery()->getResult();
  722.     }
  723.     /**
  724.      * Count startups matching criteria + search for SP-Connect pagination.
  725.      */
  726.     public function countWithSearch(array $criteria$teamId, ?string $search): int
  727.     {
  728.         $qb $this->getEntityManager()->createQueryBuilder()
  729.             ->select('COUNT(s)')
  730.             ->from(Startup::class, 's')
  731.             ->innerJoin(StartupRelevance::class, 'r''WITH''s.id = r.startupId');
  732.         if ($criteria) {
  733.             $qb $this->setCriteria($qb$criteria$teamId);
  734.         }
  735.         if ($search) {
  736.             $qb->andWhere('(s.name LIKE :search OR s.industry LIKE :search OR s.oneSentencePitch LIKE :search)')
  737.                 ->setParameter('search''%' $search '%');
  738.         }
  739.         return (int) $qb->getQuery()->getSingleScalarResult();
  740.     }
  741.     /**
  742.      * UP-024: Removed fields that no longer exist on Startup entity:
  743.      * - startplatz, accelerator, batch, batchLast, batchFirst, batchYear
  744.      * - rheinlandPitch, pitchNumber, pitchDate, pitchTopic
  745.      * - gruenderstipendium, gsJury, gsStatus
  746.      * - hasBadgeStartplatz
  747.      * These are now managed via Participation entity.
  748.      */
  749.     private function setSelect($qb)
  750.     {
  751.         return $qb
  752.             ->select('s.id''s.extId''s.visibility''s.teamId''s.name''s.status''s.location''s.city'
  753.                 's.website''s.domain''s.slug''s.linkedin''s.twitter''s.logoUrl''s.stage'
  754.                 's.kiAccelerator'
  755.                 's.industry''s.industryCondensed''s.customerFocus''s.businessModel''s.tags''s.todos'
  756.                 's.foundationYear''s.funding''s.oneSentencePitch'
  757.                 's.employeesCount''s.revenue''s.investors'
  758.                 's.contactPerson''s.contactEmail''s.contactTelefon''s.lastModified''s.history''s.lastChangeUser''s.createdAt'
  759.                 'r.score'
  760.             )
  761.         ;
  762.     }
  763.     public function findNotPaginated($criteria$teamId$order)
  764.     {
  765.         $qb $this->getEntityManager()->createQueryBuilder();
  766.         $qb $this->setSelect($qb);
  767.         $qb
  768.             ->from(Startup::class, 's')
  769.             ->innerJoin(StartupRelevance::class, 'r''WITH''s.id = r.startupId')
  770.             ->leftJoin(StartupAttribute::class, 'a''WITH''s.id = a.startupId and a.teamId = :teamId')
  771.             ->setParameter('teamId'$teamId)
  772.         ;
  773.         if ($criteria) {
  774.             $qb $this->setCriteria($qb$criteria$teamId);
  775.         }
  776.         if ($order) {
  777.             $metadata $this->getClassMetadata();
  778.             foreach ($order as $key => $value) {
  779.                 if ($metadata->hasField($key)) {
  780.                     $qb->addOrderBy('s.' $key$value);
  781.                 }
  782.                 // UP-024: Skip non-existent fields (removed: pitchDate, etc.)
  783.             }
  784.         } else {
  785.             $qb->addOrderBy('r.score''DESC');
  786.         }
  787.         $query $qb->getQuery();
  788.         $results $query->getResult();
  789.         return $results;
  790.     }
  791.     public function findSingleStartupByVisibility($id$visibility)
  792.     {
  793.         $qb $this->getEntityManager()->createQueryBuilder();
  794.         $qb
  795.             ->select('s')
  796.             ->from(Startup::class, 's')
  797.             ->where('s.id = :id')
  798.             ->setParameter('id'$id)
  799.         ;
  800.         switch ($visibility) {
  801.             case "public":
  802.                 $qb->andWhere("s.visibility = 'public' ");
  803.                 break;
  804.             case "sp-connect":
  805.                 $qb->andWhere("s.visibility IN ('public','sp-connect')");
  806.                 break;
  807.             default:
  808.                 break;
  809.         }
  810.         return $qb->getQuery()->getOneOrNullResult();
  811.     }
  812.     /*
  813.              if ($criteria){
  814.             foreach($criteria as $field =>$value) {
  815.                 if ($field == 'categories' or $field == 'tags'){
  816.                     $qb->andWhere('t.'. $field . " LIKE '%" . $value . "%'");
  817.                 } elseif ($value == 'IS NULL' or $value == null) {
  818.                     $qb->andWhere('t.'. $field . " IS NULL" );
  819.                 } elseif (is_array($value) ) {
  820.                     $qb->andWhere('t.'. $field . " IN (:{$field})" );
  821.                     $qb->setParameter($field, array_keys($value));
  822.                 }
  823.                 else {
  824.                     $qb->andWhere('t.'. $field . ' = :' . $field);
  825.                     $qb->setParameter($field, $value);
  826.                 }
  827.             }
  828.         }
  829.      */
  830.     private function setCriteria(QueryBuilder $qb$criteria$teamId 0)
  831.     {
  832.         foreach($criteria as $field =>$value) {
  833.             switch ($field) {
  834.                 case "teamTags":
  835.                     $qb->innerJoin(StartupAttribute::class, 'b''WITH''s.id = b.startupId and b.teamId = :teamIdInnerJoin');
  836.                     $qb->andWhere('b.'$field ' LIKE :' $field);
  837.                     $qb->setParameter($field"%{$value}%");
  838.                     $qb->setParameter('teamIdInnerJoin'$teamId);
  839.                     break;
  840.                 case "batchNumber":
  841.                     $qb->innerJoin(Application::class, 'ap''WITH''s.id = ap.startupId and ap.programId = 1');
  842.                     $qb->innerJoin(Batch::class, 'batch''WITH''ap.batchId = batch.id');
  843.                     $qb->andWhere('batch.'$field ' LIKE :' $field);
  844.                     $qb->setParameter($field"%{$value}%");
  845.                     break;
  846.                 // UP-024: gsStatus field removed from Startup - filter via Participation instead
  847.                 case "visibility":
  848.                     switch ($value) {
  849.                         case "public":
  850.                             $qb->andWhere('s.'$field ' = :' $field);
  851.                             $qb->setParameter($field$value);
  852.                             break;
  853.                         case "sp-connect":
  854.                             $qb->andWhere("s.visibility IN ('public','sp-connect')");
  855.                             break;
  856.                         default:
  857.                             break;
  858.                     }
  859.                     break;
  860.                 case "team":
  861.                     $qb->andWhere('s.teamId > 0');
  862.                     break;
  863.                 case "todos":
  864.                 case "tags":
  865.                     // UP-024: pitchNumber removed - field no longer exists on Startup entity
  866.                     $qb->andWhere('s.'$field ' LIKE :' $field);
  867.                     $qb->setParameter($field"%{$value}%");
  868.                     break;
  869.                 case "isBookmarked":
  870.                     $qb->innerJoin(StartupAttribute::class, 'b''WITH''s.id = b.startupId and b.teamId = :teamIdInnerJoin');
  871.                     $qb->andWhere('b.'$field ' = 1');
  872.                     $qb->setParameter('teamIdInnerJoin'$teamId);
  873.                     break;
  874.                 default:
  875.                     // Skip unknown fields to prevent QueryException for removed fields (e.g., startplatz)
  876.                     $metadata $this->getClassMetadata();
  877.                     if (!$metadata->hasField($field) && !$metadata->hasAssociation($field)) {
  878.                         continue 2// continue the foreach loop
  879.                     }
  880.                     if ($value == 'IS NULL' or $value == null) {
  881.                         $qb->andWhere('s.'$field " IS NULL");
  882.                     } elseif ($value == 'not empty') {
  883.                         $qb->andWhere('s.'$field " > '' ");
  884.                     } elseif (is_array($value)) {
  885.                         $qb->andWhere('s.'$field " IN (:{$field})");
  886.                         $qb->setParameter($fieldarray_keys($value));
  887.                     } else {
  888.                         $qb->andWhere('s.'$field ' = :' $field);
  889.                         $qb->setParameter($field$value);
  890.                     }
  891.                     break;
  892.             }
  893.         }
  894.         return $qb;
  895.     }
  896.     private function expandTimeCriteria($qb$value)
  897.     {
  898.         $startDate date_create($value);
  899.         $endDate   date_create($value);
  900.         $endDate->modify('+1 days');
  901.         $qb->andWhere('c.start BETWEEN :startDate AND :endDate')
  902.             ->setParameter('startDate'$startDate)
  903.             ->setParameter('endDate'$endDate)
  904.         ;
  905.         return $qb;
  906.     }
  907.     public function importOrUpdateByApplication(Application $applicationUser $user$importTag)
  908.     {
  909.         $em $this->getEntityManager();
  910.         $now = new DateTime();
  911.         $adminEmail $user->getEmail();
  912.         $status          'open';
  913.         $todo            '';
  914.         $responseContent = [];
  915.         $exportList      = [];
  916.         $preFields       = [];
  917.         $responseText    "Case #{$application->getStartupName()} ";
  918.         $program $em->getRepository(Program::class /*Startup Program*/)->findOneBy(['id'=>$application->getProgramId()]);
  919.         $batch   $em->getRepository(Batch::class)->findOneBy(['id'=>$application->getBatchId()]);
  920.         if ($application->getWebsiteUrl()) {
  921.             $domain $this->extractDomainByWebsite($application->getWebsiteUrl());
  922.             if (in_array($domain$this->getDomainServices())) {
  923.                 $domain $application->getWebsiteUrl() ;
  924.             }
  925.         } else {
  926.             $domain $this->extractDomainByEmail($application->getEmail());
  927.             if (in_array($domain$this->getMailServices())) {
  928.                 $domain str_replace('@''at'$application->getEmail()) ;
  929.             }
  930.         }
  931.         // check if application->teamId is virtual team
  932.         if ($teamId $application->getTeamId()) {
  933.             if (!$team $em->getRepository(Team::class)->find($teamId)) {
  934.                 $teamId "";
  935.             } else {
  936.                 if ($team->getType() == 'Virtual Team') {
  937.                     $teamId "";
  938.                 }
  939.             }
  940.         }
  941.         // check if startup is already imported
  942.         if (!$startup $this->findOneBy(['id' => $application->getStartupId()])) {
  943.             // check if there is already a startup with the application teamId
  944.             if (!$startup $this->findOneBy(['teamId'=>$application->getTeamId()])) {
  945.                 // lookup domain
  946.                 if ($startup $this->findOneBy(['domain'=>$domain])) {
  947.                     $response = [
  948.                         'status'  => "ERROR",
  949.                         'todo'    => "checkDomain",
  950.                         'message' => "Domain {$domain} is already stored with startup {$startup->getName()} [{$startup->getId()}] ",
  951.                         'data'    => $startup,
  952.                     ];
  953.                     return $response;
  954.                 } elseif ($startup $this->findOneBy(['teamId' => $team->getId()])) {
  955.                     $response = [
  956.                         'status'  => "ERROR",
  957.                         'todo'    => "checkTeamId",
  958.                         'message' => "teamId {$teamId} is already stored with startup {$startup->getName()} [{$startup->getId()}] ",
  959.                         'data'    => $startup,
  960.                     ];
  961.                     return $response;
  962.                 } else {
  963.                     $startup = new Startup();
  964.                     $startup->setTeamId($teamId);
  965.                     $startup->setDomain($domain);
  966.                     // UP-024: setStartplatz removed - no longer tracked on Startup
  967.                 }
  968.             }
  969.         }
  970.         if ($startup->getId()) {
  971.             $responseText .= "updated on ";
  972.         } else {
  973.             $responseText .= "imported ";
  974.         }
  975.         $startup->setName($application->getStartupName());
  976.         if ($startup->getName() and !$startup->getSlug()) {
  977.             $startup $this->updateSlugByStartupName($startup);
  978.         }
  979.         $startup->setStatus('active');
  980.         // UP-024: Rheinland-Pitch data now managed via Participation entity
  981.         // setOrUpdateRheinlandPitchSpecials and pitchNumber/pitchDate/pitchTopic removed
  982.         // Use ParticipationOutcomeWriter::recordRheinlandPitchOutcome() for outcomes
  983.         $startup->setWebsite($this->sanitizeWebsite($application->getWebsiteUrl()));
  984.         $startup->setCity($application->getCity());
  985.         $startup->setEmployeesCount($application->getTeamSize());
  986.         $startup->setStage($application->getStartupStage());
  987.         $startup->setContactPerson($application->getPerson());
  988.         $startup->setContactEmail($application->getEmail());
  989.         $startup->setContactTelefon($this->sanitizePhone($application->getPhone()));
  990.         $startup->setLinkedin($application->getLinkedin());
  991.         $startup->setOneSentencePitch($this->sanitizeDescription($application->getIdeaPitch()));
  992.         $startup->setVisibility($this->setVisibilityByApplicationProgramStatus($application$startup->getVisibility()));
  993.         $startup->setLastChangedBy($adminEmail);
  994.         $em->persist($startup);
  995.         $em->flush();
  996.         $responseText .= "application has been imported";
  997.         if (!$startupAttribute $em->getRepository(StartupAttribute::class)->findOneBy(['startupId'=> $startup->getId(), 'teamId'=>$user->getMemberId()])) {
  998.             $startupAttribute = new StartupAttribute();
  999.             $startupAttribute->setTeamId($user->getMemberId());
  1000.             $startupAttribute->setStartupId($startup->getId());
  1001.         }
  1002.         $startupAttribute->setTeamTags($em->getRepository(Tag::class)->addHashTag($importTag$startupAttribute->getTeamTags()));
  1003.         $startupAttribute->setLastModified($now);
  1004.         $startupAttribute->setLastChangedBy($adminEmail);
  1005.         $em->persist($startupAttribute);
  1006.         $em->flush();
  1007.         $em->getRepository(StartupRelevance::class)->updateByUserAction($startup->getId(), 'create');
  1008.         if (!$application->getStartupId()) {
  1009.             $application->setStartupId($startup->getId());
  1010.             $application->setEditStatus('imported in startup table');
  1011.             $responseText .= " startupId has been set in application";
  1012.         } else {
  1013.             $application->setEditStatus('updated in startup table');
  1014.             $responseText .= " updated in startup table";
  1015.         }
  1016.         $em->persist($application);
  1017.         $em->flush();
  1018.         // check if startupId need to be set in table team
  1019.         if ($startup->getTeamId() > 0) {
  1020.             $em->getRepository(Team::class)->setStartupIdIfNotExists($startup->getTeamId(), $startup->getId(), $adminEmail);
  1021.         }
  1022.         $responseContent[] = $responseText " done";
  1023.         $response = [
  1024.             'status'  => "SUCCESS",
  1025.             'todo'    => "done",
  1026.             'message' => $responseContent,
  1027.             'data'    => $startup,
  1028.         ];
  1029.         return $response;
  1030.     }
  1031.     /**
  1032.      * @deprecated UP-024: Removed - Rheinland-Pitch data now managed via Participation entity
  1033.      * Use ParticipationOutcomeWriter::recordRheinlandPitchOutcome() instead
  1034.      */
  1035.     public function setOrUpdateRheinlandPitchSpecials(Startup $startupApplication $application)
  1036.     {
  1037.         // UP-024: This method is now a no-op
  1038.         // Rheinland-Pitch participation and outcomes are stored in sp_ecosystem_participation
  1039.         // See ParticipationOutcomeWriter::recordRheinlandPitchOutcome()
  1040.         return $startup;
  1041.     }
  1042.     private function setVisibilityByApplicationProgramStatus(Application $application$currentVisibility)
  1043.     {
  1044.         switch ($application->getBatchStatus()) {
  1045.             case "pitch":
  1046.                 return "public";
  1047.             case "pre pitch":
  1048.                 if ($currentVisibility != "public") {
  1049.                     return "sp-connect";
  1050.                 } else {
  1051.                     return $currentVisibility;
  1052.                 }
  1053.                 // no break
  1054.             case "applicant":
  1055.                 if (!$currentVisibility) {
  1056.                     return "admin";
  1057.                 } else {
  1058.                     return $currentVisibility;
  1059.                 }
  1060.         }
  1061.     }
  1062.     public function importOrUpdateByTeam(Team $team$user false$importTag false)
  1063.     {
  1064.         $em $this->getEntityManager();
  1065.         $now = new DateTime();
  1066.         if ($user) {
  1067.             $adminEmail $user->getEmail();
  1068.         } else {
  1069.             $adminEmail "system@";
  1070.         }
  1071.         if (!$team->getCategories()){
  1072.             $team $em->getRepository(Team::class)->setCategory($team"startup"$adminEmail);
  1073.         }
  1074.         $todos           = [];
  1075.         $responseText    "Case #{$team->getName()} ";
  1076.         $teamCategories  explode(','str_replace(' '''$team->getCategories() ?? ''));
  1077.         $teamTags        array_filter(explode('#'str_replace(' '''$team->getTags() ?? '')));
  1078.         // check if startup is already imported
  1079.         if (!$startup $this->findOneBy(['teamId' => $team->getId()])) {
  1080.             $startup = new Startup();
  1081.             $startup->setTeamId($team->getId());
  1082.             $startup->setStatus('active');
  1083.             $startup->setCreatedAt($now);
  1084.         }
  1085.         $tags $startup->getTags();
  1086.         if (!$startup->getWebsite()) {
  1087.             if (!$team->getHomepage()) {
  1088.                 $todos[] = "check homepage";
  1089.             } else {
  1090.                 $domain $this->cleanDomain($team->getHomepage());
  1091.                 $startup->setDomain($domain);
  1092.                 $startup->setWebsite($this->sanitizeWebsite($team->getHomepage()));
  1093.             }
  1094.         }
  1095.         if ($startup->getId()) {
  1096.             $responseText .= "updated on ";
  1097.         } else {
  1098.             $responseText .= "imported ";
  1099.         }
  1100.         if (!$startup->getName()) {
  1101.             $startup->setName($team->getName());
  1102.             if ($startup->getName() and !$startup->getSlug()) {
  1103.                 $startup $this->updateSlugByStartupName($startup);
  1104.             }
  1105.         }
  1106.         // UP-024: Program-specific fields removed from Startup
  1107.         // startplatz, gruenderstipendium, gsJury, gsStatus, accelerator, batch, batchYear
  1108.         // are now managed via Participation entity
  1109.         if ($team->getGsStatus()) {
  1110.             $tags $em->getRepository(Tag::class)->addHashTag('gruenderstipendium'$tags);
  1111.             $tags $em->getRepository(Tag::class)->addHashTag('sp-connect'$tags);
  1112.         }
  1113.         if (in_array('accelerator'$teamCategories)) {
  1114.             $tags $em->getRepository(Tag::class)->addHashTag('accelerator'$tags);
  1115.             $tags $em->getRepository(Tag::class)->addHashTag('sp-connect'$tags);
  1116.         }
  1117.         if (!$startup->getCity()) {
  1118.             $startup->setCity($team->getAddressCity());
  1119.         }
  1120.         if (!$startup->getEmployeesCount()) {
  1121.             $startup->setEmployeesCount($team->getNEmployees());
  1122.         }
  1123.         if (!$startup->getContactPerson()) {
  1124.             $startup->setContactPerson($team->getPerson());
  1125.         }
  1126.         if(!$startup->getLogoUrl() and $team->getLogoUrl()) {
  1127.             $startup->setLogoUrl($team->getLogoURL());
  1128.         }
  1129.         if (!$startup->getContactEmail()) {
  1130.             $startup->setContactEmail($team->getConnectEmail());
  1131.         }
  1132.         if (!$startup->getContactTelefon()) {
  1133.             $startup->setContactTelefon($team->getPhone());
  1134.         }
  1135.         if (!$startup->getStage()) {
  1136.             $startup->setStage('idea');
  1137.         }
  1138.         if (!$startup->getCustomerFocus()) {
  1139.             $startup->setCustomerFocus('B2B');
  1140.         }
  1141.         if (!$startup->getIndustry()) {
  1142.             $startup->setIndustry($team->getIndustries());
  1143.         }
  1144.         $startup->setTags($tags);
  1145.         if (!$startup->getOneSentencePitch()) {
  1146.             $startup->setOneSentencePitch($this->sanitizeDescription($team->getDescription()));
  1147.         }
  1148.         $startup->setLastChangedBy($adminEmail);
  1149.         $startup->setTodos(implode(','$todos));
  1150.         $startup->setLastModified($now);
  1151.         $em->persist($startup);
  1152.         $em->flush();
  1153.         $responseText .= "application has been imported";
  1154.         if ($user and $importTag) {
  1155.             if (!$startupAttribute $em->getRepository(StartupAttribute::class)->findOneBy(['startupId'=> $startup->getId(), 'teamId'=>$user->getMemberId()])) {
  1156.                 $startupAttribute = new StartupAttribute();
  1157.                 $startupAttribute->setTeamId($user->getMemberId());
  1158.                 $startupAttribute->setStartupId($startup->getId());
  1159.             }
  1160.             $startupAttribute->setTeamTags($em->getRepository(Tag::class)->addHashTag($importTag$startupAttribute->getTeamTags()));
  1161.             $startupAttribute->setLastModified($now);
  1162.             $startupAttribute->setLastChangedBy($adminEmail);
  1163.             $em->persist($startupAttribute);
  1164.             $em->flush();
  1165.             $em->getRepository(StartupRelevance::class)->updateByUserAction($startup->getId(), 'create');
  1166.         }
  1167.         if (!$team->getStartupId()) {
  1168.             $team $em->getRepository(Team::class)->setStartupId($team$startup->getId(), $adminEmail);
  1169.             $responseText .= " startupId has been set in team";
  1170.         }
  1171.         $responseText .= " done";
  1172.         return $responseText;
  1173.     }
  1174.     public function updateWebsiteByTeamHomepage(Startup $startup$homepage)
  1175.     {
  1176.         $domain $this->cleanDomain($homepage);
  1177.         $startup->setDomain($domain);
  1178.         $startup->setWebsite($this->sanitizeWebsite($homepage));
  1179.         return $startup;
  1180.     }
  1181.     public function syncDataWithTeam(Startup $startupTeam $team)
  1182.     {
  1183.         $update false;
  1184.         if ($team->getShortName() and $startup->getName() != $team->getShortName()) {
  1185.             $startup->setName($team->getShortName());
  1186.             $update true;
  1187.         }
  1188.         if ($team->getLogoURL() and $startup->getLogoUrl() != $team->getLogoURL()) {
  1189.             $startup->setLogoUrl($team->getLogoURL());
  1190.             $update true;
  1191.         }
  1192.         if ($team->getHomepage() and $startup->getWebsite() != $team->getHomepage()) {
  1193.             $startup $this->updateWebsiteByTeamHomepage($startup$team->getHomepage());
  1194.             $update true;
  1195.         }
  1196.         if ($team->getLinkedin() and $startup->getLinkedin() != $team->getLinkedin()) {
  1197.             $startup->setLinkedin($team->getLinkedin());
  1198.             $update true;
  1199.         }
  1200.         if ($team->getXing() and $startup->getXing() != $team->getXing()) {
  1201.             $startup->setXing($team->getXing());
  1202.             $update true;
  1203.         }
  1204.         if ($team->getTwitter() and $startup->getTwitter() != $team->getTwitter()) {
  1205.             $startup->setTwitter($team->getTwitter());
  1206.             $update true;
  1207.         }
  1208.         if ($team->getFacebook() and $startup->getFacebook() != $team->getFacebook()) {
  1209.             $startup->setFacebook($team->getFacebook());
  1210.             $update true;
  1211.         }
  1212.         if ($team->getInstagram() and $startup->getInstagram() != $team->getInstagram()) {
  1213.             $startup->setInstagram($team->getInstagram());
  1214.             $update true;
  1215.         }
  1216.         if ($update) {
  1217.             $startup->setScore($this->setStartupCompletenessScore($startup));
  1218.             $em $this->getEntityManager();
  1219.             $em->persist($startup);
  1220.             $em->flush();
  1221.         }
  1222.         return $startup;
  1223.     }
  1224.     public function updateSlugByStartupName(Startup $startup)
  1225.     {
  1226.         $slug Utility::generateSlug($startup->getName());
  1227.         if ($startup->getSlug() != $slug) {
  1228.             $connection $this->getEntityManager()->getConnection();
  1229.             $oldSlug $startup->getSlug();
  1230.             if ($oldSlug != null) {
  1231.                 // replace old slug with new slug
  1232.                 // in memberPosts
  1233.                 $sql "
  1234.                 UPDATE sp_content_posts 
  1235.                 SET tags = REPLACE(tags,:oldSlug,:slug)
  1236.                 WHERE tags LIKE :queryString
  1237.             ";
  1238.                 $connection->executeQuery($sql, ['oldSlug'=>$oldSlug'slug'=>$slug'queryString' => '%'.$oldSlug.'%']);
  1239.                 // in feeds
  1240.                 $sql "
  1241.                 UPDATE sp_content_feed
  1242.                 SET tags = REPLACE(tags,:oldSlug,:slug)
  1243.                 WHERE tags LIKE :queryString
  1244.             ";
  1245.                 $connection->executeQuery($sql, ['oldSlug'=>$oldSlug'slug'=>$slug'queryString' => '%"'.$oldSlug.'"%']);
  1246.             }
  1247.             $startup->setSlug($slug);
  1248.         }
  1249.         return $startup;
  1250.     }
  1251.     public function updateByEdit(Startup $startup$differences$adminEmail)
  1252.     {
  1253.         $em $this->getEntityManager();
  1254.         if ($differences) {
  1255.             foreach ($differences as $diff => $value) {
  1256.                 switch ($diff) {
  1257.                     case "id":
  1258.                         break;
  1259.                     case "website":
  1260.                         $startup->setWebsite(rtrim((string) $value"/"));
  1261.                         $domain $this->cleanDomain($startup->getWebsite());
  1262.                         if ($startup->getDomain() != $domain) {
  1263.                             $startup->setDomain($domain);
  1264.                         }
  1265.                         break;
  1266.                     default:
  1267.                         $method 'set' ucfirst((string) $diff);
  1268.                         $startup->$method($value);
  1269.                         break;
  1270.                 }
  1271.             }
  1272.             $startup->setScore($this->setStartupCompletenessScore($startup));
  1273.             $startup->setLastChangedBy($adminEmail);
  1274.             $em->persist($startup);
  1275.             $em->flush();
  1276.         }
  1277.         return $startup;
  1278.     }
  1279.     public function setComments(Startup $startup$addNote$adminEmail)
  1280.     {
  1281.         $em $this->getEntityManager();
  1282.         $startup->setLastChangeUser($adminEmail);
  1283.         $startup->setComments('==== 'date("Y-m-d") . ' by ' $startup->getLastChangeUser() .' ==='PHP_EOL $addNote   PHP_EOL $startup->getComments());
  1284.         $logText "added Comment";
  1285.         $startup->setHistory('==== 'date("Y-m-d") . ' by ' $startup->getLastChangeUser() .' ==='PHP_EOL $logText   PHP_EOL $startup->getHistory());
  1286.         $em->persist($startup);
  1287.         $em->flush();
  1288.         return $startup;
  1289.     }
  1290.     public function importOrUpdate($feedback$fieldsUser $user$template$importTag)
  1291.     {
  1292.         $em $this->getEntityManager();
  1293.         $now = new DateTime();
  1294.         $adminEmail $user->getEmail();
  1295.         $responseContent = [];
  1296.         $exportList      = [];
  1297.         $preFields       = [];
  1298.         $j 0;
  1299.         foreach($feedback as $row) {
  1300.             $j++;
  1301.             $responseText "Case #{$j} ";
  1302.             // check if array in row contains as many elements as fields
  1303.             if ((is_countable($fields) ? count($fields) : 0) != (is_countable($row) ? count($row) : 0)) {
  1304.                 $i 0;
  1305.                 foreach ($fields as $field) {
  1306.                     $newRow$field ] = $row[$i] ?? '' ;
  1307.                     $i++;
  1308.                 }
  1309.                 $row $newRow;
  1310.             } else {
  1311.                 $row array_combine($fields$row);
  1312.             }
  1313.             if (array_key_exists('action'$row) and  $row['action'] == 'checkUrl') {
  1314.                 $preData = [
  1315.                     'sp_id'      => '99999',
  1316.                     'sp_teamId'  => '77777',
  1317.                     'sp_name'    => 'n.a.',
  1318.                     'sp_website' => 'n.a.',
  1319.                 ];
  1320.                 $preFields array_keys($preData);
  1321.                 if (!$row['website']) {
  1322.                     $exportList[] = implode("\t"array_merge($preData$row));
  1323.                     continue;
  1324.                 }
  1325.                 $domain $this->cleanDomain($row['website']);
  1326.                 if ($startup $this->findOneByLike('domain'$domain)) {
  1327.                     $preData = [
  1328.                         'sp_id'      => $startup->getId(),
  1329.                         'sp_teamId'  => $startup->getTeamId(),
  1330.                         'sp_name'    => $startup->getName(),
  1331.                         'sp_website' => $startup->getWebsite(),
  1332.                     ];
  1333.                 }
  1334.                 $exportList[] = implode("\t"array_merge($preData$row));
  1335.                 continue;
  1336.             }
  1337.             if (array_key_exists('action'$row)) {
  1338.                 $stopProcessing false;
  1339.                 switch ($row['action']) {
  1340.                     case "addApplication":
  1341.                         $responseContent[] = $this->importAddApplication($row$fields$adminEmail);
  1342.                         $stopProcessing true;
  1343.                         break;
  1344.                     case "delete":
  1345.                         $em->remove($startup);
  1346.                         $em->flush();
  1347.                         $responseContent[] = $responseText " entry permanently removed";
  1348.                         $stopProcessing true;
  1349.                         break;
  1350.                 }
  1351.                 if ($stopProcessing) {
  1352.                     continue; // go to next iteration in loop
  1353.                 }
  1354.             }
  1355.             if (!$startup $this->findOneBy(['id'=>$row['id']])) {
  1356.                 // check if startup is already imported
  1357.                 if (!$startup $this->findAlreadyImportedstartup($row$template)) {
  1358.                     $startup = new Startup();
  1359.                 }
  1360.             } else {
  1361.                 $responseText .= "{$startup->getId()} ";
  1362.             }
  1363.             if (in_array('phone'array_keys($row)) or in_array('website'array_keys($row)) or in_array('segment'array_keys($row))) {
  1364.                 $row $this->sanitizeRowData($row);
  1365.             }
  1366.             // get rid of elements with empty value
  1367.             // $row = array_filter($row);
  1368.             $startupData $this->fillEntityData($startup$fields);
  1369.             if ($startup->getImportNote() == 'foundByDomain') {
  1370.                 if (array_key_exists('overwrite'$row) and $row['overwrite'] == 'yes') {
  1371.                     $responseText .= "content will be overwritten ";
  1372.                 } else {
  1373.                     $exportList[] = implode("\t"$startupData + ['overwrite' => 'no']) ;
  1374.                     $exportList[] = implode("\t"$this->fillRow($row$fields) + ['overwrite' => 'no']);
  1375.                     $responseContent[] = $responseText "no import - foundByDomain";
  1376.                     continue;
  1377.                 }
  1378.                 $startup->setImportNote('');
  1379.             }
  1380.             $differences array_diff($row$startupData);
  1381.             $differences2 array_diff($startupData$row);
  1382.             $differences $differences $differences2;
  1383.             if ($differences) {
  1384.                 if ($startup->getId()) {
  1385.                     $responseText .= "updated on ";
  1386.                 } else {
  1387.                     $responseText .= "imported ";
  1388.                 }
  1389.                 foreach ($differences as $diff => $value) {
  1390.                     switch ($diff) {
  1391.                         case "id":
  1392.                             break;
  1393.                         case "action":
  1394.                             if ($row['action'] == 'checkLogo') {
  1395.                                 $startup $this->checkLogo($startup);
  1396.                                 if ($startup->getImportNote() and $startup->getImportNote() != "Bad Request") {
  1397.                                     $suggestions json_decode((string) $startup->getImportNote());
  1398.                                     $exportParts '';
  1399.                                     $exportPartsArray = [];
  1400.                                     foreach ($suggestions as  $suggestion) {
  1401.                                         $columns array_values((array) $suggestion);
  1402.                                         $exportPartsArray = [...$exportPartsArray, ...$columns];
  1403.                                     }
  1404.                                     $exportList[] = implode("\t"$this->fillRow($row$fields) + $exportPartsArray);
  1405.                                     $startup->setImportNote('');
  1406.                                 }
  1407.                             }
  1408.                             break;
  1409.                         case "website":
  1410.                             $startup->setWebsite(rtrim((string) $row['website'], "/"));
  1411.                             $domain $this->cleanDomain($startup->getWebsite());
  1412.                             if ($startup->getDomain() != $domain) {
  1413.                                 $startup->setDomain($domain);
  1414.                             }
  1415.                             break;
  1416.                         case "domain":
  1417.                             if (!$domain $row['domain']) {
  1418.                                 if (array_key_exists('website'$row)) {
  1419.                                     $domain $this->cleanDomain($row['website']);
  1420.                                 }
  1421.                             }
  1422.                             $startup->setDomain($domain);
  1423.                             break;
  1424.                         case "pitchNumber":
  1425.                             // UP-024: pitchNumber field removed - creating application only
  1426.                             $program $em->getRepository(Program::class /*Startup Program*/)->findOneBy(['id'=>1]);
  1427.                             $em->persist($startup);
  1428.                             $em->flush();
  1429.                             $startupArray['id'] = $startup->getId();
  1430.                             $responseText .=  $em->getRepository(Application::class)->createApplicationByStartup($startupArray$program$adminEmail);
  1431.                             break;
  1432.                         case "todos":
  1433.                             $todosArrayStored = [];
  1434.                             if ($startup->getTodos()) {
  1435.                                 $todosArrayStored json_decode((string) $startup->getTodos(), true);
  1436.                             }
  1437.                             $todosArrayImported explode(','str_replace(', '',', (string) $row['todos']));
  1438.                             $todosArray array_merge($todosArrayImported$todosArrayStored);
  1439.                             array_filter($todosArray);
  1440.                             $todosArray array_unique($todosArray);
  1441.                             $todosJson json_encode(array_values($todosArray));
  1442.                             $startup->setTodos($todosJson);
  1443.                             break;
  1444.                         case "tags":
  1445.                             $startup->setTags($em->getRepository(Tag::class)->updateHashTags($row['tags'], $startup->getTags()));
  1446.                             break;
  1447.                         case "status":
  1448.                             $startup->setStatus($row['status'] ?: 'unknown');
  1449.                             break;
  1450.                         case "startplatz":
  1451.                         case "batch":
  1452.                         case "batchLast":
  1453.                         case "pitchDate":
  1454.                             // UP-024: These fields removed from Startup - managed via Participation entity
  1455.                             break;
  1456.                         case "city":
  1457.                             if ($row['city'] == 'CGN' or $row['city'] == 'DUS') {
  1458.                                 $startup->setLocation($row['city']);
  1459.                                 if ($row['city'] == 'CGN') {
  1460.                                     $startup->setCity('Köln');
  1461.                                 }
  1462.                                 if ($row['city'] == 'DUS') {
  1463.                                     $startup->setCity('Düsseldorf');
  1464.                                 }
  1465.                             } else {
  1466.                                 $startup->setCity($row['city']);
  1467.                             }
  1468.                             break;
  1469.                         default:
  1470.                             $method 'set' ucfirst($diff);
  1471.                             $startup->$method(trim((string) $row[$diff]));
  1472.                             break;
  1473.                     }
  1474.                     $responseText .= {$diff} with  {$row[$diff]}, ";
  1475.                 }
  1476.                 if ($startup->getWebsite() and !$startup->getDomain()) {
  1477.                     $startup->setDomain($this->cleanDomain($startup->getWebsite()));
  1478.                 }
  1479.                 if ($startup->getName() and !$startup->getSlug()) {
  1480.                     $startup $this->updateSlugByStartupName($startup);
  1481.                 }
  1482.                 if (!$startup->getStatus()) {
  1483.                     $startup->setStatus('unknown');
  1484.                 }
  1485.                 // UP-024: startplatz, accelerator, rheinlandPitch fields removed
  1486.                 // These are now managed via Participation entity
  1487.                 if (!$startup->getIndustry()) {
  1488.                     $startup->setIndustry('Other');
  1489.                 }
  1490.                 if (!$startup->getIndustryCondensed()) {
  1491.                     $startup->setIndustryCondensed('Other');
  1492.                 }
  1493.                 if (!$startup->getCustomerFocus()) {
  1494.                     $startup->setCustomerFocus('unknown');
  1495.                 }
  1496.                 if (!$startup->getBusinessModel()) {
  1497.                     $startup->setBusinessModel('Other');
  1498.                 }
  1499.                 $startup->setLastChangedBy($adminEmail);
  1500.                 $em->persist($startup);
  1501.                 $em->flush();
  1502.                 // check if teamId
  1503.                 if ($startup->getTeamId()>0) {
  1504.                     $em->getRepository(Team::class)->setStartupIdIfNotExists($startup->getTeamId(), $startup->getId(), $adminEmail);
  1505.                 }
  1506.                 if (!$startupAttribute $em->getRepository(StartupAttribute::class)->findOneBy(['startupId'=> $startup->getId(), 'teamId'=>$user->getMemberId()])) {
  1507.                     $startupAttribute = new StartupAttribute();
  1508.                     $startupAttribute->setTeamId($user->getMemberId());
  1509.                     $startupAttribute->setStartupId($startup->getId());
  1510.                 }
  1511.                 $em->getRepository(StartupRelevance::class)->updateByUserAction($startup->getId(), 'create');
  1512.                 $startupAttribute->setTeamTags($em->getRepository(Tag::class)->addHashTag($importTag$startupAttribute->getTeamTags()));
  1513.                 $startupAttribute->setLastModified($now);
  1514.                 $startupAttribute->setLastChangedBy($adminEmail);
  1515.                 $em->persist($startupAttribute);
  1516.                 $em->flush();
  1517.                 $responseContent[] = $responseText "row has been imported";
  1518.             } else {
  1519.                 $responseContent[] = $responseText "no update";
  1520.             }
  1521.             //$responseContent[] = $startupData;
  1522.         }
  1523.         return ['responseContent' =>$responseContent'exportList' => $exportList'preFields' => $preFields];
  1524.     }
  1525.     private function importAddApplication($row$fields$adminEmail)
  1526.     {
  1527.         $em $this->getEntityManager();
  1528.         $now = new \DateTime();
  1529.         $domain  $this->extractDomainByWebsite($row['website']);
  1530.         $startup $this->findOrCreateStartupByDomain($domain);
  1531.         $batch   $em->getRepository(Batch::class)->findOneBy(['programId'=>$row['programId'], 'batchNumber'=>$row['batchNumber']]);
  1532.         /** @var Program $program */
  1533.         $program $batch->getProgram();
  1534.         $application $em->getRepository(Application::class)->findOrCreateApplicationByWebsiteUrl($row['website']);
  1535.         if (!$startup->getId()) {
  1536.             $startup->setName($row['name']);
  1537.             $startup->setWebsite($row['website']);
  1538.             $startup->setOneSentencePitch($row['oneSentencePitch']);
  1539.             $startup->setContactEmail($row['email']);
  1540.             $startup->setLastChangedBy($adminEmail);
  1541.             $startup->setCreatedAt($now);
  1542.             $startup->setLastModified($now);
  1543.             $em->persist($startup);
  1544.             $em->flush();
  1545.         }
  1546.         if (!$teamId $startup->getTeamId()) {
  1547.             // find virtual team of that batch
  1548.             $settings = (array) json_decode((string) $batch->getSettings(), true);
  1549.             $team     $em->getRepository(Team::class)->findOneBy(['shortName'=>$settings['virtualTeam']]);
  1550.             $teamId   $team->getId();
  1551.         }
  1552.         $application->setStartupId($startup->getId());
  1553.         $application->setTeamId($teamId);
  1554.         $application->setEmail($row['email']);
  1555.         $application->setStartupName($row['name']);
  1556.         $application->setProgramId($row['programId']);
  1557.         $application->setBatchId($batch->getId());
  1558.         $application->setBatchName($batch->getName());
  1559.         $application->setBatchSlug($batch->getSlug());
  1560.         $application->setTemplate($batch->getTemplate());
  1561.         $application->setProgramSlug($program->getSlug());
  1562.         $application->setProgramName($program->getName());
  1563.         $application->setApplicationStatus('approved');
  1564.         $application->setBatchStatus($row['status']);
  1565.         $application->setIdeaPitch($row['oneSentencePitch']);
  1566.         $application->setLastChangeUser($adminEmail);
  1567.         $application->setCreatedAt($now);
  1568.         $application->setLastModified($now);
  1569.         $em->persist($application);
  1570.         $em->flush();
  1571.         return "application created with id={$application->getId()}";
  1572.     }
  1573.     private function findOrCreateStartupByDomain($domain)
  1574.     {
  1575.         $em $this->getEntityManager();
  1576.         if (!$startup $this->findOneBy(['domain'=>$domain])) {
  1577.             $startup = new Startup();
  1578.             $startup->setDomain($domain);
  1579.         }
  1580.         return $startup;
  1581.     }
  1582.     public function setEmptyStartup()
  1583.     {
  1584.         return new Startup();
  1585.     }
  1586.     private function findOneByLike($field$criterium)
  1587.     {
  1588.         $qb $this->getEntityManager()->createQueryBuilder();
  1589.         $qb
  1590.             ->select('c')
  1591.             ->from(Startup::class, 'c')
  1592.             ->where("c.{$field} LIKE :{$field}")
  1593.             ->setParameter($field"%{$criterium}%")
  1594.             ->setMaxResults(1)
  1595.             ->orderBy('c.lastModified''DESC')
  1596.         ;
  1597.         return $qb->getQuery()->getOneOrNullResult();
  1598.     }
  1599.     private function fillRow($row$fields)
  1600.     {
  1601.         foreach ($fields as $field) {
  1602.             $oldData[$field] =  array_key_exists($field$row) ? $row[$field] : '' ;
  1603.         }
  1604.         return $oldData;
  1605.     }
  1606.     private function fillEntityData($entity$fields)
  1607.     {
  1608.         foreach ($fields as $field) {
  1609.             $method 'get' ucfirst((string) $field);
  1610.             if ($entity->$method() instanceof DateTime) {
  1611.                 $oldData[$field] = $entity->$method()->format('Y-m-d') ;
  1612.             } elseif (in_array($field, ['todos'])) {
  1613.                 if ($entity->$method()) {
  1614.                     $oldData[$field] = implode(','json_decode((string) $entity->$method(), true));
  1615.                 } else {
  1616.                     $oldData[$field] = '';
  1617.                 }
  1618.             } else {
  1619.                 $oldData[$field] = trim((string) $entity->$method());
  1620.             }
  1621.             // UP-024: Removed 'startplatz' and 'accelerator' - fields no longer exist
  1622.             if (in_array($field, ['status']) and !$oldData[$field]) {
  1623.                 $oldData[$field] = 'default';
  1624.             }
  1625.         }
  1626.         return $oldData;
  1627.     }
  1628.     private function sanitizeRowData($row)
  1629.     {
  1630.         if (in_array('phone'array_keys($row))) {
  1631.             $row['phone'] = $this->sanitizePhone($row['phone']);
  1632.         }
  1633.         if (in_array('website'array_keys($row)) and $row['website']) {
  1634.             $row['website'] = $this->sanitizeWebsite($row['website']);
  1635.         }
  1636.         if (in_array('todos'array_keys($row)) and $row['todos']) {
  1637.             $row['todos'] = str_replace(', '',', (string) $row['todos']);
  1638.         }
  1639.         if (in_array('segment'array_keys($row))) {
  1640.             $row['segment'] = $this->sanitizeSegment($row['segment']);
  1641.         }
  1642.         return $row;
  1643.     }
  1644.     private function sanitizeWebsite($website)
  1645.     {
  1646.         $website strtolower((string) $website);
  1647.         if (!(str_starts_with($website'https://') or str_starts_with($website'http://'))) {
  1648.             $website 'https://' $website;
  1649.         }
  1650.         $website str_replace('http://''https://'$website);
  1651.         return $website;
  1652.     }
  1653.     private function sanitizePhone($phone)
  1654.     {
  1655.         $phone str_replace('+49 (0) ''+49-', (string) $phone);
  1656.         $phone str_replace('+49 ''+49-'$phone);
  1657.         $phone str_replace('/''-'$phone);
  1658.         $phone str_replace('('''$phone);
  1659.         $phone str_replace(') ''-'$phone);
  1660.         $phone str_replace(' '''$phone);
  1661.         if (substr((string) $phone01) === 0) {
  1662.             $phone '+49-' substr((string) $phone1);
  1663.         }
  1664.         if (str_starts_with($phone'49')) {
  1665.             $phone '+' $phone;
  1666.         }
  1667.         if (substr((string) $phone31) !== '-') {
  1668.             $phone substr((string) $phone03) . '-' substr((string) $phone3);
  1669.         }
  1670.         return $phone;
  1671.     }
  1672.     private function sanitizeDescription($description)
  1673.     {
  1674.         $string str_replace("\r\n""<br>", (string) $description);
  1675.         $string str_replace("\r"""$string);
  1676.         $string str_replace("\t"""$string);
  1677.         $string str_replace("\n"""$string);
  1678.         $string str_replace("<p>"""$string);
  1679.         $string str_replace("</p>"""$string);
  1680.         return $string;
  1681.     }
  1682.     private function findAlreadyImportedStartup($row$template)
  1683.     {
  1684.         $startup null;
  1685.         switch ($template) {
  1686.             case "ddw":
  1687.                 if (!$startup $this->findOneBy(['ddwId'=>$row['ddwId']])) {
  1688.                     if ($startup $this->findByWebsite($row['website'])) {
  1689.                         $startup->setImportNote('foundByWebsite');
  1690.                     }
  1691.                 }
  1692.                 break;
  1693.             case "bizcover":
  1694.                 if (!$startup $this->findByRegister($row['registerGericht'], $row['registerNumber'])) {
  1695.                     $startup $this->findByWebsite($row['website']);
  1696.                     if ($startup and $startup->getRegisterGericht()) {
  1697.                         $startup null;
  1698.                     } else {
  1699.                         $startup->setImportNote('foundByWebsite');
  1700.                     }
  1701.                 }
  1702.                 break;
  1703.             case "default":
  1704.                 if (array_key_exists('extId'$row)) {
  1705.                     if ($startup $this->findOneBy(['extId'=>$row['extId']])) {
  1706.                         $startup->setImportNote('foundByExtId');
  1707.                         break;
  1708.                     }
  1709.                 }
  1710.                 // check if domain is set
  1711.                 if (array_key_exists('domain'$row)) {
  1712.                     if (!$domain $row['domain']) {
  1713.                         if (array_key_exists('website'$row) and $row['website']) {
  1714.                             $domain $this->cleanDomain($row['website']);
  1715.                         }
  1716.                     }
  1717.                     if ($startup $this->findOneBy(['domain'=>$domain])) {
  1718.                         $startup->setImportNote('foundByDomain');
  1719.                     }
  1720.                 }
  1721.                 break;
  1722.         }
  1723.         return $startup;
  1724.     }
  1725.     private function setSlug($name)
  1726.     {
  1727.         return Utility::generateSlug($name);
  1728.     }
  1729.     private function cleanDomain($website)
  1730.     {
  1731.         $website str_replace("https://www.""", (string) $website);
  1732.         $website str_replace("http://www."""$website);
  1733.         $website str_replace("https://"""$website);
  1734.         $website str_replace("http://"""$website);
  1735.         $website explode('/'$website)[0];
  1736.         return $website;
  1737.     }
  1738.     private function extractDomainByWebsite($website)
  1739.     {
  1740.         return $this->cleanDomain($website);
  1741.     }
  1742.     private function extractDomainByEmail($email)
  1743.     {
  1744.         return explode('@', (string) $email)[1];
  1745.     }
  1746.     private function getMailServices()
  1747.     {
  1748.         return [
  1749.             "aol.com",
  1750.             "aol.de",
  1751.             "arcor.de",
  1752.             "gmail.com",
  1753.             "gmx.de",
  1754.             "gmx.net",
  1755.             "googlemail.com",
  1756.             "hotmail.com",
  1757.             "hotmail.de",
  1758.             "icloud.com",
  1759.             "mailbox.org",
  1760.             "me.com",
  1761.             "outlook.de",
  1762.             "posteo.de",
  1763.             "yahoo.de",
  1764.         ];
  1765.     }
  1766.     private function getDomainServices()
  1767.     {
  1768.         return [
  1769.             "linkedin.com",
  1770.             "linkedin.de",
  1771.             "facebook",
  1772.         ];
  1773.     }
  1774.     public function updateLogo($startup): void
  1775.     {
  1776.         $startup $this->checkLogo($startup);
  1777.         if ($startup->getImportNote()) {
  1778.             $suggestions json_decode((string) $startup->getImportNote());
  1779.             $exportParts '';
  1780.             $exportPartsArray = [];
  1781.             foreach ($suggestions as  $suggestion) {
  1782.                 $columns array_values((array) $suggestion);
  1783.                 $exportPartsArray = [...$exportPartsArray, ...$columns];
  1784.             }
  1785.             $exportList[] = implode("\t"$exportPartsArray);
  1786.         }
  1787.     }
  1788.     private function checkLogo(Startup $startup)
  1789.     {
  1790.         $url "https://autocomplete.clearbit.com/v1/companies/suggest?query={$startup->getName()}";
  1791.         if (!$result $this->curl_get($url)) {
  1792.             $domain str_replace('https://'''$startup->getWebsite());
  1793.             $domain str_replace('www.'''$domain);
  1794.             $domain explode("/"$domain)[0];
  1795.             $url "https://autocomplete.clearbit.com/v1/companies/suggest?query={$domain}";
  1796.             $result $this->curl_get($url);
  1797.         }
  1798.         if ($result) {
  1799.             $startup->setImportNote($result);
  1800.         }
  1801.         return $startup;
  1802.     }
  1803.     private function checkDomain(Startup $startup$row)
  1804.     {
  1805.         $url "https://autocomplete.clearbit.com/v1/companies/suggest?query={$row['domain']}";
  1806.         if ($result $this->curl_get($url)) {
  1807.             $startup->setImportNote($result);
  1808.         }
  1809.         return $startup;
  1810.     }
  1811.     public function curl_get($Url$login false)
  1812.     {
  1813.         // is cURL installed yet?
  1814.         if (!function_exists('curl_init')) {
  1815.             die('Sorry cURL is not installed!');
  1816.         }
  1817.         // OK cool - then let's create a new cURL resource handle
  1818.         $ch curl_init();
  1819.         // Set URL to call back
  1820.         curl_setopt($chCURLOPT_URL$Url);
  1821.         // Should cURL return or print out the data? (true = return, false = print)
  1822.         curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
  1823.         $headers = ['Content-Type:application/json'];
  1824.         curl_setopt($chCURLOPT_HTTPHEADER$headers);
  1825.         // Download the given URL, and return output
  1826.         $output curl_exec($ch);
  1827.         // Close the cURL resource, and free system resources
  1828.         curl_close($ch);
  1829.         return $output;
  1830.     }
  1831.     public function getTemplateVars($request)
  1832.     {
  1833.         $templateVars = ['filter'  => null'tag'     => []];
  1834.         if ($tag $request->query->get('tag')) {
  1835.             $templateVars['tag'] = trim((string) $tag);
  1836.         }
  1837.         if ($location $request->query->get('location')) {
  1838.             $templateVars['location']   = $location;
  1839.         }
  1840.         if ($status $request->query->get('status')) {
  1841.             $status = (!in_array($status, ['active''inactive']) ? 'active' $status);
  1842.             $templateVars['status']   = $status;
  1843.         }
  1844.         if ($criteria $request->query->get('criteria')) {
  1845.             $templateVars['criteria'] = $criteria;
  1846.         } else {
  1847.             $templateVars['criteria'] = [];
  1848.         }
  1849.         if ($dimensions $request->query->get('dimension')) {
  1850.             $templateVars['dimensions'] = $dimensions;
  1851.         } else {
  1852.             $templateVars['dimensions'] = [];
  1853.         }
  1854.         if ($filter $request->query->get('filter') and !$page $request->get('page')) {
  1855.             $templateVars['filter']   = $filter;
  1856.             if (preg_match("/:/", (string) $filter)) {
  1857.                 $parameters explode(':', (string) $filter);
  1858.                 if (isset($templateVars['criteria']['id'])) {
  1859.                     unset($templateVars['criteria']['id']);
  1860.                 }
  1861.                 if ($parameters[0] == 'isBookmarked') {
  1862.                     $templateVars['criteria'] = [];
  1863.                 }
  1864.                 if (isset($templateVars['criteria'][$parameters[0]]) and $templateVars['criteria'][$parameters[0]] == $parameters[1]) {
  1865.                     unset($templateVars['criteria'][$parameters[0]]);
  1866.                 } else {
  1867.                     $templateVars['criteria'][$parameters[0]] = $parameters[1];
  1868.                     $templateVars['filter']   = null;
  1869.                 }
  1870.             } else {
  1871.                 if ($filter == 'none') {
  1872.                     $templateVars['criteria'] = [];
  1873.                     $templateVars['filter']   = null;
  1874.                 } else {
  1875.                     $templateVars['filter']   = null;
  1876.                 }
  1877.             }
  1878.         }
  1879.         if ($sort $request->query->get('sort')) {
  1880.             $templateVars['sort']   = $sort;
  1881.             if (preg_match("/:/", (string) $sort)) {
  1882.                 $parameters explode(':', (string) $sort);
  1883.                 $templateVars['dimensions'][$parameters[0]] = $parameters[1];
  1884.             } else {
  1885.                 $sort 'none';
  1886.                 switch ($sort) {
  1887.                     default:
  1888.                         $templateVars['dimensions'] = [];
  1889.                         $templateVars['sort']   = null;
  1890.                         break;
  1891.                 }
  1892.             }
  1893.         }
  1894.         if ($fields $request->query->get('fields')) {
  1895.             $templateVars['fields']   = $fields;
  1896.         }
  1897.         return $templateVars;
  1898.     }
  1899.     public function getAggregateByGroup($group$criteria$order = [], $exact false)
  1900.     {
  1901.         if (empty($group)) {
  1902.             return [];
  1903.         }
  1904.         // UP-024: Validate field exists before querying to prevent QueryException
  1905.         $metadata $this->getClassMetadata();
  1906.         if (!$metadata->hasField($group)) {
  1907.             return [];
  1908.         }
  1909.         $qb $this->getEntityManager()->createQueryBuilder();
  1910.         $qb->select(['s.' $group'COUNT(s.id) as number']);
  1911.         $qb->from(Startup::class, 's');
  1912.         if ($exact) {
  1913.             $qb->where("s.{$group}>''");
  1914.         }
  1915.         if ($criteria) {
  1916.             $qb $this->setCriteria($qb$criteria);
  1917.         }
  1918.         $qb->groupBy('s.'$group);
  1919.         if ($order) {
  1920.             $metadata $this->getClassMetadata();
  1921.             foreach ($order as $key => $value) {
  1922.                 if ($metadata->hasField($key)) {
  1923.                     $qb->addOrderBy('s.' $key$value);
  1924.                 }
  1925.                 // UP-024: Skip non-existent fields (removed: pitchDate, etc.)
  1926.             }
  1927.         } else {
  1928.             $qb->addOrderBy('s.'$group'ASC');
  1929.         }
  1930.         return $qb->getQuery()->getResult();
  1931.     }
  1932.     // --- 1:n Team → Startup (UP-024) ---
  1933.     /**
  1934.      * Find all startups for a team.
  1935.      *
  1936.      * Since UP-024, a team can have multiple startups (1:n relationship).
  1937.      *
  1938.      * @return Startup[]
  1939.      */
  1940.     public function findAllByTeam(Team $team): array
  1941.     {
  1942.         return $this->createQueryBuilder('s')
  1943.             ->where('s.teamId = :teamId')
  1944.             ->setParameter('teamId'$team->getId())
  1945.             ->orderBy('s.createdAt''DESC')
  1946.             ->getQuery()
  1947.             ->getResult();
  1948.     }
  1949.     /**
  1950.      * Find all startups for a team by team ID.
  1951.      *
  1952.      * @return Startup[]
  1953.      */
  1954.     public function findAllByTeamId(int $teamId): array
  1955.     {
  1956.         return $this->createQueryBuilder('s')
  1957.             ->where('s.teamId = :teamId')
  1958.             ->setParameter('teamId'$teamId)
  1959.             ->orderBy('s.createdAt''DESC')
  1960.             ->getQuery()
  1961.             ->getResult();
  1962.     }
  1963.     /**
  1964.      * Count startups for a team.
  1965.      */
  1966.     public function countByTeam(Team $team): int
  1967.     {
  1968.         return (int) $this->createQueryBuilder('s')
  1969.             ->select('COUNT(s.id)')
  1970.             ->where('s.teamId = :teamId')
  1971.             ->setParameter('teamId'$team->getId())
  1972.             ->getQuery()
  1973.             ->getSingleScalarResult();
  1974.     }
  1975. }