Using a cache and grouped data fetching to avoid superfluous queries
authorAlexander Ebert <ebert@woltlab.com>
Tue, 1 Jul 2014 23:04:44 +0000 (01:04 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Tue, 1 Jul 2014 23:04:44 +0000 (01:04 +0200)
com.woltlab.wcf/userNotificationEvent.xml
wcfsetup/install/files/lib/data/comment/CommentAction.class.php
wcfsetup/install/files/lib/data/comment/LikeableComment.class.php
wcfsetup/install/files/lib/data/comment/response/LikeableCommentResponse.class.php
wcfsetup/install/files/lib/system/comment/CommentDataHandler.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/user/notification/UserNotificationHandler.class.php
wcfsetup/install/files/lib/system/user/notification/event/AbstractSharedUserNotificationEvent.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/user/notification/event/AbstractUserNotificationEvent.class.php
wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentLikeUserNotificationEvent.class.php
wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentResponseOwnerUserNotificationEvent.class.php
wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentResponseUserNotificationEvent.class.php

index 9e6b8db102176d2725aa4c50470c11518e70b308..cf0cd194bfbd2af84817070f4a3ec9b796f4124f 100644 (file)
                <event>
                        <name>like</name>
                        <objecttype>com.woltlab.wcf.user.profileComment.like.notification</objecttype>
-                       <classname><![CDATA[wcf\system\user\notification\event\UserProfileCommentUserNotificationEvent]]></classname>
+                       <classname><![CDATA[wcf\system\user\notification\event\UserProfileCommentUserLikeNotificationEvent]]></classname>
                        <preset>1</preset>
                </event>
                <event>
                        <name>like</name>
                        <objecttype>com.woltlab.wcf.user.profileComment.response.like.notification</objecttype>
-                       <classname><![CDATA[wcf\system\user\notification\event\UserProfileCommentResponseUserNotificationEvent]]></classname>
+                       <classname><![CDATA[wcf\system\user\notification\event\UserProfileCommentResponseLikeUserNotificationEvent]]></classname>
                        <preset>1</preset>
                </event>
        </import>
index 4e36131ffe2bbbb9802fc0e9c762e58fbe1fa85b..1c3b2dd048919753bd7085b718017dcb1c008079 100644 (file)
@@ -250,7 +250,9 @@ class CommentAction extends AbstractDatabaseObjectAction {
                        if ($userID != WCF::getUser()->userID) {
                                $notificationObject = new CommentUserNotificationObject($this->createdComment);
                                
-                               UserNotificationHandler::getInstance()->fireEvent('comment', $objectType->objectType.'.notification', $notificationObject, array($userID));
+                               UserNotificationHandler::getInstance()->fireEvent('comment', $objectType->objectType.'.notification', $notificationObject, array($userID), array(
+                                       'objectUserID' => $userID
+                               ));
                        }
                }
                
@@ -349,18 +351,28 @@ class CommentAction extends AbstractDatabaseObjectAction {
                
                // fire notification event
                if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.response.notification')) {
+                       $notificationObjectType = UserNotificationHandler::getInstance()->getObjectTypeProcessor($objectType->objectType.'.notification');
+                       $userID = $notificationObjectType->getOwnerID($this->comment->commentID);
+                       
                        $notificationObject = new CommentResponseUserNotificationObject($this->createdResponse);
                        if ($this->comment->userID != WCF::getUser()->userID) {
-                               UserNotificationHandler::getInstance()->fireEvent('commentResponse', $objectType->objectType.'.response.notification', $notificationObject, array($this->comment->userID));
+                               UserNotificationHandler::getInstance()->fireEvent('commentResponse', $objectType->objectType.'.response.notification', $notificationObject, array($this->comment->userID), array(
+                                       'commentID' => $this->comment->commentID,
+                                       'objectID' => $this->comment->objectID,
+                                       'objectUserID' => $userID,
+                                       'userID' => $this->comment->userID
+                               ));
                        }
                        
                        // notify the container owner
                        if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.notification')) {
-                               $notificationObjectType = UserNotificationHandler::getInstance()->getObjectTypeProcessor($objectType->objectType.'.notification');
-                               $userID = $notificationObjectType->getOwnerID($this->comment->commentID);
-                               
                                if ($userID != $this->comment->userID && $userID != WCF::getUser()->userID) {
-                                       UserNotificationHandler::getInstance()->fireEvent('commentResponseOwner', $objectType->objectType.'.response.notification', $notificationObject, array($userID));
+                                       UserNotificationHandler::getInstance()->fireEvent('commentResponseOwner', $objectType->objectType.'.response.notification', $notificationObject, array($userID), array(
+                                               'commentID' => $this->comment->commentID,
+                                               'objectID' => $this->comment->objectID,
+                                               'objectUserID' => $userID,
+                                               'userID' => $this->comment->userID
+                                       ));
                                }
                        }
                }
index 9ea3bef730a89eb5ad01c4dfc56864bd9c54f5a9..458d3a62bcb8473abcdbfa614253365894021a1c 100644 (file)
@@ -65,7 +65,10 @@ class LikeableComment extends AbstractLikeObject {
                        $notificationObjectType = UserNotificationHandler::getInstance()->getObjectTypeProcessor($objectType->objectType.'.like.notification');
                        if ($this->userID != WCF::getUser()->userID) {
                                $notificationObject = new CommentLikeUserNotificationObject($like);
-                               UserNotificationHandler::getInstance()->fireEvent('like', $objectType->objectType.'.like.notification', $notificationObject, array($this->userID), array('objectID' => $this->object->objectID));
+                               UserNotificationHandler::getInstance()->fireEvent('like', $objectType->objectType.'.like.notification', $notificationObject, array($this->userID), array(
+                                       'objectID' => $this->object->objectID,
+                                       'objectOwnerID' => $this->userID
+                               ));
                        }
                }
        }
index c31e866d6d2d0530bdf5d2aef3c03ff19046b604..bc03823b07e3b7adf2a0337ef7a1c6762c9f88e7 100644 (file)
@@ -70,7 +70,7 @@ class LikeableCommentResponse extends AbstractLikeObject {
                                UserNotificationHandler::getInstance()->fireEvent('like', $objectType->objectType.'.response.like.notification', $notificationObject, array($this->userID), array(
                                        'commentID' => $comment->commentID,
                                        'commentUserID' => $comment->userID,
-                                       'objectID' => $comment->objectID,
+                                       'objectID' => $comment->objectID
                                ));
                        }
                }
diff --git a/wcfsetup/install/files/lib/system/comment/CommentDataHandler.class.php b/wcfsetup/install/files/lib/system/comment/CommentDataHandler.class.php
new file mode 100644 (file)
index 0000000..f787c3b
--- /dev/null
@@ -0,0 +1,94 @@
+<?php
+namespace wcf\system\comment;
+use wcf\data\comment\CommentList;
+use wcf\data\user\UserProfile;
+use wcf\system\SingletonFactory;
+
+/**
+ * Handles common data resources for comment-related user notifications
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.comment
+ * @category   Community Framework
+ */
+class CommentDataHandler extends SingletonFactory {
+       /**
+        * list of comment ids
+        * @var array<integer>
+        */
+       protected $commentIDs = array();
+       
+       /**
+        * list of cached comment objects
+        * @var array<\wcf\data\comment\Comment>
+        */
+       protected $comments = array();
+       
+       /**
+        * list of user ids
+        * @var array<integer>
+        */
+       protected $userIDs = array();
+       
+       /**
+        * Caches a comment id.
+        * 
+        * @param       integer         $commentID
+        */
+       public function cacheCommentID($commentID) {
+               if (!in_array($commentID, $this->commentIDs)) {
+                       $this->commentIDs[] = $commentID;
+               }
+       }
+       
+       /**
+        * Caches a user id.
+        * 
+        * @param       integer         $userID
+        */
+       public function cacheUserID($userID) {
+               if (!in_array($userID, $this->userIDs)) {
+                       $this->userIDs[] = $userID;
+               }
+       }
+       
+       /**
+        * Returns a comment by id, fetches comments on first call.
+        * 
+        * @param       integer         $commentID
+        * @return      \wcf\data\comment\Comment
+        */
+       public function getComment($commentID) {
+               if (!empty($this->commentIDs)) {
+                       $commentList = new CommentList();
+                       $commentList->setObjectIDs($this->commentIDs);
+                       $commentList->readObjects();
+                       $this->comments = $commentList->getObjects();
+                       $this->commentIDs = array();
+               }
+               
+               if (isset($this->comments[$commentID])) {
+                       return $this->comments[$commentID];
+               }
+               
+               return null;
+       }
+       
+       /**
+        * Returns a user profile by id, fetches user profiles on first call.
+        * 
+        * @param       integer         $userID
+        * @return      \wcf\data\user\UserProfile
+        */
+       public function getUser($userID) {
+               if (!empty($this->userIDs)) {
+                       UserProfile::getUserProfiles($this->userIDs);
+                       $this->userIDs = array();
+               }
+               
+               return UserProfile::getUserProfile($userID);
+       }
+}
index c1fb78971ea87774614a0d4f83b107622fa9640c..42b10e85147dd2dae391377db7b911a7f73f061c 100644 (file)
@@ -298,10 +298,7 @@ class UserNotificationHandler extends SingletonFactory {
                $conditions->add("notification.userID = ?", array(WCF::getUser()->userID));
                if (!$showConfirmedNotifications) $conditions->add("notification.confirmed = ?", array(0));
                
-               $sql = "SELECT          notification.notificationID, notification_event.eventID, notification.authorID,
-                                       notification.timesTriggered, object_type.objectType, notification.objectID,
-                                       notification.additionalData,
-                                       notification.time".($showConfirmedNotifications ? ", notification.confirmed" : "")."
+               $sql = "SELECT          notification.*, notification_event.eventID, object_type.objectType
                        FROM            wcf".WCF_N."_user_notification notification
                        LEFT JOIN       wcf".WCF_N."_user_notification_event notification_event
                        ON              (notification_event.eventID = notification.eventID)
@@ -312,10 +309,8 @@ class UserNotificationHandler extends SingletonFactory {
                $statement = WCF::getDB()->prepareStatement($sql, $limit, $offset);
                $statement->execute($conditions->getParameters());
                
-               $authorIDs = $events = $objectTypes = $eventIDs = $notificationIDs = array();
+               $authorIDs = $objectTypes = $eventIDs = $notificationObjects = array();
                while ($row = $statement->fetchArray()) {
-                       $events[] = $row;
-                       
                        // cache object types
                        if (!isset($objectTypes[$row['objectType']])) {
                                $objectTypes[$row['objectType']] = array(
@@ -327,11 +322,12 @@ class UserNotificationHandler extends SingletonFactory {
                        
                        $objectTypes[$row['objectType']]['objectIDs'][] = $row['objectID'];
                        $eventIDs[] = $row['eventID'];
-                       $notificationIDs[] = $row['notificationID'];
+                       
+                       $notificationObjects[$row['notificationID']] = new UserNotification(null, $row);
                }
                
                // return an empty set if no notifications exist
-               if (empty($events)) {
+               if (empty($notificationObjects)) {
                        return array(
                                'count' => 0,
                                'notifications' => array()
@@ -340,7 +336,7 @@ class UserNotificationHandler extends SingletonFactory {
                
                // load authors
                $conditions = new PreparedStatementConditionBuilder();
-               $conditions->add("notificationID IN (?)", array($notificationIDs));
+               $conditions->add("notificationID IN (?)", array(array_keys($notificationObjects)));
                $sql = "SELECT          notificationID, authorID
                        FROM            wcf".WCF_N."_user_notification_author
                        ".$conditions."
@@ -376,30 +372,22 @@ class UserNotificationHandler extends SingletonFactory {
                $eventList->readObjects();
                $eventObjects = $eventList->getObjects();
                
-               // load notification objects
-               $notificationList = new UserNotificationList();
-               $notificationList->getConditionBuilder()->add("user_notification.notificationID IN (?)", array($notificationIDs));
-               $notificationList->readObjects();
-               $notificationObjects = $notificationList->getObjects();
-               
                // build notification data
                $notifications = array();
-               foreach ($events as $event) {
-                       $className = $eventObjects[$event['eventID']]->className;
-                       $class = new $className($eventObjects[$event['eventID']]);
-                       $notificationID = $event['notificationID'];
-                       
+               foreach ($notificationObjects as $notification) {
+                       $className = $eventObjects[$notification->eventID]->className;
+                       $class = new $className($eventObjects[$notification->eventID]);
                        $class->setObject(
-                               $notificationObjects[$notificationID],
-                               $objectTypes[$event['objectType']]['objects'][$event['objectID']],
-                               (isset($authors[$event['authorID']]) ? $authors[$event['authorID']] : $unknownAuthor),
-                               unserialize($event['additionalData']),
-                               $event['timesTriggered']
+                               $notification,
+                               $objectTypes[$notification->objectType]['objects'][$notification->objectID],
+                               (isset($authors[$notification->authorID]) ? $authors[$notification->authorID] : $unknownAuthor),
+                               $notification->additionalData,
+                               $notification->timesTriggered
                        );
                        
-                       if (isset($authorToNotification[$notificationID])) {
+                       if (isset($authorToNotification[$notification->notificationID])) {
                                $eventAuthors = array();
-                               foreach ($authorToNotification[$notificationID] as $userID) {
+                               foreach ($authorToNotification[$notification->notificationID] as $userID) {
                                        if (isset($authors[$userID])) {
                                                $eventAuthors[$userID] = $authors[$userID];
                                        }
@@ -412,12 +400,12 @@ class UserNotificationHandler extends SingletonFactory {
                        $data = array(
                                'authors' => count($class->getAuthors()),
                                'event' => $class,
-                               'notificationID' => $event['notificationID'],
-                               'time' => $event['time']
+                               'notificationID' => $notification->notificationID,
+                               'time' => $notification->time
                        );
                        
                        if ($showConfirmedNotifications) {
-                               $data['confirmed'] = $event['confirmed'];
+                               $data['confirmed'] = $notification->confirmed;
                        }
                        
                        $notifications[] = $data;
diff --git a/wcfsetup/install/files/lib/system/user/notification/event/AbstractSharedUserNotificationEvent.class.php b/wcfsetup/install/files/lib/system/user/notification/event/AbstractSharedUserNotificationEvent.class.php
new file mode 100644 (file)
index 0000000..14ea0b9
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+namespace wcf\system\user\notification\event;
+use wcf\data\user\notification\UserNotification;
+use wcf\data\user\UserProfile;
+use wcf\system\user\notification\object\IUserNotificationObject;
+
+/**
+ * Provides a default implementation for objects sharing common data.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.user.notification.event
+ * @category   Community Framework
+ */
+abstract class AbstractSharedUserNotificationEvent extends AbstractUserNotificationEvent {
+       /**
+        * @see \wcf\system\user\notification\event\IUserNotificationEvent::setObject()
+        */
+       public function setObject(UserNotification $notification, IUserNotificationObject $object, UserProfile $author, array $additionalData = array(), $timesTriggered = 0) {
+               parent::setObject($notification, $object, $author, $additionalData, $timesTriggered);
+               
+               $this->prepare();
+       }
+       
+       /**
+        * Provide specialized handlers with object ids, these ids will be collected and should be
+        * read once the first time data is requested from the notification event.
+        */
+       abstract protected function prepare();
+}
index d188339c09ac16fa97976b90073583d7be9d1807..bacaef722d793c234353506ace246bdbb291203c 100644 (file)
@@ -9,7 +9,7 @@ use wcf\system\WCF;
 use wcf\util\DateUtil;
 
 /**
- * Provides default a implementation for user notification events.
+ * Provides a default implementation for user notification events.
  * 
  * @author     Marcel Werk, Oliver Kliebisch
  * @copyright  2001-2014 WoltLab GmbH, Oliver Kliebisch
index f96c2cc6179cce646ddbafa7b6fbcab5f646693c..f72869945899bb880fd6d3ba4a857f4a36bc3925 100644 (file)
@@ -1,8 +1,8 @@
 <?php
 namespace wcf\system\user\notification\event;
 use wcf\data\user\User;
+use wcf\system\comment\CommentDataHandler;
 use wcf\system\request\LinkHandler;
-use wcf\system\user\notification\event\AbstractUserNotificationEvent;
 use wcf\system\WCF;
 
 /**
@@ -15,12 +15,19 @@ use wcf\system\WCF;
  * @subpackage system.user.notification.event
  * @category   Community Framework
  */
-class UserProfileCommentLikeUserNotificationEvent extends AbstractUserNotificationEvent {
+class UserProfileCommentLikeUserNotificationEvent extends AbstractSharedUserNotificationEvent {
        /**
         * @see \wcf\system\user\notification\event\AbstractUserNotificationEvent::$stackable
         */
        protected $stackable = true;
        
+       /**
+        * @see \wcf\system\user\notification\event\AbstractUserNotificationEvent::prepare()
+        */
+       protected function prepare() {
+               CommentDataHandler::getInstance()->cacheUserID($this->additionalData['objectID']);
+       }
+       
        /**
         * @see \wcf\system\user\notification\event\IUserNotificationEvent::getTitle()
         */
@@ -44,7 +51,7 @@ class UserProfileCommentLikeUserNotificationEvent extends AbstractUserNotificati
                $count = count($authors);
                $owner = null;
                if ($this->additionalData['objectID'] != WCF::getUser()->userID) {
-                       $owner = new User($this->additionalData['objectID']);
+                       $owner = CommentDataHandler::getInstance()->getUser($this->additionalData['objectID']);
                }
                
                if ($count > 1) {
index 6ba0b94a47cc17ece3a37d2d21793733e7e98a0d..bc1263cdfe86e21fb58f83a45c393a2ef828675b 100644 (file)
@@ -2,8 +2,8 @@
 namespace wcf\system\user\notification\event;
 use wcf\data\comment\Comment;
 use wcf\data\user\User;
+use wcf\system\comment\CommentDataHandler;
 use wcf\system\request\LinkHandler;
-use wcf\system\user\notification\event\AbstractUserNotificationEvent;
 use wcf\system\WCF;
 
 /**
@@ -16,12 +16,19 @@ use wcf\system\WCF;
  * @subpackage system.user.notification.event
  * @category   Community Framework
  */
-class UserProfileCommentResponseOwnerUserNotificationEvent extends AbstractUserNotificationEvent {
+class UserProfileCommentResponseOwnerUserNotificationEvent extends AbstractSharedUserNotificationEvent {
        /**
         * @see \wcf\system\user\notification\event\AbstractUserNotificationEvent::$stackable
         */
        protected $stackable = true;
        
+       /**
+        * @see \wcf\system\user\notification\event\AbstractUserNotificationEvent::prepare()
+        */
+       protected function prepare() {
+               CommentDataHandler::getInstance()->cacheCommentID($this->userNotificationObject->commentID);
+       }
+       
        /**
         * @see \wcf\system\user\notification\event\IUserNotificationEvent::getTitle()
         */
index c3f2c4b0e0715224ac2ae8d2887abcaa12aba962..68b5eea6b457de1e5f14cbd7909a74f60e6dbfdc 100644 (file)
@@ -1,9 +1,8 @@
 <?php
 namespace wcf\system\user\notification\event;
-use wcf\data\comment\Comment;
 use wcf\data\user\User;
+use wcf\system\comment\CommentDataHandler;
 use wcf\system\request\LinkHandler;
-use wcf\system\user\notification\event\AbstractUserNotificationEvent;
 
 /**
  * User notification event for profile commment responses.
@@ -15,12 +14,21 @@ use wcf\system\user\notification\event\AbstractUserNotificationEvent;
  * @subpackage system.user.notification.event
  * @category   Community Framework
  */
-class UserProfileCommentResponseUserNotificationEvent extends AbstractUserNotificationEvent {
+class UserProfileCommentResponseUserNotificationEvent extends AbstractSharedUserNotificationEvent {
        /**
         * @see \wcf\system\user\notification\event\AbstractUserNotificationEvent::$stackable
         */
        protected $stackable = true;
        
+       /**
+        * @see \wcf\system\user\notification\event\AbstractUserNotificationEvent::prepare()
+        */
+       protected function prepare() {
+               CommentDataHandler::getInstance()->cacheCommentID($this->userNotificationObject->commentID);
+               CommentDataHandler::getInstance()->cacheUserID($this->additionalData['objectID']);
+               CommentDataHandler::getInstance()->cacheUserID($this->additionalData['userID']);
+       }
+       
        /**
         * @see \wcf\system\user\notification\event\IUserNotificationEvent::getTitle()
         */
@@ -41,10 +49,10 @@ class UserProfileCommentResponseUserNotificationEvent extends AbstractUserNotifi
         */
        public function getMessage() {
                // @todo: use cache or a single query to retrieve required data
-               $comment = new Comment($this->userNotificationObject->commentID);
-               $owner = new User($comment->objectID);
+               $comment = CommentDataHandler::getInstance()->getComment($this->userNotificationObject->commentID);
+               $owner = CommentDataHandler::getInstance()->getUser($comment->objectID);
                if ($comment->userID) {
-                       $commentAuthor = new User($comment->userID);
+                       $commentAuthor = CommentDataHandler::getInstance()->getUser($comment->userID);
                }
                else {
                        $commentAuthor = new User(null, array(
@@ -73,7 +81,7 @@ class UserProfileCommentResponseUserNotificationEvent extends AbstractUserNotifi
         * @see \wcf\system\user\notification\event\IUserNotificationEvent::getEmailMessage()
         */
        public function getEmailMessage($notificationType = 'instant') {
-               $comment = new Comment($this->userNotificationObject->commentID);
+               $comment = CommentDataHandler::getInstance()->getComment($this->userNotificationObject->commentID);
                $user = new User($comment->objectID);
                
                return $this->getLanguage()->getDynamicVariable('wcf.user.notification.commentResponse.mail', array(
@@ -89,8 +97,8 @@ class UserProfileCommentResponseUserNotificationEvent extends AbstractUserNotifi
         */
        public function getLink() {
                // @todo: use cache or a single query to retrieve required data
-               $comment = new Comment($this->userNotificationObject->commentID);
-               $user = new User($comment->objectID);
+               $comment = CommentDataHandler::getInstance()->getComment($this->userNotificationObject->commentID);
+               $user = CommentDataHandler::getInstance()->getUser($comment->objectID);
                
                return LinkHandler::getInstance()->getLink('User', array('object' => $user), '#wall');
        }