2121use App \Models \Foundation \Summit \Events \RSVP \RSVPInvitation ;
2222use Doctrine \ORM \Query \ResultSetMappingBuilder ;
2323use Illuminate \Support \Facades \Config ;
24+ use Doctrine \DBAL \ParameterType ;
2425use LaravelDoctrine \ORM \Facades \EntityManager ;
2526use models \summit \Presentation ;
2627use models \summit \SummitMetric ;
@@ -1832,21 +1833,44 @@ public function getLastNSponsorMemberships($last_n = 2)
18321833 */
18331834 public function getActiveSummitsSponsorMemberships ()
18341835 {
1835- $ dql = <<<DQL
1836- SELECT sp
1837- FROM models\summit\Sponsor sp
1838- JOIN sp.members m
1839- JOIN sp.summit s
1840- WHERE m.id = :member_id
1841- AND s.end_date >= :now
1842- ORDER BY s.begin_date ASC
1843- DQL ;
1844-
1845- $ query = $ this ->createQuery ($ dql );
1846- return $ query
1847- ->setParameter ('member_id ' , $ this ->getId ())
1848- ->setParameter ('now ' , new \DateTime ('now ' , new \DateTimeZone ('UTC ' )))
1849- ->getResult ();
1836+ // Step 1 — use native SQL (needed for JSON_CONTAINS) to collect IDs only.
1837+ $ idSql = <<<SQL
1838+ SELECT sp.ID
1839+ FROM Sponsor sp
1840+ INNER JOIN Sponsor_Users su ON su.SponsorID = sp.ID
1841+ INNER JOIN Summit s ON s.ID = sp.SummitID
1842+ WHERE su.MemberID = :member_id
1843+ AND s.SummitEndDate >= :now
1844+ AND (
1845+ JSON_CONTAINS(COALESCE(su.Permissions, '[]'), JSON_QUOTE(:slug_sponsors))
1846+ OR JSON_CONTAINS(COALESCE(su.Permissions, '[]'), JSON_QUOTE(:slug_external))
1847+ )
1848+ ORDER BY s.SummitBeginDate ASC
1849+ SQL ;
1850+
1851+ $ stmt = $ this ->prepareRawSQL ($ idSql , [
1852+ 'member_id ' => $ this ->getId (),
1853+ 'now ' => (new \DateTime ('now ' , new \DateTimeZone ('UTC ' )))->format ('Y-m-d H:i:s ' ),
1854+ 'slug_sponsors ' => IGroup::Sponsors,
1855+ 'slug_external ' => IGroup::SponsorExternalUsers,
1856+ ]);
1857+ $ ids = $ stmt ->executeQuery ()->fetchFirstColumn ();
1858+
1859+ if (empty ($ ids )) {
1860+ return [];
1861+ }
1862+
1863+ // Step 2 — load each Sponsor by PK. find() uses a different code path that avoids
1864+ // the ORM 3 assertion failure triggered by the OneToOne inverse associations on Sponsor
1865+ // (lead_report_setting, sponsorservices_statistics) when using DQL/native query hydration.
1866+ $ sponsors = [];
1867+ foreach ($ ids as $ id ) {
1868+ $ sponsor = $ this ->getEM ()->find (\models \summit \Sponsor::class, $ id );
1869+ if ($ sponsor !== null ) {
1870+ $ sponsors [] = $ sponsor ;
1871+ }
1872+ }
1873+ return $ sponsors ;
18501874 }
18511875
18521876 /**
@@ -1859,11 +1883,17 @@ public function getSponsorMembershipIds(Summit $summit): array
18591883FROM Sponsor_Users
18601884INNER JOIN Sponsor ON Sponsor.ID = Sponsor_Users.SponsorID
18611885WHERE MemberID = :member_id AND Sponsor.SummitID = :summit_id
1886+ AND (
1887+ JSON_CONTAINS(COALESCE(Sponsor_Users.Permissions, '[]'), JSON_QUOTE(:slug_sponsors))
1888+ OR JSON_CONTAINS(COALESCE(Sponsor_Users.Permissions, '[]'), JSON_QUOTE(:slug_external))
1889+ )
18621890SQL ;
18631891
1864- $ stmt = $ this ->prepareRawSQL ($ sql , [
1865- 'member_id ' => $ this ->getId (),
1866- 'summit_id ' => $ summit ->getId (),
1892+ $ stmt = $ this ->prepareRawSQL ($ sql , [
1893+ 'member_id ' => $ this ->getId (),
1894+ 'summit_id ' => $ summit ->getId (),
1895+ 'slug_sponsors ' => IGroup::Sponsors,
1896+ 'slug_external ' => IGroup::SponsorExternalUsers,
18671897 ]);
18681898 $ res = $ stmt ->executeQuery ();
18691899 return $ res ->fetchFirstColumn ();
@@ -1881,11 +1911,17 @@ public function hasSponsorMembershipsFor(Summit $summit, Sponsor $sponsor = null
18811911WHERE
18821912 MemberID = :member_id
18831913 AND Sponsor.SummitID = :summit_id
1914+ AND (
1915+ JSON_CONTAINS(COALESCE(Sponsor_Users.Permissions, '[]'), JSON_QUOTE(:slug_sponsors))
1916+ OR JSON_CONTAINS(COALESCE(Sponsor_Users.Permissions, '[]'), JSON_QUOTE(:slug_external))
1917+ )
18841918SQL ;
18851919
1886- $ params = [
1887- 'member_id ' => $ this ->getId (),
1888- 'summit_id ' => $ summit ->getId (),
1920+ $ params = [
1921+ 'member_id ' => $ this ->getId (),
1922+ 'summit_id ' => $ summit ->getId (),
1923+ 'slug_sponsors ' => IGroup::Sponsors,
1924+ 'slug_external ' => IGroup::SponsorExternalUsers,
18891925 ];
18901926
18911927 if (!is_null ($ sponsor )) {
@@ -1956,12 +1992,26 @@ public function addSummitRegistrationOrder(SummitOrder $summit_order)
19561992
19571993 /**
19581994 * @param Summit $summit
1995+ * @return ArrayCollection
1996+ */
1997+ public function getSponsorsBySummit (Summit $ summit ): ArrayCollection
1998+ {
1999+ return new ArrayCollection (
2000+ $ this ->sponsor_memberships ->filter (function ($ entity ) use ($ summit ) {
2001+ return $ entity ->getSummitId () == $ summit ->getId ();
2002+ })->toArray ()
2003+ );
2004+ }
2005+
2006+ /**
2007+ * @param Summit $summit
2008+ * @param int $sponsor_id
19592009 * @return Sponsor|null
19602010 */
1961- public function getSponsorBySummit (Summit $ summit ): ?Sponsor
2011+ public function getSponsorBySummitAndId (Summit $ summit, int $ sponsor_id ): ?Sponsor
19622012 {
1963- $ sponsor = $ this ->sponsor_memberships ->filter (function ($ entity ) use ($ summit ) {
1964- return $ entity ->getSummitId () == $ summit ->getId ();
2013+ $ sponsor = $ this ->sponsor_memberships ->filter (function ($ entity ) use ($ summit, $ sponsor_id ) {
2014+ return $ entity ->getSummitId () == $ summit ->getId () && $ entity -> getId () == $ sponsor_id ;
19652015 })->first ();
19662016
19672017 return $ sponsor === false ? null : $ sponsor ;
@@ -3412,6 +3462,63 @@ public function getIndividualMemberJoinDate(): ?\DateTime
34123462 return $ this ->individual_member_join_date ;
34133463 }
34143464
3465+ /**
3466+ * Appends $group_slug to the Permissions JSON array on the Sponsor_Users row
3467+ * for this member and the given sponsor. Idempotent: the slug is only added
3468+ * when it is not already present.
3469+ */
3470+ public function addSponsorPermission (int $ sponsor_id , string $ group_slug ): void
3471+ {
3472+ $ sql = <<<SQL
3473+ UPDATE Sponsor_Users
3474+ SET Permissions = IF(
3475+ JSON_CONTAINS(COALESCE(Permissions, '[]'), JSON_QUOTE(:group_slug)),
3476+ Permissions,
3477+ JSON_ARRAY_APPEND(COALESCE(Permissions, '[]'), '$', :group_slug)
3478+ )
3479+ WHERE SponsorID = :sponsor_id AND MemberID = :member_id
3480+ SQL ;
3481+ $ stmt = $ this ->prepareRawSQL ($ sql , [
3482+ 'group_slug ' => $ group_slug ,
3483+ 'sponsor_id ' => $ sponsor_id ,
3484+ 'member_id ' => $ this ->getId (),
3485+ ]);
3486+ $ stmt ->executeStatement ();
3487+ }
3488+
3489+ /**
3490+ * Removes $group_slug from the Permissions JSON array on the Sponsor_Users row
3491+ * for this member and the given sponsor, then returns how many other Sponsor_Users
3492+ * rows for this member still carry that slug. The caller uses the count to decide
3493+ * whether to also revoke the global group membership.
3494+ */
3495+ public function removeSponsorPermission (int $ sponsor_id , string $ group_slug ): int
3496+ {
3497+ $ removeSQL = <<<SQL
3498+ UPDATE Sponsor_Users
3499+ SET Permissions = JSON_REMOVE(Permissions, JSON_UNQUOTE(JSON_SEARCH(Permissions, 'one', :group_slug)))
3500+ WHERE SponsorID = :sponsor_id AND MemberID = :member_id
3501+ AND JSON_CONTAINS(COALESCE(Permissions, '[]'), JSON_QUOTE(:group_slug))
3502+ SQL ;
3503+ $ stmt = $ this ->prepareRawSQL ($ removeSQL , [
3504+ 'group_slug ' => $ group_slug ,
3505+ 'sponsor_id ' => $ sponsor_id ,
3506+ 'member_id ' => $ this ->getId (),
3507+ ]);
3508+ $ stmt ->executeStatement ();
3509+
3510+ $ countSQL = <<<SQL
3511+ SELECT COUNT(*) FROM Sponsor_Users
3512+ WHERE MemberID = :member_id
3513+ AND JSON_CONTAINS(COALESCE(Permissions, '[]'), JSON_QUOTE(:group_slug))
3514+ SQL ;
3515+ $ stmt = $ this ->prepareRawSQL ($ countSQL , [
3516+ 'member_id ' => $ this ->getId (),
3517+ 'group_slug ' => $ group_slug ,
3518+ ]);
3519+ return intval ($ stmt ->executeQuery ()->fetchOne ());
3520+ }
3521+
34153522 public function addSponsorMembership (Sponsor $ sponsor ):void
34163523 {
34173524 if ($ this ->sponsor_memberships ->contains ($ sponsor )) return ;
0 commit comments