diff --git a/app/Repositories/Summit/DoctrineSpeakerRepository.php b/app/Repositories/Summit/DoctrineSpeakerRepository.php index 237042028..2fdf546a0 100644 --- a/app/Repositories/Summit/DoctrineSpeakerRepository.php +++ b/app/Repositories/Summit/DoctrineSpeakerRepository.php @@ -705,23 +705,13 @@ function ($query) { */ public function getUniqueActivitiesCountBySummit(Summit $summit, Filter $filter = null): int { - // Inner query: distinct IDs of speakers who belong to this summit (via assignment - // or moderator role) and match any caller-supplied filter. Uses IDENTITY() with a - // scalar summit ID so no entity parameter is embedded in the getDQL() string — - // entity parameters copied into an outer QB via getDQL() are not correctly resolved - // to their primary key by Doctrine's type system. + // Build a non-correlated inner QB that selects IDs of speakers matching the filter. + // MySQL can materialise this set once per query rather than re-executing per row. $innerQb = $this->getEntityManager()->createQueryBuilder() ->select('e.id') - ->distinct(true) ->from('models\summit\PresentationSpeaker', 'e') - ->leftJoin('e.registration_request', 'rr') ->leftJoin('e.member', 'm') - ->where( - 'EXISTS (SELECT 1 FROM App\Models\Foundation\Summit\Speakers\PresentationSpeakerAssignment __a' - . ' JOIN __a.presentation __ap WHERE IDENTITY(__ap.summit) = :summit_id AND __a.speaker = e)' - . ' OR EXISTS (SELECT 1 FROM models\summit\Presentation __mp WHERE IDENTITY(__mp.summit) = :summit_id AND __mp.moderator = e)' - ) - ->setParameter('summit_id', $summit->getId()); + ->leftJoin('e.registration_request', 'rr'); if (!is_null($filter)) { $filter->apply2Query($innerQb, $this->getFilterMappings($filter)); @@ -729,30 +719,35 @@ public function getUniqueActivitiesCountBySummit(Summit $summit, Filter $filter $innerDql = $innerQb->getDQL(); - // Outer query counts distinct presentations where at least one matched speaker is - // either an assigned speaker or the moderator. The inner DQL is embedded exactly - // once (inside a single wrapper EXISTS) to avoid Doctrine alias-conflict errors. - $outerQb = $this->getEntityManager()->createQueryBuilder() - ->select('COUNT(DISTINCT p.id)') + // Q1: presentation IDs where a qualifying speaker is formally assigned. + $assignmentQb = $this->getEntityManager()->createQueryBuilder() + ->select('p.id') ->from('models\summit\Presentation', 'p') + ->join('p.speakers', 'a') ->where('p.summit = :summit') - ->andWhere( - 'EXISTS (' - . 'SELECT 1 FROM models\summit\PresentationSpeaker __spk' - . ' WHERE __spk.id IN (' . $innerDql . ')' - . ' AND (' - . 'EXISTS (SELECT 1 FROM App\Models\Foundation\Summit\Speakers\PresentationSpeakerAssignment __cnt WHERE __cnt.presentation = p AND __cnt.speaker = __spk)' - . ' OR p.moderator = __spk' - . ')' - . ')' - ) + ->andWhere('a.speaker IN (' . $innerDql . ')') ->setParameter('summit', $summit); foreach ($innerQb->getParameters() as $param) { - $outerQb->setParameter($param->getName(), $param->getValue()); + $assignmentQb->setParameter($param->getName(), $param->getValue(), $param->getType()); } - return intval($outerQb->getQuery()->getSingleScalarResult()); + // Q2: presentation IDs where the moderator is a qualifying speaker. + $moderatorQb = $this->getEntityManager()->createQueryBuilder() + ->select('p.id') + ->from('models\summit\Presentation', 'p') + ->where('p.summit = :summit') + ->andWhere('p.moderator IN (' . $innerDql . ')') + ->setParameter('summit', $summit); + + foreach ($innerQb->getParameters() as $param) { + $moderatorQb->setParameter($param->getName(), $param->getValue(), $param->getType()); + } + + $assignmentIds = array_column($assignmentQb->getQuery()->getScalarResult(), 'id'); + $moderatorIds = array_column($moderatorQb->getQuery()->getScalarResult(), 'id'); + + return count(array_unique(array_merge($assignmentIds, $moderatorIds))); } /**