Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 100 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
EventPagerTrait | |
0.00% |
0 / 100 |
|
0.00% |
0 / 4 |
90 | |
0.00% |
0 / 1 |
getSubqueryInfo | |
0.00% |
0 / 51 |
|
0.00% |
0 / 1 |
2 | |||
getQueryInfo | |
0.00% |
0 / 27 |
|
0.00% |
0 / 1 |
6 | |||
preprocessResults | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
20 | |||
getEventPageFromRow | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | declare( strict_types=1 ); |
3 | |
4 | namespace MediaWiki\Extension\CampaignEvents\Pager; |
5 | |
6 | use MediaWiki\Cache\LinkBatchFactory; |
7 | use MediaWiki\Extension\CampaignEvents\MWEntity\CampaignsPageFactory; |
8 | use MediaWiki\Extension\CampaignEvents\MWEntity\ICampaignsPage; |
9 | use MediaWiki\WikiMap\WikiMap; |
10 | use stdClass; |
11 | use Wikimedia\Rdbms\IExpression; |
12 | use Wikimedia\Rdbms\IReadableDatabase; |
13 | use Wikimedia\Rdbms\IResultWrapper; |
14 | use Wikimedia\Rdbms\LikeValue; |
15 | use Wikimedia\Rdbms\Subquery; |
16 | |
17 | /** |
18 | * @property string $search; |
19 | * @property string $status; |
20 | * @property IReadableDatabase $mDb; |
21 | * @property LinkBatchFactory $linkBatchFactory; |
22 | * @property CampaignsPageFactory $campaignsPageFactory; |
23 | */ |
24 | trait EventPagerTrait { |
25 | /** @var array<int,ICampaignsPage> Cache of event page objects, keyed by event ID */ |
26 | private array $eventPageCache = []; |
27 | |
28 | /** |
29 | * @todo The joins and grouping below are not used by EventsListPager (which wouldn't even need a subquery), and |
30 | * they just slow the query down. We should either implement those features in the list pager, or move the |
31 | * complexity to EventsTablePager. |
32 | * @return array |
33 | */ |
34 | public function getSubqueryInfo(): array { |
35 | $options = [ |
36 | 'GROUP BY' => [ |
37 | 'cep_event_id', |
38 | 'event_id', |
39 | 'event_name', |
40 | 'event_page_namespace', |
41 | 'event_page_title', |
42 | 'event_page_prefixedtext', |
43 | 'event_page_wiki', |
44 | 'event_status', |
45 | 'event_start_utc', |
46 | 'event_end_utc', |
47 | 'event_meeting_type' |
48 | ] ]; |
49 | $join_conds = [ |
50 | 'ce_participants' => [ |
51 | 'LEFT JOIN', |
52 | [ |
53 | 'event_id=cep_event_id', |
54 | 'cep_unregistered_at' => null, |
55 | ] |
56 | ], |
57 | 'ce_organizers' => [ |
58 | 'JOIN', |
59 | [ |
60 | 'event_id=ceo_event_id', |
61 | 'ceo_deleted_at' => null, |
62 | ] |
63 | ] |
64 | ]; |
65 | return [ |
66 | 'tables' => [ 'campaign_events', 'ce_participants', 'ce_organizers' ], |
67 | 'fields' => [ |
68 | 'event_id', |
69 | 'event_name', |
70 | 'event_page_namespace', |
71 | 'event_page_title', |
72 | 'event_page_prefixedtext', |
73 | 'event_page_wiki', |
74 | 'event_status', |
75 | 'event_start_utc', |
76 | 'event_end_utc', |
77 | 'event_meeting_type', |
78 | 'num_participants' => 'COUNT(cep_id)' |
79 | ], |
80 | 'conds' => [ |
81 | 'event_deleted_at' => null, |
82 | ], |
83 | 'options' => $options, |
84 | 'join_conds' => $join_conds |
85 | ]; |
86 | } |
87 | |
88 | /** |
89 | * @inheritDoc |
90 | */ |
91 | public function getQueryInfo() { |
92 | // Use a subquery and a temporary table to work around IndexPager not using HAVING for aggregates (T308694) |
93 | // and to support postgres (which doesn't allow aliases in HAVING). |
94 | $subqueryInfo = $this->getSubqueryInfo(); |
95 | $subquery = $this->mDb->newSelectQueryBuilder() |
96 | ->queryInfo( $subqueryInfo ) |
97 | ->caller( __METHOD__ ); |
98 | $conds = []; |
99 | if ( $this->search !== '' ) { |
100 | // TODO Make this case-insensitive. Not easy right now because the name is a binary string and the DBAL does |
101 | // not provide a method for converting it to a non-binary value on which LOWER can be applied. |
102 | $conds[] = $this->mDb->expr( 'event_name', IExpression::LIKE, |
103 | new LikeValue( $this->mDb->anyString(), $this->search, $this->mDb->anyString() ) ); |
104 | } |
105 | |
106 | return [ |
107 | 'tables' => [ 'tmp' => new Subquery( $subquery->getSQL() ) ], |
108 | 'fields' => [ |
109 | 'event_id', |
110 | 'event_name', |
111 | 'event_page_namespace', |
112 | 'event_page_title', |
113 | 'event_page_prefixedtext', |
114 | 'event_page_wiki', |
115 | 'event_status', |
116 | 'event_start_utc', |
117 | 'event_end_utc', |
118 | 'event_meeting_type', |
119 | 'num_participants' |
120 | ], |
121 | 'conds' => $conds, |
122 | 'options' => [], |
123 | 'join_conds' => [] |
124 | ]; |
125 | } |
126 | |
127 | /** |
128 | * Add event pages to a LinkBatch to improve performance and not make one query per page. |
129 | * This code was stolen from AbuseFilter's pager et al. |
130 | * @param IResultWrapper $result |
131 | */ |
132 | protected function preprocessResults( $result ): void { |
133 | // Error suppressed, method is declared in inheritor |
134 | // @phan-suppress-next-line PhanUndeclaredMethod |
135 | if ( $this->getNumRows() === 0 ) { |
136 | return; |
137 | } |
138 | $linkBatchFactory = $this->linkBatchFactory; |
139 | $lb = $linkBatchFactory->newLinkBatch(); |
140 | $lb->setCaller( __METHOD__ ); |
141 | $curWikiID = WikiMap::getCurrentWikiId(); |
142 | foreach ( $result as $row ) { |
143 | // XXX LinkCache only supports local pages, and it's not used in foreign instances of PageStore. |
144 | if ( $row->event_page_wiki === $curWikiID ) { |
145 | $lb->add( (int)$row->event_page_namespace, $row->event_page_title ); |
146 | } |
147 | } |
148 | $lb->execute(); |
149 | $result->seek( 0 ); |
150 | } |
151 | |
152 | /** |
153 | * @param stdClass $eventRow |
154 | * @return ICampaignsPage |
155 | */ |
156 | private function getEventPageFromRow( stdClass $eventRow ): ICampaignsPage { |
157 | $eventID = $eventRow->event_id; |
158 | $eventPageCache = $this->eventPageCache; |
159 | $campaignsPageFactory = $this->campaignsPageFactory; |
160 | if ( !isset( $eventPageCache[$eventID] ) ) { |
161 | $eventPageCache[$eventID] = $campaignsPageFactory->newPageFromDB( |
162 | (int)$eventRow->event_page_namespace, |
163 | $eventRow->event_page_title, |
164 | $eventRow->event_page_prefixedtext, |
165 | $eventRow->event_page_wiki |
166 | ); |
167 | } |
168 | return $eventPageCache[$eventID]; |
169 | } |
170 | } |