diff --git a/src/contracts/Persistence/Notification/Handler.php b/src/contracts/Persistence/Notification/Handler.php index 0ba5686d6a..a87af531a9 100644 --- a/src/contracts/Persistence/Notification/Handler.php +++ b/src/contracts/Persistence/Notification/Handler.php @@ -51,20 +51,16 @@ public function countPendingNotifications(int $ownerId): int; public function getNotificationById(int $notificationId): Notification; /** - * @param int $userId - * @param int $offset - * @param int $limit + * @param string[] $query * * @return \Ibexa\Contracts\Core\Persistence\Notification\Notification[] */ - public function loadUserNotifications(int $userId, int $offset, int $limit): array; + public function loadUserNotifications(int $userId, int $offset, int $limit, array $query = []): array; /** - * @param int $currentUserId - * - * @return int + * @param string[] $query */ - public function countNotifications(int $currentUserId): int; + public function countNotifications(int $currentUserId, array $query = []): int; /** * @param \Ibexa\Contracts\Core\Repository\Values\Notification\Notification $notification diff --git a/src/contracts/Repository/Decorator/NotificationServiceDecorator.php b/src/contracts/Repository/Decorator/NotificationServiceDecorator.php index 7236473ffd..f4738425c9 100644 --- a/src/contracts/Repository/Decorator/NotificationServiceDecorator.php +++ b/src/contracts/Repository/Decorator/NotificationServiceDecorator.php @@ -23,11 +23,15 @@ public function __construct(NotificationService $innerService) $this->innerService = $innerService; } + /** + * @param string[] $query + */ public function loadNotifications( int $offset, - int $limit + int $limit, + array $query = [] ): NotificationList { - return $this->innerService->loadNotifications($offset, $limit); + return $this->innerService->loadNotifications($offset, $limit, $query); } public function getNotification(int $notificationId): Notification @@ -50,9 +54,12 @@ public function getPendingNotificationCount(): int return $this->innerService->getPendingNotificationCount(); } - public function getNotificationCount(): int + /** + * @param string[] $query + */ + public function getNotificationCount(array $query = []): int { - return $this->innerService->getNotificationCount(); + return $this->innerService->getNotificationCount($query); } public function createNotification(CreateStruct $createStruct): Notification diff --git a/src/contracts/Repository/NotificationService.php b/src/contracts/Repository/NotificationService.php index b702c99c41..ab198f515e 100644 --- a/src/contracts/Repository/NotificationService.php +++ b/src/contracts/Repository/NotificationService.php @@ -19,14 +19,9 @@ interface NotificationService { /** - * Get currently logged user notifications. - * - * @param int $offset the start offset for paging - * @param int $limit the number of notifications returned - * - * @return \Ibexa\Contracts\Core\Repository\Values\Notification\NotificationList + * @param string[] $query */ - public function loadNotifications(int $offset, int $limit): NotificationList; + public function loadNotifications(int $offset, int $limit, array $query = []): NotificationList; /** * Load single notification (by ID). @@ -65,11 +60,9 @@ public function markNotificationAsUnread(Notification $notification): void; public function getPendingNotificationCount(): int; /** - * Get count of total users notifications. - * - * @return int + * @param string[] $query */ - public function getNotificationCount(): int; + public function getNotificationCount(array $query = []): int; /** * Creates a new notification. diff --git a/src/lib/Persistence/Cache/NotificationHandler.php b/src/lib/Persistence/Cache/NotificationHandler.php index 572687d4cc..494ac144cf 100644 --- a/src/lib/Persistence/Cache/NotificationHandler.php +++ b/src/lib/Persistence/Cache/NotificationHandler.php @@ -99,12 +99,17 @@ public function countPendingNotifications(int $ownerId): int } /** - * {@inheritdoc} + * @param string[] $query */ - public function countNotifications(int $ownerId): int + public function countNotifications(int $ownerId, array $query = []): int { + $cacheKeyParams = [$ownerId]; + if (!empty($query)) { + $cacheKeyParams[] = json_encode($query); + } + $cacheItem = $this->cache->getItem( - $this->cacheIdentifierGenerator->generateKey(self::NOTIFICATION_COUNT_IDENTIFIER, [$ownerId], true) + $this->cacheIdentifierGenerator->generateKey(self::NOTIFICATION_COUNT_IDENTIFIER, $cacheKeyParams, true) ); $count = $cacheItem->get(); @@ -114,9 +119,10 @@ public function countNotifications(int $ownerId): int $this->logger->logCall(__METHOD__, [ 'ownerId' => $ownerId, + 'query' => $query, ]); - $count = $this->persistenceHandler->notificationHandler()->countNotifications($ownerId); + $count = $this->persistenceHandler->notificationHandler()->countNotifications($ownerId, $query); $cacheItem->set($count); $this->cache->save($cacheItem); @@ -151,17 +157,18 @@ public function getNotificationById(int $notificationId): Notification } /** - * {@inheritdoc} + * @param string[] $query */ - public function loadUserNotifications(int $userId, int $offset, int $limit): array + public function loadUserNotifications(int $userId, int $offset, int $limit, array $query = []): array { $this->logger->logCall(__METHOD__, [ 'ownerId' => $userId, 'offset' => $offset, 'limit' => $limit, + 'query' => $query, ]); - return $this->persistenceHandler->notificationHandler()->loadUserNotifications($userId, $offset, $limit); + return $this->persistenceHandler->notificationHandler()->loadUserNotifications($userId, $offset, $limit, $query); } } diff --git a/src/lib/Persistence/Legacy/Notification/Gateway.php b/src/lib/Persistence/Legacy/Notification/Gateway.php index be47b14407..a2f73f3555 100644 --- a/src/lib/Persistence/Legacy/Notification/Gateway.php +++ b/src/lib/Persistence/Legacy/Notification/Gateway.php @@ -42,11 +42,9 @@ abstract public function getNotificationById(int $notificationId): array; abstract public function updateNotification(Notification $notification): void; /** - * @param int $userId - * - * @return int + * @param string[] $query */ - abstract public function countUserNotifications(int $userId): int; + abstract public function countUserNotifications(int $userId, array $query = []): int; /** * Count users unread Notifications. @@ -58,13 +56,14 @@ abstract public function countUserNotifications(int $userId): int; abstract public function countUserPendingNotifications(int $userId): int; /** - * @param int $userId - * @param int $offset - * @param int $limit - * - * @return array + * @param string[] $query */ - abstract public function loadUserNotifications(int $userId, int $offset = 0, int $limit = -1): array; + abstract public function loadUserNotifications( + int $userId, + int $offset = 0, + int $limit = -1, + array $query = [] + ): array; /** * @param int $notificationId diff --git a/src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php index d862209c8d..65a2d2f7c7 100644 --- a/src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php @@ -9,6 +9,7 @@ namespace Ibexa\Core\Persistence\Legacy\Notification\Gateway; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Query\QueryBuilder; use Ibexa\Contracts\Core\Persistence\Notification\CreateStruct; use Ibexa\Contracts\Core\Persistence\Notification\Notification; use Ibexa\Core\Base\Exceptions\InvalidArgumentException; @@ -25,20 +26,13 @@ class DoctrineDatabase extends Gateway public const COLUMN_CREATED = 'created'; public const COLUMN_DATA = 'data'; - /** @var \Doctrine\DBAL\Connection */ - private $connection; + private Connection $connection; - /** - * @param \Doctrine\DBAL\Connection $connection - */ public function __construct(Connection $connection) { $this->connection = $connection; } - /** - * {@inheritdoc} - */ public function insert(CreateStruct $createStruct): int { $query = $this->connection->createQueryBuilder(); @@ -62,9 +56,6 @@ public function insert(CreateStruct $createStruct): int return (int) $this->connection->lastInsertId(); } - /** - * {@inheritdoc} - */ public function getNotificationById(int $notificationId): array { $query = $this->connection->createQueryBuilder(); @@ -78,9 +69,6 @@ public function getNotificationById(int $notificationId): array return $query->execute()->fetchAll(PDO::FETCH_ASSOC); } - /** - * {@inheritdoc} - */ public function updateNotification(Notification $notification): void { if (!isset($notification->id) || !is_numeric($notification->id)) { @@ -100,23 +88,57 @@ public function updateNotification(Notification $notification): void } /** - * {@inheritdoc} + * @param string[] $query */ - public function countUserNotifications(int $userId): int + private function applyFilters(QueryBuilder $queryBuilder, array $query): void { - $query = $this->connection->createQueryBuilder(); - $query + if (isset($query['type'])) { + $queryBuilder + ->andWhere($queryBuilder->expr()->like(self::COLUMN_TYPE, ':type')) + ->setParameter(':type', '%' . $query['type'] . '%'); + } + + if (isset($query['status']) && is_array($query['status'])) { + if (in_array('read', $query['status'])) { + $queryBuilder->andWhere($queryBuilder->expr()->eq(self::COLUMN_IS_PENDING, ':status_read')) + ->setParameter(':status_read', false); + } + if (in_array('unread', $query['status'])) { + $queryBuilder->andWhere($queryBuilder->expr()->eq(self::COLUMN_IS_PENDING, ':status_unread')) + ->setParameter(':status_unread', true); + } + } + + if (isset($query['created_from'])) { + $queryBuilder->andWhere($queryBuilder->expr()->gte(self::COLUMN_CREATED, ':created_from')) + ->setParameter(':created_from', $query['created_from'], PDO::PARAM_INT); + } + + if (isset($query['created_to'])) { + $queryBuilder->andWhere($queryBuilder->expr()->lte(self::COLUMN_CREATED, ':created_to')) + ->setParameter(':created_to', $query['created_to'], PDO::PARAM_INT); + } + } + + /** + * @param string[] $query + */ + public function countUserNotifications(int $userId, array $query = []): int + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder ->select('COUNT(' . self::COLUMN_ID . ')') ->from(self::TABLE_NOTIFICATION) - ->where($query->expr()->eq(self::COLUMN_OWNER_ID, ':user_id')) + ->where($queryBuilder->expr()->eq(self::COLUMN_OWNER_ID, ':user_id')) ->setParameter(':user_id', $userId, PDO::PARAM_INT); - return (int)$query->execute()->fetchColumn(); + if (!empty($query)) { + $this->applyFilters($queryBuilder, $query); + } + + return (int)$queryBuilder->execute()->fetchColumn(); } - /** - * {@inheritdoc} - */ public function countUserPendingNotifications(int $userId): int { $query = $this->connection->createQueryBuilder(); @@ -133,30 +155,31 @@ public function countUserPendingNotifications(int $userId): int } /** - * {@inheritdoc} + * @param string[] $query */ - public function loadUserNotifications(int $userId, int $offset = 0, int $limit = -1): array + public function loadUserNotifications(int $userId, int $offset = 0, int $limit = -1, array $query = []): array { - $query = $this->connection->createQueryBuilder(); - $query + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder ->select(...$this->getColumns()) ->from(self::TABLE_NOTIFICATION) - ->where($query->expr()->eq(self::COLUMN_OWNER_ID, ':user_id')) + ->where($queryBuilder->expr()->eq(self::COLUMN_OWNER_ID, ':user_id')) ->setFirstResult($offset); + if (!empty($query)) { + $this->applyFilters($queryBuilder, $query); + } + if ($limit > 0) { - $query->setMaxResults($limit); + $queryBuilder->setMaxResults($limit); } - $query->orderBy(self::COLUMN_ID, 'DESC'); - $query->setParameter(':user_id', $userId, PDO::PARAM_INT); + $queryBuilder->orderBy(self::COLUMN_ID, 'DESC'); + $queryBuilder->setParameter(':user_id', $userId, PDO::PARAM_INT); - return $query->execute()->fetchAll(PDO::FETCH_ASSOC); + return $queryBuilder->execute()->fetchAll(PDO::FETCH_ASSOC); } - /** - * {@inheritdoc} - */ public function delete(int $notificationId): void { $query = $this->connection->createQueryBuilder(); @@ -168,9 +191,6 @@ public function delete(int $notificationId): void $query->execute(); } - /** - * @return array - */ private function getColumns(): array { return [ diff --git a/src/lib/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php index 562cd536a2..90a97db8cc 100644 --- a/src/lib/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php +++ b/src/lib/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php @@ -52,10 +52,13 @@ public function updateNotification(Notification $notification): void } } - public function countUserNotifications(int $userId): int + /** + * @param string[] $query + */ + public function countUserNotifications(int $userId, array $query = []): int { try { - return $this->innerGateway->countUserNotifications($userId); + return $this->innerGateway->countUserNotifications($userId, $query); } catch (DBALException | PDOException $e) { throw DatabaseException::wrap($e); } @@ -70,10 +73,13 @@ public function countUserPendingNotifications(int $userId): int } } - public function loadUserNotifications(int $userId, int $offset = 0, int $limit = -1): array + /** + * @param string[] $query + */ + public function loadUserNotifications(int $userId, int $offset = 0, int $limit = -1, array $query = []): array { try { - return $this->innerGateway->loadUserNotifications($userId, $offset, $limit); + return $this->innerGateway->loadUserNotifications($userId, $offset, $limit, $query); } catch (DBALException | PDOException $e) { throw DatabaseException::wrap($e); } diff --git a/src/lib/Persistence/Legacy/Notification/Handler.php b/src/lib/Persistence/Legacy/Notification/Handler.php index 9dd97545f2..f8db913c4d 100644 --- a/src/lib/Persistence/Legacy/Notification/Handler.php +++ b/src/lib/Persistence/Legacy/Notification/Handler.php @@ -90,20 +90,20 @@ public function updateNotification(APINotification $apiNotification, UpdateStruc } /** - * {@inheritdoc} + * @param string[] $query */ - public function countNotifications(int $userId): int + public function countNotifications(int $userId, array $query = []): int { - return $this->gateway->countUserNotifications($userId); + return $this->gateway->countUserNotifications($userId, $query); } /** - * {@inheritdoc} + * @param string[] $query */ - public function loadUserNotifications(int $userId, int $offset, int $limit): array + public function loadUserNotifications(int $userId, int $offset, int $limit, array $query = []): array { return $this->mapper->extractNotificationsFromRows( - $this->gateway->loadUserNotifications($userId, $offset, $limit) + $this->gateway->loadUserNotifications($userId, $offset, $limit, $query) ); } diff --git a/src/lib/Repository/NotificationService.php b/src/lib/Repository/NotificationService.php index 35a63f91f5..557bff5e43 100644 --- a/src/lib/Repository/NotificationService.php +++ b/src/lib/Repository/NotificationService.php @@ -41,26 +41,24 @@ public function __construct(Handler $persistenceHandler, PermissionResolver $per } /** - * {@inheritdoc} + * @param string[] $query */ - public function loadNotifications(int $offset = 0, int $limit = 25): NotificationList + public function loadNotifications(int $offset = 0, int $limit = 25, array $query = []): NotificationList { $currentUserId = $this->getCurrentUserId(); $list = new NotificationList(); - $list->totalCount = $this->persistenceHandler->countNotifications($currentUserId); + $list->totalCount = $this->persistenceHandler->countNotifications($currentUserId, $query); + if ($list->totalCount > 0) { $list->items = array_map(function (Notification $spiNotification) { return $this->buildDomainObject($spiNotification); - }, $this->persistenceHandler->loadUserNotifications($currentUserId, $offset, $limit)); + }, $this->persistenceHandler->loadUserNotifications($currentUserId, $offset, $limit, $query)); } return $list; } - /** - * {@inheritdoc} - */ public function createNotification(APICreateStruct $createStruct): APINotification { $spiCreateStruct = new CreateStruct(); @@ -85,9 +83,6 @@ public function createNotification(APICreateStruct $createStruct): APINotificati ); } - /** - * {@inheritdoc} - */ public function getNotification(int $notificationId): APINotification { $notification = $this->persistenceHandler->getNotificationById($notificationId); @@ -100,9 +95,6 @@ public function getNotification(int $notificationId): APINotification return $this->buildDomainObject($notification); } - /** - * {@inheritdoc} - */ public function markNotificationAsRead(APINotification $notification): void { $currentUserId = $this->getCurrentUserId(); @@ -147,9 +139,6 @@ public function markNotificationAsUnread(APINotification $notification): void $this->persistenceHandler->updateNotification($notification, $updateStruct); } - /** - * {@inheritdoc} - */ public function getPendingNotificationCount(): int { return $this->persistenceHandler->countPendingNotifications( @@ -158,18 +147,16 @@ public function getPendingNotificationCount(): int } /** - * {@inheritdoc} + * @param string[] $query */ - public function getNotificationCount(): int + public function getNotificationCount(array $query = []): int { return $this->persistenceHandler->countNotifications( - $this->getCurrentUserId() + $this->getCurrentUserId(), + $query ); } - /** - * {@inheritdoc} - */ public function deleteNotification(APINotification $notification): void { $this->persistenceHandler->delete($notification); diff --git a/src/lib/Repository/SiteAccessAware/NotificationService.php b/src/lib/Repository/SiteAccessAware/NotificationService.php index e350b1592a..2cf53b78f0 100644 --- a/src/lib/Repository/SiteAccessAware/NotificationService.php +++ b/src/lib/Repository/SiteAccessAware/NotificationService.php @@ -30,16 +30,11 @@ public function __construct( } /** - * Get currently logged user notifications. - * - * @param int $offset - * @param int $limit - * - * @return \Ibexa\Contracts\Core\Repository\Values\Notification\NotificationList + * @param string[] $query */ - public function loadNotifications(int $offset, int $limit): NotificationList + public function loadNotifications(int $offset, int $limit, array $query = []): NotificationList { - return $this->service->loadNotifications($offset, $limit); + return $this->service->loadNotifications($offset, $limit, $query); } /** @@ -78,13 +73,11 @@ public function getPendingNotificationCount(): int } /** - * Get count of total users notifications. - * - * @return int + * @param string[] $query */ - public function getNotificationCount(): int + public function getNotificationCount(array $query = []): int { - return $this->service->getNotificationCount(); + return $this->service->getNotificationCount($query); } /** diff --git a/tests/integration/Core/Repository/NotificationServiceTest.php b/tests/integration/Core/Repository/NotificationServiceTest.php index 0825e56fe6..0e3196f1b7 100644 --- a/tests/integration/Core/Repository/NotificationServiceTest.php +++ b/tests/integration/Core/Repository/NotificationServiceTest.php @@ -28,15 +28,16 @@ public function testLoadNotifications() { $repository = $this->getRepository(); - /* BEGIN: Use Case */ $notificationService = $repository->getNotificationService(); - $notificationList = $notificationService->loadNotifications(0, 25); - /* END: Use Case */ + $query = ['type' => 'Workflow:Review']; + $notificationList = $notificationService->loadNotifications(0, 25, $query); $this->assertInstanceOf(NotificationList::class, $notificationList); $this->assertIsArray($notificationList->items); $this->assertIsInt($notificationList->totalCount); - $this->assertEquals(5, $notificationList->totalCount); + + $expectedCount = 3; + $this->assertEquals($expectedCount, $notificationList->totalCount); } /** diff --git a/tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php index 369459d921..3da62c8551 100644 --- a/tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php +++ b/tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php @@ -115,7 +115,7 @@ public function testLoadUserNotifications() $offset = 1; $limit = 3; - $results = $this->getGateway()->loadUserNotifications($userId, $offset, $limit); + $resultsWithoutQuery = $this->getGateway()->loadUserNotifications($userId, $offset, $limit); $this->assertEquals([ [ @@ -142,7 +142,34 @@ public function testLoadUserNotifications() 'created' => '1529998652', 'data' => null, ], - ], $results); + ], $resultsWithoutQuery); + + $query = ['type' => 'Workflow:Review']; + $resultsWithQuery = $this->getGateway()->loadUserNotifications($userId, $offset, $limit, $query); + + $this->assertEquals([ + [ + 'id' => '4', + 'owner_id' => '14', + 'is_pending' => 1, + 'type' => 'Workflow:Review', + 'created' => '1530005852', + 'data' => null, + ], + [ + 'id' => '1', + 'owner_id' => '14', + 'is_pending' => 1, + 'type' => 'Workflow:Review', + 'created' => '1529995052', + 'data' => null, + ], + ], $resultsWithQuery); + + $queryNoResults = ['type' => 'NonExistingType']; + $resultsWithNoResults = $this->getGateway()->loadUserNotifications($userId, $offset, $limit, $queryNoResults); + + $this->assertEquals([], $resultsWithNoResults); } public function testDelete() diff --git a/tests/lib/Persistence/Legacy/Notification/HandlerTest.php b/tests/lib/Persistence/Legacy/Notification/HandlerTest.php index 3dd8d06225..861d47118a 100644 --- a/tests/lib/Persistence/Legacy/Notification/HandlerTest.php +++ b/tests/lib/Persistence/Legacy/Notification/HandlerTest.php @@ -167,32 +167,42 @@ public function testLoadUserNotifications() $ownerId = 9; $limit = 5; $offset = 0; + $query = ['type' => 'Workflow:Review']; $rows = [ - ['id' => 1/* ... */], - ['id' => 2/* ... */], - ['id' => 3/* ... */], + ['id' => 1, 'owner_id' => 9, 'is_pending' => 1, 'type' => 'Workflow:Review', 'created' => '1530005852', 'data' => null], + ['id' => 2, 'owner_id' => 9, 'is_pending' => 0, 'type' => 'Workflow:Reject', 'created' => '1530002252', 'data' => null], + ['id' => 3, 'owner_id' => 9, 'is_pending' => 0, 'type' => 'Workflow:Approve', 'created' => '1529998652', 'data' => null], ]; $objects = [ - new Notification(['id' => 1/* ... */]), - new Notification(['id' => 2/* ... */]), - new Notification(['id' => 3/* ... */]), + new Notification(['id' => 1, 'ownerId' => 9, 'isPending' => 1, 'type' => 'Workflow:Review', 'created' => 1530005852, 'data' => null]), + new Notification(['id' => 2, 'ownerId' => 9, 'isPending' => 0, 'type' => 'Workflow:Reject', 'created' => 1530002252, 'data' => null]), + new Notification(['id' => 3, 'ownerId' => 9, 'isPending' => 0, 'type' => 'Workflow:Approve', 'created' => 1529998652, 'data' => null]), ]; $this->gateway - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('loadUserNotifications') - ->with($ownerId, $offset, $limit) + ->with( + $this->logicalOr( + $this->equalTo($ownerId), + $this->equalTo($query) + ), + $offset, + $limit + ) ->willReturn($rows); $this->mapper - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('extractNotificationsFromRows') ->with($rows) ->willReturn($objects); $this->assertEquals($objects, $this->handler->loadUserNotifications($ownerId, $offset, $limit)); + + $this->assertEquals($objects, $this->handler->loadUserNotifications($ownerId, $offset, $limit, $query)); } public function testDelete() diff --git a/tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php b/tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php index 8fbcffc0da..f041faa1c8 100644 --- a/tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php @@ -36,9 +36,12 @@ public function testLoadNotificationsDecorator() $parameters = [ 264, 959, + ['type' => 'some search query'], ]; - $serviceMock->expects($this->once())->method('loadNotifications')->with(...$parameters); + $serviceMock->expects($this->once()) + ->method('loadNotifications') + ->with(...$parameters); $decoratedService->loadNotifications(...$parameters); }