wiki.techinc.nl/includes/user/Options/LocalUserOptionsStore.php
Tim Starling cdc5178150 user: Introduce UserOptionsStore
Refactoring:

* Break out the database access part of UserOptionsManager to a separate
  class hierarchy implementing interface UserOptionsStore. It's
  basically a key/key/string-value store, very simple. The complex
  parts of user options storage remain in UserOptionsManager.
* Bundle the UserOptionsManager caches into a per-user cache object. I
  was adding a couple more and it was getting tedious.

Start integrating GlobalPreferences with UserOptionsManager:

* Have an array of stores. There's always a local store, and extensions
  can add stores via an attribute.
* Add $global parameter to UserOptionsManager::setOption(), allowing
  this method to update or override global options.
* Rename loadOptionsFromDb to loadOptionsFromStore.
* Move the local override feature from GlobalPreferences to core.

Bug: T323076
Change-Id: Ib3623b723557c819bc0ffdf21a4ffcb070eb298b
2024-06-12 01:27:57 +00:00

96 lines
2.5 KiB
PHP

<?php
namespace MediaWiki\User\Options;
use DBAccessObjectUtils;
use MediaWiki\User\UserIdentity;
use Wikimedia\Rdbms\IConnectionProvider;
class LocalUserOptionsStore implements UserOptionsStore {
private IConnectionProvider $dbProvider;
/** @var array[] Cached options for each user, by user ID */
private $optionsFromDb;
public function __construct( IConnectionProvider $dbProvider ) {
$this->dbProvider = $dbProvider;
}
public function fetch(
UserIdentity $user,
int $recency
): array {
$dbr = DBAccessObjectUtils::getDBFromRecency( $this->dbProvider, $recency );
$res = $dbr->newSelectQueryBuilder()
->select( [ 'up_property', 'up_value' ] )
->from( 'user_properties' )
->where( [ 'up_user' => $user->getId() ] )
->recency( $recency )
->caller( __METHOD__ )->fetchResultSet();
$options = [];
foreach ( $res as $row ) {
$options[$row->up_property] = (string)$row->up_value;
}
$this->optionsFromDb[$user->getId()] = $options;
return $options;
}
public function store( UserIdentity $user, array $updates ) {
$oldOptions = $this->optionsFromDb[ $user->getId() ]
?? $this->fetch( $user, \IDBAccessObject::READ_LATEST );
$newOptions = $oldOptions;
$keysToDelete = [];
$rowsToInsert = [];
foreach ( $updates as $key => $value ) {
if ( !UserOptionsManager::isValueEqual(
$value, $oldOptions[$key] ?? null )
) {
// Update by deleting and reinserting
if ( array_key_exists( $key, $oldOptions ) ) {
$keysToDelete[] = $key;
unset( $newOptions[$key] );
}
if ( $value !== null ) {
$truncValue = mb_strcut( $value, 0,
UserOptionsManager::MAX_BYTES_OPTION_VALUE );
$rowsToInsert[] = [
'up_user' => $user->getId(),
'up_property' => $key,
'up_value' => $truncValue,
];
$newOptions[$key] = $truncValue;
}
}
}
if ( !count( $keysToDelete ) && !count( $rowsToInsert ) ) {
// Nothing to do
return false;
}
// Do the DELETE
$dbw = $this->dbProvider->getPrimaryDatabase();
if ( $keysToDelete ) {
$dbw->newDeleteQueryBuilder()
->deleteFrom( 'user_properties' )
->where( [ 'up_user' => $user->getId() ] )
->andWhere( [ 'up_property' => $keysToDelete ] )
->caller( __METHOD__ )->execute();
}
if ( $rowsToInsert ) {
// Insert the new preference rows
$dbw->newInsertQueryBuilder()
->insertInto( 'user_properties' )
->ignore()
->rows( $rowsToInsert )
->caller( __METHOD__ )->execute();
}
// Update cache
$this->optionsFromDb[$user->getId()] = $newOptions;
return true;
}
}