Skip to content

Commit 156ffd1

Browse files
committed
IBX-9060: Refactored notification filtering with criterion handlers, updated NotificationQuery, Gateway, tests and services
1 parent 852c013 commit 156ffd1

File tree

12 files changed

+206
-122
lines changed

12 files changed

+206
-122
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
/**
4+
* @copyright Copyright (C) Ibexa AS. All rights reserved.
5+
* @license For full copyright and license information view LICENSE file distributed with this source code.
6+
*/
7+
declare(strict_types=1);
8+
9+
namespace Ibexa\Contracts\Core\Repository\Values\Notification;
10+
11+
use Doctrine\DBAL\Query\QueryBuilder;
12+
use Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion;
13+
14+
interface CriterionHandlerInterface
15+
{
16+
public function supports(Criterion $criterion): bool;
17+
18+
public function apply(QueryBuilder $qb, Criterion $criterion): void;
19+
}

src/contracts/Repository/Values/Notification/Query/Criterion/LogicalAnd.php

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/contracts/Repository/Values/Notification/Query/Criterion/NotificationQuery.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313
final class NotificationQuery
1414
{
1515
/** @var \Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion[] */
16-
public array $criteria = [];
16+
private array $criteria;
1717

18-
public int $offset = 0;
18+
private int $offset;
1919

20-
public int $limit = 25;
20+
private int $limit;
2121

2222
/**
2323
* @param \Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion[] $criteria
@@ -33,4 +33,22 @@ public function addCriterion(Criterion $criterion): void
3333
{
3434
$this->criteria[] = $criterion;
3535
}
36+
37+
/**
38+
* @return \Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion[]
39+
*/
40+
public function getCriteria(): array
41+
{
42+
return $this->criteria;
43+
}
44+
45+
public function getOffset(): int
46+
{
47+
return $this->offset;
48+
}
49+
50+
public function getLimit(): int
51+
{
52+
return $this->limit;
53+
}
3654
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/**
4+
* @copyright Copyright (C) Ibexa AS. All rights reserved.
5+
* @license For full copyright and license information view LICENSE file distributed with this source code.
6+
*/
7+
declare(strict_types=1);
8+
9+
namespace Ibexa\Core\Persistence\Legacy\Notification\Gateway\CriterionHandler;
10+
11+
use Doctrine\DBAL\Query\QueryBuilder;
12+
use Ibexa\Contracts\Core\Repository\Values\Notification\CriterionHandlerInterface;
13+
use Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion;
14+
use Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion\DateCreated;
15+
use Ibexa\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase;
16+
17+
final class DateCreatedCriterionHandler implements CriterionHandlerInterface
18+
{
19+
public function supports(Criterion $criterion): bool
20+
{
21+
return $criterion instanceof DateCreated;
22+
}
23+
24+
public function apply(QueryBuilder $qb, Criterion $criterion): void
25+
{
26+
/** @var \Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion\DateCreated $criterion */
27+
if ($criterion->from !== null) {
28+
$qb->andWhere($qb->expr()->gte(DoctrineDatabase::COLUMN_CREATED, ':created_from'));
29+
$qb->setParameter(':created_from', $criterion->from->getTimestamp());
30+
}
31+
32+
if ($criterion->to !== null) {
33+
$qb->andWhere($qb->expr()->lte(DoctrineDatabase::COLUMN_CREATED, ':created_to'));
34+
$qb->setParameter(':created_to', $criterion->to->getTimestamp());
35+
}
36+
}
37+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/**
4+
* @copyright Copyright (C) Ibexa AS. All rights reserved.
5+
* @license For full copyright and license information view LICENSE file distributed with this source code.
6+
*/
7+
declare(strict_types=1);
8+
9+
namespace Ibexa\Core\Persistence\Legacy\Notification\Gateway\CriterionHandler;
10+
11+
use Doctrine\DBAL\Connection;
12+
use Doctrine\DBAL\Query\QueryBuilder;
13+
use Ibexa\Contracts\Core\Repository\Values\Notification\CriterionHandlerInterface;
14+
use Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion;
15+
use Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion\Status;
16+
use Ibexa\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase;
17+
18+
final class StatusCriterionHandler implements CriterionHandlerInterface
19+
{
20+
public function supports(Criterion $criterion): bool
21+
{
22+
return $criterion instanceof Status;
23+
}
24+
25+
public function apply(QueryBuilder $qb, Criterion $criterion): void
26+
{
27+
/** @var \Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion\Status $criterion */
28+
$qb->andWhere($qb->expr()->eq(DoctrineDatabase::COLUMN_IS_PENDING, ':status'));
29+
$qb->setParameter(':status', $criterion->statuses, Connection::PARAM_STR_ARRAY);
30+
}
31+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/**
4+
* @copyright Copyright (C) Ibexa AS. All rights reserved.
5+
* @license For full copyright and license information view LICENSE file distributed with this source code.
6+
*/
7+
declare(strict_types=1);
8+
9+
namespace Ibexa\Core\Persistence\Legacy\Notification\Gateway\CriterionHandler;
10+
11+
use Doctrine\DBAL\Query\QueryBuilder;
12+
use Ibexa\Contracts\Core\Repository\Values\Notification\CriterionHandlerInterface;
13+
use Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion;
14+
use Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion\Type;
15+
use Ibexa\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase;
16+
17+
final class TypeCriterionHandler implements CriterionHandlerInterface
18+
{
19+
public function supports(Criterion $criterion): bool
20+
{
21+
return $criterion instanceof Type;
22+
}
23+
24+
public function apply(QueryBuilder $qb, Criterion $criterion): void
25+
{
26+
/** @var \Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion\Type $criterion */
27+
$qb->andWhere($qb->expr()->eq(DoctrineDatabase::COLUMN_TYPE, ':type'));
28+
$qb->setParameter(':type', $criterion->value);
29+
}
30+
}

src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php

Lines changed: 26 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,12 @@
99
namespace Ibexa\Core\Persistence\Legacy\Notification\Gateway;
1010

1111
use Doctrine\DBAL\Connection;
12+
use Doctrine\DBAL\ParameterType;
1213
use Doctrine\DBAL\Query\QueryBuilder;
1314
use Ibexa\Contracts\Core\Persistence\Notification\CreateStruct;
1415
use Ibexa\Contracts\Core\Persistence\Notification\Notification;
1516
use Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion;
16-
use Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion\DateCreated;
17-
use Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion\LogicalAnd;
1817
use Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion\NotificationQuery;
19-
use Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion\Status;
20-
use Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion\Type;
2118
use Ibexa\Core\Base\Exceptions\InvalidArgumentException;
2219
use Ibexa\Core\Persistence\Legacy\Notification\Gateway;
2320
use PDO;
@@ -34,9 +31,18 @@ class DoctrineDatabase extends Gateway
3431

3532
private Connection $connection;
3633

37-
public function __construct(Connection $connection)
34+
/**
35+
* @var \Ibexa\Contracts\Core\Repository\Values\Notification\CriterionHandlerInterface[]
36+
*/
37+
private array $criterionHandlers;
38+
39+
/**
40+
* @param iterable<\Ibexa\Contracts\Core\Repository\Values\Notification\CriterionHandlerInterface> $criterionHandlers
41+
*/
42+
public function __construct(Connection $connection, iterable $criterionHandlers)
3843
{
3944
$this->connection = $connection;
45+
$this->criterionHandlers = iterator_to_array($criterionHandlers);
4046
}
4147

4248
public function insert(CreateStruct $createStruct): int
@@ -100,10 +106,10 @@ public function countUserNotifications(int $userId, ?NotificationQuery $query =
100106
->select('COUNT(' . self::COLUMN_ID . ')')
101107
->from(self::TABLE_NOTIFICATION)
102108
->where($queryBuilder->expr()->eq(self::COLUMN_OWNER_ID, ':user_id'))
103-
->setParameter(':user_id', $userId, PDO::PARAM_INT);
109+
->setParameter('user_id', $userId, ParameterType::INTEGER);
104110

105-
if (($query !== null) && !empty($query->criteria)) {
106-
$this->applyFilters($queryBuilder, $query->criteria);
111+
if (($query !== null) && !empty($query->getCriteria())) {
112+
$this->applyFilters($queryBuilder, $query->getCriteria());
107113
}
108114

109115
return (int)$queryBuilder->execute()->fetchColumn();
@@ -124,9 +130,6 @@ public function countUserPendingNotifications(int $userId): int
124130
return (int)$query->execute()->fetchColumn();
125131
}
126132

127-
/**
128-
* @return array<int, array<string, mixed>>
129-
*/
130133
public function loadUserNotifications(int $userId, int $offset = 0, int $limit = -1): array
131134
{
132135
$query = $this->connection->createQueryBuilder();
@@ -146,9 +149,6 @@ public function loadUserNotifications(int $userId, int $offset = 0, int $limit =
146149
return $query->execute()->fetchAllAssociative();
147150
}
148151

149-
/**
150-
* @return array<int, array<string, mixed>>
151-
*/
152152
public function findUserNotifications(int $userId, ?NotificationQuery $query = null): array
153153
{
154154
$queryBuilder = $this->connection->createQueryBuilder();
@@ -160,16 +160,16 @@ public function findUserNotifications(int $userId, ?NotificationQuery $query = n
160160
->orderBy(self::COLUMN_ID, 'DESC');
161161

162162
if ($query !== null) {
163-
if (!empty($query->criteria)) {
164-
$this->applyFilters($queryBuilder, $query->criteria);
163+
if (!empty($query->getCriteria())) {
164+
$this->applyFilters($queryBuilder, $query->getCriteria());
165165
}
166166

167-
if ($query->offset > 0) {
168-
$queryBuilder->setFirstResult($query->offset);
167+
if ($query->getOffset() > 0) {
168+
$queryBuilder->setFirstResult($query->getOffset());
169169
}
170170

171-
if ($query->limit > 0) {
172-
$queryBuilder->setMaxResults($query->limit);
171+
if ($query->getLimit() > 0) {
172+
$queryBuilder->setMaxResults($query->getLimit());
173173
}
174174
}
175175

@@ -188,55 +188,15 @@ private function applyFilters(QueryBuilder $qb, array $criteria): void
188188

189189
private function applyCriterion(QueryBuilder $qb, Criterion $criterion): void
190190
{
191-
switch (true) {
192-
case $criterion instanceof Type:
193-
$qb->andWhere($qb->expr()->eq(self::COLUMN_TYPE, ':type'));
194-
$qb->setParameter(':type', $criterion->value);
195-
break;
196-
197-
case $criterion instanceof Status:
198-
$qb->andWhere($qb->expr()->in(self::COLUMN_IS_PENDING, ':status'));
199-
$qb->setParameter(':status', $criterion->statuses, Connection::PARAM_STR_ARRAY);
200-
break;
201-
202-
case $criterion instanceof DateCreated:
203-
if ($criterion->from !== null) {
204-
$qb->andWhere($qb->expr()->gte(self::COLUMN_CREATED, ':created_from'));
205-
$qb->setParameter(':created_from', $criterion->from->getTimestamp());
206-
}
207-
if ($criterion->to !== null) {
208-
$qb->andWhere($qb->expr()->lte(self::COLUMN_CREATED, ':created_to'));
209-
$qb->setParameter(':created_to', $criterion->to->getTimestamp());
210-
}
211-
break;
212-
213-
case $criterion instanceof LogicalAnd:
214-
$this->applyLogicalOperator($qb, $criterion->criteria, 'and');
215-
break;
216-
217-
default:
218-
throw new InvalidArgumentException(get_class($criterion), 'Unknown criterion');
219-
}
220-
}
191+
foreach ($this->criterionHandlers as $handler) {
192+
if ($handler->supports($criterion)) {
193+
$handler->apply($qb, $criterion);
221194

222-
/**
223-
* @param \Ibexa\Contracts\Core\Repository\Values\Notification\Query\Criterion[] $criteria
224-
*/
225-
private function applyLogicalOperator(QueryBuilder $qb, array $criteria, string $type): void
226-
{
227-
$expr = $qb->expr();
228-
$parts = [];
229-
230-
foreach ($criteria as $index => $criterion) {
231-
$subQb = $this->connection->createQueryBuilder();
232-
$this->applyCriterion($subQb, $criterion);
233-
$parts[] = '(' . $subQb->getSQL() . ')';
195+
return;
196+
}
234197
}
235198

236-
if (!empty($parts)) {
237-
$logicalExpr = $type === 'and' ? $expr->and(...$parts) : $expr->or(...$parts);
238-
$qb->andWhere($logicalExpr);
239-
}
199+
throw new InvalidArgumentException(get_class($criterion), 'No handler found for criterion of type %s');
240200
}
241201

242202
public function delete(int $notificationId): void

src/lib/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,6 @@ public function countUserPendingNotifications(int $userId): int
7171
}
7272
}
7373

74-
/**
75-
* @return array<int, array<string, mixed>>
76-
*/
7774
public function loadUserNotifications(int $userId, int $offset = 0, int $limit = -1): array
7875
{
7976
try {
@@ -83,9 +80,6 @@ public function loadUserNotifications(int $userId, int $offset = 0, int $limit =
8380
}
8481
}
8582

86-
/**
87-
* @return array<int, array<string, mixed>>
88-
*/
8983
public function findUserNotifications(int $userId, ?NotificationQuery $query = null): array
9084
{
9185
try {

src/lib/Resources/settings/storage_engines/legacy/notification.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ services:
22
Ibexa\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase:
33
arguments:
44
$connection: '@ibexa.persistence.connection'
5+
$criterionHandlers: !tagged_iterator 'ibexa.notification.criterion_handler'
56

67
Ibexa\Core\Persistence\Legacy\Notification\Gateway\ExceptionConversion:
78
arguments:
@@ -17,3 +18,7 @@ services:
1718

1819
ibexa.spi.persistence.legacy.notification.handler:
1920
alias: 'Ibexa\Core\Persistence\Legacy\Notification\Handler'
21+
22+
Ibexa\Core\Persistence\Legacy\Notification\Gateway\CriterionHandler\:
23+
resource: '../../../../../../src/lib/Persistence/Legacy/Notification/Gateway/CriterionHandler/*'
24+
tags: [ ibexa.notification.criterion_handler ]

0 commit comments

Comments
 (0)