From d7392c63cd49af6f7b8f5dd6f17708014e4b28e1 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Wed, 2 Jul 2014 01:04:44 +0200 Subject: [PATCH] Using a cache and grouped data fetching to avoid superfluous queries --- com.woltlab.wcf/userNotificationEvent.xml | 4 +- .../lib/data/comment/CommentAction.class.php | 24 +++-- .../data/comment/LikeableComment.class.php | 5 +- .../LikeableCommentResponse.class.php | 2 +- .../comment/CommentDataHandler.class.php | 94 +++++++++++++++++++ .../UserNotificationHandler.class.php | 50 ++++------ ...tractSharedUserNotificationEvent.class.php | 32 +++++++ .../AbstractUserNotificationEvent.class.php | 2 +- ...CommentLikeUserNotificationEvent.class.php | 13 ++- ...sponseOwnerUserNotificationEvent.class.php | 11 ++- ...entResponseUserNotificationEvent.class.php | 26 +++-- 11 files changed, 207 insertions(+), 56 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/comment/CommentDataHandler.class.php create mode 100644 wcfsetup/install/files/lib/system/user/notification/event/AbstractSharedUserNotificationEvent.class.php diff --git a/com.woltlab.wcf/userNotificationEvent.xml b/com.woltlab.wcf/userNotificationEvent.xml index 9e6b8db102..cf0cd194bf 100644 --- a/com.woltlab.wcf/userNotificationEvent.xml +++ b/com.woltlab.wcf/userNotificationEvent.xml @@ -32,13 +32,13 @@ like com.woltlab.wcf.user.profileComment.like.notification - + 1 like com.woltlab.wcf.user.profileComment.response.like.notification - + 1 diff --git a/wcfsetup/install/files/lib/data/comment/CommentAction.class.php b/wcfsetup/install/files/lib/data/comment/CommentAction.class.php index 4e36131ffe..1c3b2dd048 100644 --- a/wcfsetup/install/files/lib/data/comment/CommentAction.class.php +++ b/wcfsetup/install/files/lib/data/comment/CommentAction.class.php @@ -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 + )); } } } diff --git a/wcfsetup/install/files/lib/data/comment/LikeableComment.class.php b/wcfsetup/install/files/lib/data/comment/LikeableComment.class.php index 9ea3bef730..458d3a62bc 100644 --- a/wcfsetup/install/files/lib/data/comment/LikeableComment.class.php +++ b/wcfsetup/install/files/lib/data/comment/LikeableComment.class.php @@ -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 + )); } } } diff --git a/wcfsetup/install/files/lib/data/comment/response/LikeableCommentResponse.class.php b/wcfsetup/install/files/lib/data/comment/response/LikeableCommentResponse.class.php index c31e866d6d..bc03823b07 100644 --- a/wcfsetup/install/files/lib/data/comment/response/LikeableCommentResponse.class.php +++ b/wcfsetup/install/files/lib/data/comment/response/LikeableCommentResponse.class.php @@ -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 index 0000000000..f787c3b28b --- /dev/null +++ b/wcfsetup/install/files/lib/system/comment/CommentDataHandler.class.php @@ -0,0 +1,94 @@ + + * @package com.woltlab.wcf + * @subpackage system.comment + * @category Community Framework + */ +class CommentDataHandler extends SingletonFactory { + /** + * list of comment ids + * @var array + */ + protected $commentIDs = array(); + + /** + * list of cached comment objects + * @var array<\wcf\data\comment\Comment> + */ + protected $comments = array(); + + /** + * list of user ids + * @var array + */ + 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); + } +} diff --git a/wcfsetup/install/files/lib/system/user/notification/UserNotificationHandler.class.php b/wcfsetup/install/files/lib/system/user/notification/UserNotificationHandler.class.php index c1fb78971e..42b10e8514 100644 --- a/wcfsetup/install/files/lib/system/user/notification/UserNotificationHandler.class.php +++ b/wcfsetup/install/files/lib/system/user/notification/UserNotificationHandler.class.php @@ -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 index 0000000000..14ea0b9704 --- /dev/null +++ b/wcfsetup/install/files/lib/system/user/notification/event/AbstractSharedUserNotificationEvent.class.php @@ -0,0 +1,32 @@ + + * @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(); +} diff --git a/wcfsetup/install/files/lib/system/user/notification/event/AbstractUserNotificationEvent.class.php b/wcfsetup/install/files/lib/system/user/notification/event/AbstractUserNotificationEvent.class.php index d188339c09..bacaef722d 100644 --- a/wcfsetup/install/files/lib/system/user/notification/event/AbstractUserNotificationEvent.class.php +++ b/wcfsetup/install/files/lib/system/user/notification/event/AbstractUserNotificationEvent.class.php @@ -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 diff --git a/wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentLikeUserNotificationEvent.class.php b/wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentLikeUserNotificationEvent.class.php index f96c2cc617..f728699458 100644 --- a/wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentLikeUserNotificationEvent.class.php +++ b/wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentLikeUserNotificationEvent.class.php @@ -1,8 +1,8 @@ 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) { diff --git a/wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentResponseOwnerUserNotificationEvent.class.php b/wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentResponseOwnerUserNotificationEvent.class.php index 6ba0b94a47..bc1263cdfe 100644 --- a/wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentResponseOwnerUserNotificationEvent.class.php +++ b/wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentResponseOwnerUserNotificationEvent.class.php @@ -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() */ diff --git a/wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentResponseUserNotificationEvent.class.php b/wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentResponseUserNotificationEvent.class.php index c3f2c4b0e0..68b5eea6b4 100644 --- a/wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentResponseUserNotificationEvent.class.php +++ b/wcfsetup/install/files/lib/system/user/notification/event/UserProfileCommentResponseUserNotificationEvent.class.php @@ -1,9 +1,8 @@ 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'); } -- 2.20.1