EBernhardson has uploaded a new change for review.
https://gerrit.wikimedia.org/r/115530
Change subject: Split reusable parts out of ContributionsQuery
......................................................................
Split reusable parts out of ContributionsQuery
Change-Id: I8dac314453d1bdffe076007a57cb8b1aa7242431
---
M Flow.php
M container.php
A includes/Formatter/AbstractQuery.php
M includes/Formatter/ContributionsQuery.php
4 files changed, 270 insertions(+), 247 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Flow
refs/changes/30/115530/1
diff --git a/Flow.php b/Flow.php
index d9a35ac..d167d42 100755
--- a/Flow.php
+++ b/Flow.php
@@ -136,6 +136,7 @@
$wgAutoloadClasses['Flow\Log\Formatter'] = $dir . 'includes/Log/Formatter.php';
$wgAutoloadClasses['Flow\Log\PostModerationLogger'] = $dir .
'includes/Log/PostModerationLogger.php';
$wgAutoloadClasses['Flow\Formatter\AbstractFormatter'] = $dir .
'includes/Formatter/AbstractFormatter.php';
+$wgAutoloadClasses['Flow\Formatter\AbstractQuery'] = $dir .
'includes/Formatter/AbstractQuery.php';
$wgAutoloadClasses['Flow\Formatter\CheckUser'] = $dir .
'includes/Formatter/CheckUser.php';
$wgAutoloadClasses['Flow\Formatter\ContributionsQuery'] = $dir .
'includes/Formatter/ContributionsQuery.php';
$wgAutoloadClasses['Flow\Formatter\Contributions'] = $dir .
'includes/Formatter/Contributions.php';
diff --git a/container.php b/container.php
index f9632cb..daa00ab 100644
--- a/container.php
+++ b/container.php
@@ -472,8 +472,8 @@
$c['contributions.query'] = $c->share( function( $c ) {
return new Flow\Formatter\ContributionsQuery(
$c['storage'],
- $c['memcache'],
- $c['repository.tree']
+ $c['repository.tree'],
+ $c['memcache']
);
} );
$c['contributions.formatter'] = $c->share( function( $c ) {
diff --git a/includes/Formatter/AbstractQuery.php
b/includes/Formatter/AbstractQuery.php
new file mode 100644
index 0000000..94ac671
--- /dev/null
+++ b/includes/Formatter/AbstractQuery.php
@@ -0,0 +1,253 @@
+<?php
+
+namespace Flow\Formatter;
+
+use Flow\Model\AbstractRevision;
+use Flow\Model\PostRevision;
+use Flow\Model\Header;
+use Flow\Model\Workflow;
+use Flow\Data\ManagerGroup;
+use Flow\Model\UUID;
+use Flow\Repository\TreeRepository;
+
+/**
+ * Base class that collects the data necessary to utilize AbstractFormatter
+ * based on a list of revisions. In some cases formatters will not utilize
+ * this query and will instead receive data from a table such as the core
+ * recentchanges.
+ */
+abstract class AbstractQuery {
+ /**
+ * @var ManagerGroup
+ */
+ protected $storage;
+
+ /**
+ * @var TreeRepository
+ */
+ protected $treeRepo;
+
+ /**
+ * @var UUID[] Associative array of post ID to root post's UUID object.
+ */
+ protected $rootPostIdCache = array();
+
+ /**
+ * @var PostRevision[] Associative array of post ID to PostRevision
object.
+ */
+ protected $postCache = array();
+
+ /**
+ * @var Workflow[] Associative array of workflow ID to Workflow object.
+ */
+ protected $workflowCache = array();
+
+ /**
+ * @param ManagerGroup $storage
+ * @param TreeRepository $treeRepo
+ */
+ public function __construct( ManagerGroup $storage, TreeRepository
$treeRepo ) {
+ $this->storage = $storage;
+ $this->treeRepo = $treeRepo;
+ }
+
+ protected function loadMetadataBatch( $results ) {
+ // Batch load data related to a list of revisions
+ $postIds = array();
+ $workflowIds = array();
+ $previousRevisionIds = array();
+ foreach( $results as $result ) {
+ if ( $result instanceof PostRevision ) {
+ // If top-level, then just get the workflow.
+ // Otherwise we need to find the root post.
+ if ( $result->isTopicTitle() ) {
+ $workflowIds[] = $result->getPostId();
+ } else {
+ $postIds[] = $result->getPostId();
+ }
+ } elseif ( $result instanceof Header ) {
+ $workflowIds[] = $result->getWorkflowId();
+ }
+
+ $previousRevisionIds[get_class( $result )][] =
$result->getPrevRevisionId();
+ }
+
+ // map from post Id to the related root post id
+ $rootPostIds = array_filter( $this->treeRepo->findRoots(
$postIds ) );
+
+ $rootPostRequests = array();
+ foreach( $rootPostIds as $postId ) {
+ $rootPostRequests[] = array( 'tree_rev_descendant_id'
=> $postId );
+ }
+
+ $rootPostResult = $this->storage->findMulti(
+ 'PostRevision',
+ $rootPostRequests,
+ array(
+ 'SORT' => 'rev_id',
+ 'ORDER' => 'DESC',
+ 'LIMIT' => 1,
+ )
+ );
+
+ $rootPosts = array();
+ if ( count( $rootPostResult ) > 0 ) {
+ foreach ( $rootPostResult as $found ) {
+ $root = reset( $found );
+
$rootPosts[$root->getPostId()->getAlphadecimal()] = $root;
+ }
+ }
+
+ // Workflow IDs are the same as root post IDs
+ // So any post IDs that *are* root posts + found root post IDs
+ header workflow IDs
+ // should cover the lot.
+ $workflows = $this->storage->getMulti( 'Workflow', array_merge(
$rootPostIds, $workflowIds ) );
+
+ // preload all previous revisions
+ $previousRevisions = array();
+ foreach ( $previousRevisionIds as $revisionType => $ids ) {
+ // get rid of null-values (for original revisions,
without previous revision)
+ $ids = array_filter( $ids );
+ foreach ( $this->storage->getMulti( $revisionType, $ids
) as $rev ) {
+
$previousRevisions[$rev->getRevisionId()->getAlphadecimal()] = $rev;
+ }
+ }
+
+ $this->postCache = array_merge( $this->postCache, $rootPosts,
$previousRevisions, $results );
+ $this->rootPostIdCache = array_merge( $this->rootPostIdCache,
$rootPostIds );
+ $this->workflowCache = array_merge( $this->workflowCache,
$workflows );
+ }
+
+ /*
+ * @param AbstractRevision $revision
+ * @param string $blockType Block name (e.g. "topic", "header")
+ * @param string $indexField The field used for pagination
+ * @return \stdClass
+ */
+ protected function buildResult( AbstractRevision $revision, $blockType,
$indexField ) {
+ $uuid = $revision->getRevisionId();
+ $timestamp = $uuid->getTimestamp();
+ $fakeRow = array();
+
+ $workflow = $this->getWorkflow( $revision );
+ if ( !$workflow ) {
+ wfWarn( __METHOD__ . ": could not locate workflow for
revision " . $revision->getRevisionId()->getAlphadecimal() );
+ return false;
+ }
+
+ // other contributions entries
+ $fakeRow[$indexField] = $timestamp; // used for navbar
+ $fakeRow['page_namespace'] =
$workflow->getArticleTitle()->getNamespace();
+ $fakeRow['page_title'] =
$workflow->getArticleTitle()->getDBkey();
+ $fakeRow['revision'] = $revision;
+ $fakeRow['previous_revision'] = $this->getPreviousRevision(
$revision );
+ $fakeRow['workflow'] = $workflow;
+ $fakeRow['blocktype'] = $blockType;
+
+ if ( $revision instanceof PostRevision ) {
+ $fakeRow['root_post'] = $this->getRootPost( $revision );
+ if ( $fakeRow['root_post'] === null ) {
+ wfWarn( __METHOD__ . ': no root post loaded for
' . $revision->getRevisionId()->getAlphadecimal() );
+ } else {
+ $revision->setRootPost( $fakeRow['root_post'] );
+ }
+ }
+
+ return (object) $fakeRow;
+ }
+
+ /**
+ * @param AbstractRevision $revision
+ * @return Workflow
+ * @throws \MWException
+ */
+ protected function getWorkflow( AbstractRevision $revision ) {
+ if ( $revision instanceof PostRevision ) {
+ $rootPostId = $this->getRootPostId( $revision );
+
+ return $this->getWorkflowById( $rootPostId );
+ } elseif ( $revision instanceof Header ) {
+ return $this->getWorkflowById(
$revision->getWorkflowId() );
+ } else {
+ throw new \MWException( 'Unsupported revision type ' .
get_class( $revision ) );
+ }
+ }
+
+ /**
+ * Retrieves the previous revision for a given AbstractRevision
+ * @param AbstractRevision $revision The revision to retrieve the
previous revision for.
+ * @return AbstractRevision|null AbstractRevision of the previous
revision or null if no previous revision.
+ */
+ protected function getPreviousRevision( AbstractRevision $revision ) {
+ $previousRevisionId = $revision->getPrevRevisionId();
+
+ // original post; no previous revision
+ if ( $previousRevisionId === null ) {
+ return null;
+ }
+
+ if ( !isset(
$this->postCache[$previousRevisionId->getAlphadecimal()] ) ) {
+
$this->postCache[$previousRevisionId->getAlphadecimal()] =
+ $this->storage->get( 'PostRevision',
$previousRevisionId );
+ }
+
+ return $this->postCache[$previousRevisionId->getAlphadecimal()];
+ }
+
+ /**
+ * Retrieves the root post for a given PostRevision
+ * @param PostRevision $revision The revision to retrieve the root
post for.
+ * @return PostRevision PostRevision of the root post.
+ * @throws \MWException
+ */
+ protected function getRootPost( PostRevision $revision ) {
+ if ( $revision->isTopicTitle() ) {
+ return $revision;
+ }
+ $rootPostId = $this->getRootPostId( $revision );
+
+ if ( !isset( $this->postCache[$rootPostId->getAlphadecimal()] )
) {
+ throw new \MwException( 'Did not load root post ' .
$rootPostId->getAlphadecimal() );
+ }
+
+ $rootPost = $this->postCache[$rootPostId->getAlphadecimal()];
+ if ( !$rootPost ) {
+ throw new \MWException( 'Did not locate root post ' .
$rootPostId->getAlphadecimal() );
+ }
+ if ( !$rootPost->isTopicTitle() ) {
+ throw new \MWException( "Not a topic title: " .
$rootPost->getRevisionId() );
+ }
+
+ return $rootPost;
+ }
+
+ /**
+ * Gets the root post ID for a given PostRevision
+ * @param PostRevision $revision The revision to get the root post ID
for.
+ * @return UUID The UUID for the root post.
+ * @throws \MWException
+ */
+ protected function getRootPostId( PostRevision $revision ) {
+ $postId = $revision->getPostId();
+ if ( $revision->isTopicTitle() ) {
+ return $postId;
+ } elseif ( isset(
$this->rootPostIdCache[$postId->getAlphadecimal()] ) ) {
+ return
$this->rootPostIdCache[$postId->getAlphadecimal()];
+ } else {
+ throw new \MWException( "Unable to find root post ID
for post $postId" );
+ }
+ }
+
+ /**
+ * Gets a Workflow object given its ID
+ * @param UUID $workflowId The Workflow ID to retrieve.
+ * @return Workflow The Workflow.
+ */
+ protected function getWorkflowById( UUID $workflowId ) {
+ if ( isset(
$this->workflowCache[$workflowId->getAlphadecimal()] ) ) {
+ return
$this->workflowCache[$workflowId->getAlphadecimal()];
+ } else {
+ return
$this->workflowCache[$workflowId->getAlphadecimal()] = $this->storage->get(
'Workflow', $workflowId );
+ }
+ }
+}
diff --git a/includes/Formatter/ContributionsQuery.php
b/includes/Formatter/ContributionsQuery.php
index 43f3209..fe7a3c8 100644
--- a/includes/Formatter/ContributionsQuery.php
+++ b/includes/Formatter/ContributionsQuery.php
@@ -2,60 +2,26 @@
namespace Flow\Formatter;
-use ContribsPager;
-use Flow\Model\AbstractRevision;
-use Flow\Model\PostRevision;
-use Flow\Model\Header;
-use Flow\Model\Workflow;
-use Flow\Data\RawSql;
+use Flow\Container;
use Flow\Data\ManagerGroup;
+use Flow\Data\RawSql;
+use Flow\Exception\FlowException;
use Flow\Model\UUID;
use Flow\Repository\TreeRepository;
-use Flow\Exception\FlowException;
-use User;
use BagOStuff;
-use Flow\Container;
+use ContribsPager;
+use User;
-class ContributionsQuery {
- /**
- * @var ManagerGroup
- */
- protected $storage;
+class ContributionsQuery extends AbstractQuery {
/**
* @var BagOStuff
*/
protected $cache;
- /**
- * @var TreeRepository
- */
- protected $treeRepo;
-
- /**
- * @var UUID[] Associative array of post ID to root post's UUID object.
- */
- protected $rootPostIdCache = array();
-
- /**
- * @var PostRevision[] Associative array of post ID to PostRevision
object.
- */
- protected $postCache = array();
-
- /**
- * @var Workflow[] Associative array of workflow ID to Workflow object.
- */
- protected $workflowCache = array();
-
- /**
- * @param ManagerGroup $storage
- * @param BagOStuff $cache
- * @param TreeRepository $treeRepo
- */
- public function __construct( ManagerGroup $storage, BagOStuff $cache,
TreeRepository $treeRepo ) {
- $this->storage = $storage;
+ public function __construct( ManagerGroup $storage, TreeRepository
$treeRepository, BagOStuff $cache ) {
+ parent::__construct( $storage, $treeRepository );
$this->cache = $cache;
- $this->treeRepo = $treeRepo;
}
/**
@@ -77,7 +43,7 @@
if ( $excludeUserIds ) {
// better safe than sorry - make sure
everything's an int
$excludeUserIds = array_map( 'intval',
$excludeUserIds );
- $conditions[] = new RawSql( 'rev_user_id NOT IN
(' . implode( ',', $excludeUserIds ) .')' );
+ $conditions[] = new RawSql( 'rev_user_id NOT IN
( ' . implode( ',', $excludeUserIds ) . ' )' );
}
} else {
$uid = User::idFromName( $target );
@@ -106,11 +72,14 @@
$results = array();
foreach ( $types as $revisionClass => $blockType ) {
- $revisions = $this->findRevisions( $pager, $conditions,
$limit, $revisionClass );
+ $revisions = $this->storage->find( $revisionClass,
$conditions, array(
+ 'LIMIT' => $limit,
+ ) );
$this->loadMetadataBatch( $revisions );
foreach ( $revisions as $revision ) {
try {
- $result = $this->buildResult( $pager,
$revision, $blockType );
+ $result = $this->buildResult(
$revision, $blockType, $pager->getIndexField() );
+ $result->flow_contribution = 'flow';
} catch ( FlowException $e ) {
$result = false;
\MWExceptionHandler::logException( $e );
@@ -122,206 +91,6 @@
}
return $results;
- }
-
- protected function loadMetadataBatch( $results ) {
- // Batch load data related to a list of revisions
- $postIds = array();
- $workflowIds = array();
- $previousRevisionIds = array();
- foreach( $results as $result ) {
- if ( $result instanceof PostRevision ) {
- // If top-level, then just get the workflow.
- // Otherwise we need to find the root post.
- if ( $result->isTopicTitle() ) {
- $workflowIds[] = $result->getPostId();
- } else {
- $postIds[] = $result->getPostId();
- }
- } elseif ( $result instanceof Header ) {
- $workflowIds[] = $result->getWorkflowId();
- }
-
- $previousRevisionIds[get_class( $result )][] =
$result->getPrevRevisionId();
- }
-
- $rootPostIds = array_filter( $this->treeRepo->findRoots(
$postIds ) );
- $rootPostRequests = array();
- foreach( $rootPostIds as $postId ) {
- $rootPostRequests[] = array( 'tree_rev_descendant_id'
=> $postId );
- }
-
- $rootPostResult = $this->storage->findMulti(
- 'PostRevision',
- $rootPostRequests,
- array(
- 'SORT' => 'rev_id',
- 'ORDER' => 'DESC',
- 'LIMIT' => 1,
- )
- );
-
- if ( count( $rootPostResult ) > 0 ) {
- $rootPosts = call_user_func_array( 'array_merge',
$rootPostResult ); // Muahaha
- } else {
- $rootPosts = array();
- }
-
- // Workflow IDs are the same as root post IDs
- // So any post IDs that *are* root posts + found root post IDs
+ header workflow IDs
- // should cover the lot.
- $workflows = $this->storage->getMulti( 'Workflow', array_merge(
$rootPostIds, $workflowIds ) );
-
- // preload all previous revisions
- $previousRevisions = array();
- foreach ( $previousRevisionIds as $revisionType => $ids ) {
- // get rid of null-values (for original revisions,
without previous revision)
- $ids = array_filter( $ids );
- $previousRevisions = $this->storage->getMulti(
$revisionType, $ids );
- }
-
- $this->postCache = array_merge( $this->postCache, $rootPosts,
$previousRevisions, $results );
- $this->rootPostIdCache = array_merge( $this->rootPostIdCache,
$rootPostIds );
- $this->workflowCache = array_merge( $this->workflowCache,
$workflows );
- }
-
- /**
- * @param ContribsPager $pager
- * @param array $conditions
- * @param int $limit
- * @param string $revisionClass Storage type (e.g. "PostRevision",
"Header")
- * @return mixed
- */
- protected function findRevisions( ContribsPager $pager, $conditions,
$limit, $revisionClass ) {
- return $this->storage->find( $revisionClass, $conditions, array(
- 'LIMIT' => $limit,
- ) );
- }
-
- /**
- * @param ContribsPager $pager
- * @param AbstractRevision $revision
- * @param string $blockType Block name (e.g. "topic", "header")
- * @return \stdClass
- */
- protected function buildResult( ContribsPager $pager, AbstractRevision
$revision, $blockType ) {
- $uuid = $revision->getRevisionId();
- $timestamp = $uuid->getTimestamp();
- $fakeRow = array();
-
- $workflow = $this->getWorkflow( $revision );
- if ( !$workflow ) {
- wfWarn( __METHOD__ . ": could not locate workflow for
revision " . $revision->getRevisionId()->getAlphadecimal() );
- return false;
- }
-
- // other contributions entries
- $fakeRow[$pager->getIndexField()] = $timestamp; // used for
navbar
- $fakeRow['page_namespace'] =
$workflow->getArticleTitle()->getNamespace();
- $fakeRow['page_title'] =
$workflow->getArticleTitle()->getDBkey();
- $fakeRow['revision'] = $revision;
- $fakeRow['previous_revision'] = $this->getPreviousRevision(
$revision );
- $fakeRow['workflow'] = $workflow;
- $fakeRow['blocktype'] = $blockType;
-
- if ( $blockType == 'topic' && $revision instanceof PostRevision
) {
- $fakeRow['root_post'] = $this->getRootPost( $revision );
- }
-
- // just to make sure entries will never be confused with
anything else
- $fakeRow['flow_contribution'] = 'flow';
-
- return (object) $fakeRow;
- }
-
- /**
- * @param AbstractRevision $revision
- * @return Workflow
- * @throws \MWException
- */
- protected function getWorkflow( AbstractRevision $revision ) {
- if ( $revision instanceof PostRevision ) {
- $rootPostId = $this->getRootPostId( $revision );
-
- return $this->getWorkflowById( $rootPostId );
- } elseif ( $revision instanceof Header ) {
- return $this->getWorkflowById(
$revision->getWorkflowId() );
- } else {
- throw new \MWException( 'Unsupported revision type ' .
get_class( $revision ) );
- }
- }
-
- /**
- * Retrieves the previous revision for a given AbstractRevision
- * @param AbstractRevision $revision The revision to retrieve the
previous revision for.
- * @return AbstractRevision|null AbstractRevision of the previous
revision or null if no previous revision.
- */
- protected function getPreviousRevision( AbstractRevision $revision ) {
- $previousRevisionId = $revision->getPrevRevisionId();
-
- // original post; no previous revision
- if ( $previousRevisionId === null ) {
- return null;
- }
-
- if ( !isset(
$this->postCache[$previousRevisionId->getAlphadecimal()] ) ) {
-
$this->postCache[$previousRevisionId->getAlphadecimal()] =
- $this->storage->get( 'PostRevision',
$previousRevisionId );
- }
-
- return $this->postCache[$previousRevisionId->getAlphadecimal()];
- }
-
- /**
- * Retrieves the root post for a given PostRevision
- * @param PostRevision $revision The revision to retrieve the root
post for.
- * @return PostRevision PostRevision of the root post.
- * @throws \MWException
- */
- protected function getRootPost( PostRevision $revision ) {
- $rootPostId = $this->getRootPostId( $revision );
-
- if ( !isset( $this->postCache[$rootPostId->getAlphadecimal()] )
) {
- $this->postCache[$rootPostId->getAlphadecimal()] =
- $this->storage->get( 'PostRevision',
$rootPostId );
- }
-
- $rootPost = $this->postCache[$rootPostId->getAlphadecimal()];
- if ( $rootPost && !$rootPost->isTopicTitle() ) {
- throw new \MWException( "Not a topic title: " .
$rootPost->getRevisionId() );
- }
-
- return $rootPost;
- }
-
- /**
- * Gets the root post ID for a given PostRevision
- * @param PostRevision $revision The revision to get the root post ID
for.
- * @return UUID The UUID for the root post.
- * @throws \MWException
- */
- protected function getRootPostId( PostRevision $revision ) {
- $postId = $revision->getPostId();
- if ( $revision->isTopicTitle() ) {
- return $postId;
- } elseif ( isset(
$this->rootPostIdCache[$postId->getAlphadecimal()] ) ) {
- return
$this->rootPostIdCache[$postId->getAlphadecimal()];
- } else {
- throw new \MWException( "Unable to find root post ID
for post $postId" );
- }
- }
-
- /**
- * Gets a Workflow object given its ID
- * @param UUID $workflowId The Workflow ID to retrieve.
- * @return Workflow The Workflow.
- */
- protected function getWorkflowById( UUID $workflowId ) {
- if ( isset(
$this->workflowCache[$workflowId->getAlphadecimal()] ) ) {
- return
$this->workflowCache[$workflowId->getAlphadecimal()];
- } else {
- return
$this->workflowCache[$workflowId->getAlphadecimal()] = $this->storage->get(
'Workflow', $workflowId );
- }
}
/**
--
To view, visit https://gerrit.wikimedia.org/r/115530
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I8dac314453d1bdffe076007a57cb8b1aa7242431
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: EBernhardson <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits