Merge pull request #7253 from owncloud/preferences-caching

Add caching to OC\Preferences
This commit is contained in:
icewind1991 2014-02-18 18:17:58 +01:00
commit 3cc7228c7c
2 changed files with 86 additions and 89 deletions

View File

@ -43,8 +43,26 @@ use \OC\DB\Connection;
* This class provides an easy way for storing user preferences. * This class provides an easy way for storing user preferences.
*/ */
class Preferences { class Preferences {
/**
* @var \OC\DB\Connection
*/
protected $conn; protected $conn;
/**
* 3 dimensional array with the following structure:
* [ $userId =>
* [ $appId =>
* [ $key => $value ]
* ]
* ]
*
* @var array $cache
*/
protected $cache = array();
/**
* @param \OC\DB\Connection $conn
*/
public function __construct(Connection $conn) { public function __construct(Connection $conn) {
$this->conn = $conn; $this->conn = $conn;
} }
@ -58,16 +76,38 @@ class Preferences {
*/ */
public function getUsers() { public function getUsers() {
$query = 'SELECT DISTINCT `userid` FROM `*PREFIX*preferences`'; $query = 'SELECT DISTINCT `userid` FROM `*PREFIX*preferences`';
$result = $this->conn->executeQuery( $query ); $result = $this->conn->executeQuery($query);
$users = array(); $users = array();
while( $userid = $result->fetchColumn()) { while ($userid = $result->fetchColumn()) {
$users[] = $userid; $users[] = $userid;
} }
return $users; return $users;
} }
/**
* @param string $user
* @return array[]
*/
protected function getUserValues($user) {
if (isset($this->cache[$user])) {
return $this->cache[$user];
}
$data = array();
$query = 'SELECT `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?';
$result = $this->conn->executeQuery($query, array($user));
while ($row = $result->fetch()) {
$app = $row['appid'];
if (!isset($data[$app])) {
$data[$app] = array();
}
$data[$app][$row['configkey']] = $row['configvalue'];
}
$this->cache[$user] = $data;
return $data;
}
/** /**
* @brief Get all apps of an user * @brief Get all apps of an user
* @param string $user user * @param string $user user
@ -76,16 +116,9 @@ class Preferences {
* This function returns a list of all apps of the user that have at least * This function returns a list of all apps of the user that have at least
* one entry in the preferences table. * one entry in the preferences table.
*/ */
public function getApps( $user ) { public function getApps($user) {
$query = 'SELECT DISTINCT `appid` FROM `*PREFIX*preferences` WHERE `userid` = ?'; $data = $this->getUserValues($user);
$result = $this->conn->executeQuery( $query, array( $user ) ); return array_keys($data);
$apps = array();
while( $appid = $result->fetchColumn()) {
$apps[] = $appid;
}
return $apps;
} }
/** /**
@ -97,16 +130,13 @@ class Preferences {
* This function gets all keys of an app of an user. Please note that the * This function gets all keys of an app of an user. Please note that the
* values are not returned. * values are not returned.
*/ */
public function getKeys( $user, $app ) { public function getKeys($user, $app) {
$query = 'SELECT `configkey` FROM `*PREFIX*preferences` WHERE `userid` = ? AND `appid` = ?'; $data = $this->getUserValues($user);
$result = $this->conn->executeQuery( $query, array( $user, $app )); if (isset($data[$app])) {
return array_keys($data[$app]);
$keys = array(); } else {
while( $key = $result->fetchColumn()) { return array();
$keys[] = $key;
} }
return $keys;
} }
/** /**
@ -120,13 +150,10 @@ class Preferences {
* This function gets a value from the preferences table. If the key does * This function gets a value from the preferences table. If the key does
* not exist the default value will be returned * not exist the default value will be returned
*/ */
public function getValue( $user, $app, $key, $default = null ) { public function getValue($user, $app, $key, $default = null) {
// Try to fetch the value, return default if not exists. $data = $this->getUserValues($user);
$query = 'SELECT `configvalue` FROM `*PREFIX*preferences`' if (isset($data[$app]) and isset($data[$app][$key])) {
.' WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?'; return $data[$app][$key];
$row = $this->conn->fetchAssoc( $query, array( $user, $app, $key ));
if($row) {
return $row["configvalue"];
} else { } else {
return $default; return $default;
} }
@ -142,14 +169,14 @@ class Preferences {
* Adds a value to the preferences. If the key did not exist before, it * Adds a value to the preferences. If the key did not exist before, it
* will be added automagically. * will be added automagically.
*/ */
public function setValue( $user, $app, $key, $value ) { public function setValue($user, $app, $key, $value) {
// Check if the key does exist // Check if the key does exist
$query = 'SELECT COUNT(*) FROM `*PREFIX*preferences`' $query = 'SELECT COUNT(*) FROM `*PREFIX*preferences`'
.' WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?'; . ' WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?';
$count = $this->conn->fetchColumn( $query, array( $user, $app, $key )); $count = $this->conn->fetchColumn($query, array($user, $app, $key));
$exists = $count > 0; $exists = $count > 0;
if( !$exists ) { if (!$exists) {
$data = array( $data = array(
'userid' => $user, 'userid' => $user,
'appid' => $app, 'appid' => $app,
@ -168,6 +195,14 @@ class Preferences {
); );
$this->conn->update('*PREFIX*preferences', $data, $where); $this->conn->update('*PREFIX*preferences', $data, $where);
} }
// only add to the cache if we already loaded data for the user
if (isset($this->cache[$user])) {
if (!isset($this->cache[$user][$app])) {
$this->cache[$user][$app] = array();
}
$this->cache[$user][$app][$key] = $value;
}
} }
/** /**
@ -178,13 +213,17 @@ class Preferences {
* *
* Deletes a key. * Deletes a key.
*/ */
public function deleteKey( $user, $app, $key ) { public function deleteKey($user, $app, $key) {
$where = array( $where = array(
'userid' => $user, 'userid' => $user,
'appid' => $app, 'appid' => $app,
'configkey' => $key, 'configkey' => $key,
); );
$this->conn->delete('*PREFIX*preferences', $where); $this->conn->delete('*PREFIX*preferences', $where);
if (isset($this->cache[$user]) and isset($this->cache[$user][$app])) {
unset($this->cache[$user][$app][$key]);
}
} }
/** /**
@ -194,12 +233,16 @@ class Preferences {
* *
* Removes all keys in preferences belonging to the app and the user. * Removes all keys in preferences belonging to the app and the user.
*/ */
public function deleteApp( $user, $app ) { public function deleteApp($user, $app) {
$where = array( $where = array(
'userid' => $user, 'userid' => $user,
'appid' => $app, 'appid' => $app,
); );
$this->conn->delete('*PREFIX*preferences', $where); $this->conn->delete('*PREFIX*preferences', $where);
if (isset($this->cache[$user])) {
unset($this->cache[$user][$app]);
}
} }
/** /**
@ -208,11 +251,13 @@ class Preferences {
* *
* Removes all keys in preferences belonging to the user. * Removes all keys in preferences belonging to the user.
*/ */
public function deleteUser( $user ) { public function deleteUser($user) {
$where = array( $where = array(
'userid' => $user, 'userid' => $user,
); );
$this->conn->delete('*PREFIX*preferences', $where); $this->conn->delete('*PREFIX*preferences', $where);
unset($this->cache[$user]);
} }
/** /**
@ -221,12 +266,16 @@ class Preferences {
* *
* Removes all keys in preferences belonging to the app. * Removes all keys in preferences belonging to the app.
*/ */
public function deleteAppFromAllUsers( $app ) { public function deleteAppFromAllUsers($app) {
$where = array( $where = array(
'appid' => $app, 'appid' => $app,
); );
$this->conn->delete('*PREFIX*preferences', $where); $this->conn->delete('*PREFIX*preferences', $where);
foreach ($this->cache as &$userCache) {
unset($userCache[$app]);
}
} }
} }
require_once __DIR__.'/legacy/'.basename(__FILE__); require_once __DIR__ . '/legacy/' . basename(__FILE__);

View File

@ -144,58 +144,6 @@ class Test_Preferences_Object extends PHPUnit_Framework_TestCase {
$this->assertEquals(array('foo'), $apps); $this->assertEquals(array('foo'), $apps);
} }
public function testGetApps()
{
$statementMock = $this->getMock('\Doctrine\DBAL\Statement', array(), array(), '', false);
$statementMock->expects($this->exactly(2))
->method('fetchColumn')
->will($this->onConsecutiveCalls('foo', false));
$connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false);
$connectionMock->expects($this->once())
->method('executeQuery')
->with($this->equalTo('SELECT DISTINCT `appid` FROM `*PREFIX*preferences` WHERE `userid` = ?'),
$this->equalTo(array('bar')))
->will($this->returnValue($statementMock));
$preferences = new OC\Preferences($connectionMock);
$apps = $preferences->getApps('bar');
$this->assertEquals(array('foo'), $apps);
}
public function testGetKeys()
{
$statementMock = $this->getMock('\Doctrine\DBAL\Statement', array(), array(), '', false);
$statementMock->expects($this->exactly(2))
->method('fetchColumn')
->will($this->onConsecutiveCalls('foo', false));
$connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false);
$connectionMock->expects($this->once())
->method('executeQuery')
->with($this->equalTo('SELECT `configkey` FROM `*PREFIX*preferences` WHERE `userid` = ? AND `appid` = ?'),
$this->equalTo(array('bar', 'moo')))
->will($this->returnValue($statementMock));
$preferences = new OC\Preferences($connectionMock);
$keys = $preferences->getKeys('bar', 'moo');
$this->assertEquals(array('foo'), $keys);
}
public function testGetValue()
{
$connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false);
$connectionMock->expects($this->exactly(2))
->method('fetchAssoc')
->with($this->equalTo('SELECT `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?'),
$this->equalTo(array('grg', 'bar', 'red')))
->will($this->onConsecutiveCalls(array('configvalue'=>'foo'), null));
$preferences = new OC\Preferences($connectionMock);
$value = $preferences->getValue('grg', 'bar', 'red');
$this->assertEquals('foo', $value);
$value = $preferences->getValue('grg', 'bar', 'red', 'def');
$this->assertEquals('def', $value);
}
public function testSetValue() public function testSetValue()
{ {
$connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false);