diff --git a/app/Repositories/Summit/DoctrineSpeakerRepository.php b/app/Repositories/Summit/DoctrineSpeakerRepository.php index 237042028..2a5dd6c41 100644 --- a/app/Repositories/Summit/DoctrineSpeakerRepository.php +++ b/app/Repositories/Summit/DoctrineSpeakerRepository.php @@ -705,23 +705,22 @@ 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 the EXISTS inner QB rooted at PresentationSpeaker (e) with the joins that + // filter mappings expect (m, rr). The initial WHERE ties e to the outer presentation + // p via assignment or moderator role; filter conditions are then appended by + // apply2Query. The EXISTS approach lets MySQL use the + // index on Presentation_Speakers(PresentationID) to reach speakers for each p directly + // and short-circuit on the first match. $innerQb = $this->getEntityManager()->createQueryBuilder() - ->select('e.id') - ->distinct(true) + ->select('1') ->from('models\summit\PresentationSpeaker', 'e') - ->leftJoin('e.registration_request', 'rr') ->leftJoin('e.member', 'm') + ->leftJoin('e.registration_request', 'rr') ->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()); + . ' WHERE __a.presentation = p AND __a.speaker = e)' + . ' OR p.moderator = e' + ); if (!is_null($filter)) { $filter->apply2Query($innerQb, $this->getFilterMappings($filter)); @@ -729,30 +728,22 @@ 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() + $qb = $this->getEntityManager()->createQueryBuilder() ->select('COUNT(DISTINCT p.id)') ->from('models\summit\Presentation', 'p') ->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('EXISTS (' . $innerDql . ')') ->setParameter('summit', $summit); + // Copy filter-specific parameter bindings (e.g. :value_N for text/id filters). + // Filter sub-queries reference :summit by name; that is satisfied by the binding above. foreach ($innerQb->getParameters() as $param) { - $outerQb->setParameter($param->getName(), $param->getValue()); + if ($param->getName() !== 'summit') { + $qb->setParameter($param->getName(), $param->getValue(), $param->getType()); + } } - return intval($outerQb->getQuery()->getSingleScalarResult()); + return intval($qb->getQuery()->getSingleScalarResult()); } /**