MediaWiki master
WatchlistLabelStore.php
Go to the documentation of this file.
1<?php
2
3namespace MediaWiki\Watchlist;
4
5use InvalidArgumentException;
10use Psr\Log\LoggerInterface;
12
19
20 public const TABLE_WATCHLIST_LABEL = 'watchlist_label';
21 public const TABLE_WATCHLIST_LABEL_MEMBER = 'watchlist_label_member';
22 private int $userLabelCount = 0;
23
24 public function __construct(
25 private IConnectionProvider $dbProvider,
26 private LoggerInterface $logger,
27 private Config $config
28 ) {
29 }
30
35 public function save( WatchlistLabel $label ): Status {
36 $dbw = $this->dbProvider->getPrimaryDatabase();
37 if ( $label->getId() ) {
38 $dbw->newUpdateQueryBuilder()
39 ->table( self::TABLE_WATCHLIST_LABEL )
40 ->set( [ 'wll_name' => $label->getName() ] )
41 ->where( [ 'wll_id' => $label->getId() ] )
42 ->caller( __METHOD__ )
43 ->execute();
44 if ( $dbw->affectedRows() !== 1 ) {
45 $this->logger->notice(
46 __METHOD__ . " Watchlist label not saved. ID: {0}; Name: {1}",
47 [ $label->getId(), $label->getName() ]
48 );
49 return Status::newFatal( 'unknown-error' );
50 }
51 } else {
52 $userId = $label->getUser()->getId();
53 if ( !$userId ) {
54 throw new InvalidArgumentException( 'user ID must not be zero' );
55 }
56 $this->userLabelCount = $this->countAllForUser( $label->getUser() );
57 // Check the user has not exceeded their label limit.
58 if (
59 $this->config->get( MainConfigNames::WatchlistLabelsMaxPerUser ) <= $this->userLabelCount
60 ) {
61 return Status::newFatal(
62 'watchlistlabels-limit-reached',
64 );
65 }
66 $dbw->newInsertQueryBuilder()
67 ->insertInto( self::TABLE_WATCHLIST_LABEL )
68 ->row( [ 'wll_user' => $userId, 'wll_name' => $label->getName() ] )
69 ->ignore()
70 ->caller( __METHOD__ )
71 ->execute();
72 if ( $dbw->affectedRows() > 0 ) {
73 $label->setId( $dbw->insertId() );
74 }
75 }
76 return Status::newGood();
77 }
78
87 public function delete( UserIdentity $user, array $ids ): bool {
88 if ( $ids === [] ) {
89 return true;
90 }
91 $dbw = $this->dbProvider->getPrimaryDatabase();
92 $dbw->startAtomic( __METHOD__ );
93 // Confirm that the user owns all the supplied labels.
94 sort( $ids );
95 $confirmedIdValues = $dbw->newSelectQueryBuilder()
96 ->from( self::TABLE_WATCHLIST_LABEL )
97 ->field( 'wll_id' )
98 ->where( [ 'wll_id' => $ids, 'wll_user' => $user->getId() ] )
99 ->orderBy( 'wll_id' )
100 ->caller( __METHOD__ )
101 ->fetchFieldValues();
102 $confirmedIds = array_map( 'intval', $confirmedIdValues );
103 if ( $confirmedIds !== $ids ) {
104 $dbw->cancelAtomic( __METHOD__ );
105 return false;
106 }
107 $dbw->newDeleteQueryBuilder()
108 ->deleteFrom( self::TABLE_WATCHLIST_LABEL_MEMBER )
109 ->where( [ 'wlm_label' => $confirmedIds ] )
110 ->caller( __METHOD__ )
111 ->execute();
112 $dbw->newDeleteQueryBuilder()
113 ->deleteFrom( self::TABLE_WATCHLIST_LABEL )
114 ->where( [ 'wll_id' => $confirmedIds ] )
115 ->caller( __METHOD__ )
116 ->execute();
117 $dbw->endAtomic( __METHOD__ );
118 return true;
119 }
120
129 public function loadById( UserIdentity $user, int $id ): ?WatchlistLabel {
130 $select = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder();
131 $result = $select->table( self::TABLE_WATCHLIST_LABEL )
132 ->fields( [ 'wll_id', 'wll_name' ] )
133 // It's not necessary to query for the user, but it adds an extra check.
134 ->where( [ 'wll_id' => $id, 'wll_user' => $user->getId() ] )
135 ->caller( __METHOD__ )
136 ->fetchRow();
137 return $result
138 ? new WatchlistLabel( $user, $result->wll_name, $result->wll_id )
139 : null;
140 }
141
150 public function loadByName( UserIdentity $user, string $name ): ?WatchlistLabel {
151 $select = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder();
152 $label = new WatchlistLabel( $user, $name );
153 $result = $select->table( self::TABLE_WATCHLIST_LABEL )
154 ->fields( [ 'wll_id', 'wll_name' ] )
155 ->where( [ 'wll_user' => $label->getUser()->getId(), 'wll_name' => $label->getName() ] )
156 ->caller( __METHOD__ )
157 ->fetchRow();
158 if ( $result ) {
159 $label->setId( $result->wll_id );
160 return $label;
161 }
162 return null;
163 }
164
172 public function loadAllForUser( UserIdentity $user ): array {
173 $dbr = $this->dbProvider->getReplicaDatabase();
174 $orderBy = 'wll_name';
175 if ( $dbr->getType() === 'mysql' ) {
176 $orderBy = 'CONVERT(wll_name USING utf8mb4) COLLATE utf8mb4_general_ci';
177 } elseif ( $dbr->getType() === 'sqlite' ) {
178 $orderBy = 'wll_name COLLATE NOCASE';
179 }
180 $select = $dbr->newSelectQueryBuilder();
181 $results = $select->table( self::TABLE_WATCHLIST_LABEL )
182 ->fields( [ 'wll_id', 'wll_name' ] )
183 ->where( [ 'wll_user' => $user->getId() ] )
184 ->orderBy( $orderBy, 'ASC' )
185 ->caller( __METHOD__ )
186 ->fetchResultSet();
187 $labels = [];
188 foreach ( $results as $result ) {
189 $labels[ (int)$result->wll_id ] = new WatchlistLabel( $user, $result->wll_name, $result->wll_id );
190 }
191 return $labels;
192 }
193
201 public function countItems( array $labelIds ): array {
202 if ( count( $labelIds ) === 0 ) {
203 return [];
204 }
205 $select = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder();
206 $results = $select->table( self::TABLE_WATCHLIST_LABEL_MEMBER )
207 ->fields( [ 'wlm_label', 'item_count' => 'COUNT(wlm_label)' ] )
208 ->where( [ 'wlm_label' => $labelIds ] )
209 ->groupBy( [ 'wlm_label' ] )
210 ->caller( __METHOD__ )
211 ->fetchResultSet();
212 $counts = array_combine( $labelIds, array_fill( 0, count( $labelIds ), 0 ) );
213 foreach ( $results as $result ) {
214 $counts[ $result->wlm_label ] = (int)$result->item_count;
215 }
216 return $counts;
217 }
218
226 public function countAllForUser( UserIdentity $user ): int {
227 $select = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder();
228 return (int)$select->table( self::TABLE_WATCHLIST_LABEL )
229 ->field( 'COUNT(*)' )
230 ->where( [ 'wll_user' => $user->getId() ] )
231 ->caller( __METHOD__ )
232 ->fetchField();
233 }
234}
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:69
A class containing constants representing the names of configuration variables.
const WatchlistLabelsMaxPerUser
Name constant for the WatchlistLabelsMaxPerUser setting, for use with Config::get()
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
Service class for storage of watchlist labels.
loadAllForUser(UserIdentity $user)
Get all of a user's watchlist labels.
save(WatchlistLabel $label)
Save a watchlist label to the database.
countItems(array $labelIds)
Get counts of all items with the given labels.
loadById(UserIdentity $user, int $id)
Load a single watchlist label by ID.
loadByName(UserIdentity $user, string $name)
Load a single watchlist label by (normalized) name.
__construct(private IConnectionProvider $dbProvider, private LoggerInterface $logger, private Config $config)
countAllForUser(UserIdentity $user)
Get the current total count of a user's watchlist labels.
Interface for configuration instances.
Definition Config.php:18
Interface for objects representing user identity.
getId( $wikiId=self::LOCAL)
Provide primary and replica IDatabase connections.