@@ -81,59 +81,91 @@ public function getSubscribersBySubscribedListId(int $listId): array
8181 */
8282 public function getFilteredAfterId (FilterRequestInterface $ filter ): PaginatedResult
8383 {
84- $ lastId = $ filter ->getLastId ();
85- $ limit = $ filter ->getLimit ();
8684 if (!$ filter instanceof SubscriberFilter) {
87- throw new InvalidArgumentException ('Expected SubscriberFilterRequest . ' );
85+ throw new InvalidArgumentException ('Expected SubscriberFilter . ' );
8886 }
8987
90- $ queryBuilder = $ this ->createQueryBuilder ('subscriber ' )
91- ->leftJoin ('subscriber.subscriptions ' , 'subscription ' )
92- ->leftJoin ('subscription.subscriberList ' , 'list ' );
88+ $ lastId = $ filter ->getLastId ();
89+ $ limit = $ filter ->getLimit ();
9390
94- if ($ filter ->getListId () !== null ) {
95- $ queryBuilder ->andWhere ('list.id = :listId ' )
96- ->setParameter ('listId ' , $ filter ->getListId ());
97- if ($ filter ->getSubscribedDateFrom () !== null ) {
98- $ queryBuilder ->andWhere ('subscription.createdAt > :subscribedAtFrom ' )
99- ->setParameter ('subscribedAtFrom ' , $ filter ->getSubscribedDateFrom ());
91+ $ applyFilters = function (QueryBuilder $ queryBuilder ) use ($ filter ): void {
92+ $ queryBuilder
93+ ->leftJoin ('subscriber.subscriptions ' , 'subscription ' )
94+ ->leftJoin ('subscription.subscriberList ' , 'list ' );
95+
96+ $ this ->applyListIdFilter ($ filter , $ queryBuilder );
97+ $ this ->applyTimeFilter ($ filter , $ queryBuilder );
98+
99+ if ($ filter ->getIsConfirmed () !== null ) {
100+ $ queryBuilder
101+ ->andWhere ('subscriber.confirmed = :isConfirmed ' )
102+ ->setParameter ('isConfirmed ' , $ filter ->getIsConfirmed ());
100103 }
101- if ($ filter ->getSubscribedDateTo () !== null ) {
102- $ queryBuilder ->andWhere ('subscription.createdAt < :subscribedAtTo ' )
103- ->setParameter ('subscribedAtTo ' , $ filter ->getSubscribedDateTo ());
104+
105+ if ($ filter ->getIsBlacklisted () !== null ) {
106+ $ queryBuilder
107+ ->andWhere ('subscriber.blacklisted = :isBlacklisted ' )
108+ ->setParameter ('isBlacklisted ' , $ filter ->getIsBlacklisted ());
104109 }
105- }
106110
107- $ this ->applyTimeFilter ($ filter , $ queryBuilder );
111+ if ($ filter ->getFindColumn () && $ filter ->getFindValue ()) {
112+ $ queryBuilder
113+ ->andWhere (sprintf ('subscriber.%s LIKE :search ' , $ filter ->getFindColumn ()))
114+ ->setParameter ('search ' , '% ' . $ filter ->getFindValue () . '% ' );
115+ }
116+ };
108117
109- if ($ filter ->getIsConfirmed () !== null ) {
110- $ queryBuilder ->andWhere ('subscriber.confirmed = :isConfirmed ' )
111- ->setParameter ('isConfirmed ' , $ filter ->getIsConfirmed ());
112- }
113- if ($ filter ->getIsBlacklisted () !== null ) {
114- $ queryBuilder ->andWhere ('subscriber.blacklisted = :isBlacklisted ' )
115- ->setParameter ('isBlacklisted ' , $ filter ->getIsBlacklisted ());
116- }
117- if ($ filter ->getFindColumn () && $ filter ->getFindValue ()) {
118- $ queryBuilder ->andWhere ('subscriber. ' . $ filter ->getFindColumn () . ' LIKE :search ' )
119- ->setParameter ('search ' , '% ' . $ filter ->getFindValue () . '% ' );
120- }
118+ $ countQb = $ this ->createQueryBuilder ('subscriber ' )
119+ ->select ('COUNT(DISTINCT subscriber.id) ' );
120+
121+ $ applyFilters ($ countQb );
121122
122- $ countQb = clone $ queryBuilder ;
123123 $ total = (int ) $ countQb
124- ->select ('COUNT(DISTINCT subscriber.id) ' )
125124 ->getQuery ()
126125 ->getSingleScalarResult ();
127126
128- /** @var list<Subscriber> $items */
129- $ items = $ queryBuilder
127+ $ idsQb = $ this ->createQueryBuilder ('subscriber ' )
128+ ->select ('DISTINCT subscriber.id ' );
129+
130+ $ applyFilters ($ idsQb );
131+
132+ $ rawIds = $ idsQb
130133 ->andWhere ('subscriber.id > :lastId ' )
131134 ->setParameter ('lastId ' , $ lastId )
132135 ->orderBy ('subscriber.id ' , 'ASC ' )
133- ->setMaxResults ($ limit )
136+ ->setMaxResults ($ limit + 1 )
137+ ->getQuery ()
138+ ->getScalarResult ();
139+
140+ $ ids = array_map (static fn (array $ row ): int => (int ) $ row ['id ' ], $ rawIds );
141+
142+ $ hasMore = count ($ ids ) > $ limit ;
143+ if ($ hasMore ) {
144+ array_pop ($ ids );
145+ }
146+
147+ if ($ ids === []) {
148+ return new PaginatedResult (
149+ items: [],
150+ total: $ total ,
151+ limit: $ limit ,
152+ lastId: $ lastId ,
153+ );
154+ }
155+
156+ /** @var list<Subscriber> $items */
157+ $ items = $ this ->createQueryBuilder ('subscriber ' )
158+ ->select ('DISTINCT subscriber, subscription, list ' )
159+ ->leftJoin ('subscriber.subscriptions ' , 'subscription ' )
160+ ->leftJoin ('subscription.subscriberList ' , 'list ' )
161+ ->andWhere ('subscriber.id IN (:ids) ' )
162+ ->setParameter ('ids ' , $ ids )
163+ ->orderBy ('subscriber.id ' , 'ASC ' )
134164 ->getQuery ()
135165 ->getResult ();
136166
167+ usort ($ items , static fn (Subscriber $ first , Subscriber $ second ): int => $ first ->getId () <=> $ second ->getId ());
168+
137169 return new PaginatedResult (
138170 items: $ items ,
139171 total: $ total ,
@@ -142,6 +174,27 @@ public function getFilteredAfterId(FilterRequestInterface $filter): PaginatedRes
142174 );
143175 }
144176
177+ private function applyListIdFilter (SubscriberFilter $ filter , QueryBuilder $ queryBuilder ): void
178+ {
179+ if ($ filter ->getListId () !== null ) {
180+ $ queryBuilder
181+ ->andWhere ('list.id = :listId ' )
182+ ->setParameter ('listId ' , $ filter ->getListId ());
183+
184+ if ($ filter ->getSubscribedDateFrom () !== null ) {
185+ $ queryBuilder
186+ ->andWhere ('subscription.createdAt > :subscribedAtFrom ' )
187+ ->setParameter ('subscribedAtFrom ' , $ filter ->getSubscribedDateFrom ());
188+ }
189+
190+ if ($ filter ->getSubscribedDateTo () !== null ) {
191+ $ queryBuilder
192+ ->andWhere ('subscription.createdAt < :subscribedAtTo ' )
193+ ->setParameter ('subscribedAtTo ' , $ filter ->getSubscribedDateTo ());
194+ }
195+ }
196+ }
197+
145198 private function applyTimeFilter (SubscriberFilter $ filter , QueryBuilder $ queryBuilder ): void
146199 {
147200 if ($ filter ->getCreatedDateFrom () !== null ) {
0 commit comments