added unread and read all function with endpoints for notifications

This commit is contained in:
2024-07-05 13:36:31 +00:00
committed by Julien Fastré
parent 2b09e1459c
commit 2d67843901
14 changed files with 805 additions and 377 deletions

View File

@@ -13,6 +13,8 @@ namespace Chill\MainBundle\Repository;
use Chill\MainBundle\Entity\Notification;
use Chill\MainBundle\Entity\User;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Result;
use Doctrine\DBAL\Statement;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityManagerInterface;
@@ -81,10 +83,7 @@ final class NotificationRepository implements ObjectRepository
$results->free();
} else {
$wheres = [];
foreach ([
['relatedEntityClass' => $relatedEntityClass, 'relatedEntityId' => $relatedEntityId],
...$more,
] as $k => ['relatedEntityClass' => $relClass, 'relatedEntityId' => $relId]) {
foreach ([['relatedEntityClass' => $relatedEntityClass, 'relatedEntityId' => $relatedEntityId], ...$more] as $k => ['relatedEntityClass' => $relClass, 'relatedEntityId' => $relId]) {
$wheres[] = "(relatedEntityClass = :relatedEntityClass_{$k} AND relatedEntityId = :relatedEntityId_{$k})";
$sqlParams["relatedEntityClass_{$k}"] = $relClass;
$sqlParams["relatedEntityId_{$k}"] = $relId;
@@ -228,11 +227,11 @@ final class NotificationRepository implements ObjectRepository
$rsm->addRootEntityFromClassMetadata(Notification::class, 'cmn');
$sql = 'SELECT '.$rsm->generateSelectClause(['cmn' => 'cmn']).' '.
'FROM chill_main_notification cmn '.
'WHERE '.
'EXISTS (select 1 FROM chill_main_notification_addresses_unread cmnau WHERE cmnau.user_id = :userId and cmnau.notification_id = cmn.id) '.
'ORDER BY cmn.date DESC '.
'LIMIT :limit OFFSET :offset';
'FROM chill_main_notification cmn '.
'WHERE '.
'EXISTS (select 1 FROM chill_main_notification_addresses_unread cmnau WHERE cmnau.user_id = :userId and cmnau.notification_id = cmn.id) '.
'ORDER BY cmn.date DESC '.
'LIMIT :limit OFFSET :offset';
$nq = $this->em->createNativeQuery($sql, $rsm)
->setParameter('userId', $user->getId())
@@ -255,10 +254,12 @@ final class NotificationRepository implements ObjectRepository
$qb = $this->repository->createQueryBuilder('n');
// add condition for related entity (in main arguments, and in more)
$or = $qb->expr()->orX($qb->expr()->andX(
$qb->expr()->eq('n.relatedEntityClass', ':relatedEntityClass'),
$qb->expr()->eq('n.relatedEntityId', ':relatedEntityId')
));
$or = $qb->expr()->orX(
$qb->expr()->andX(
$qb->expr()->eq('n.relatedEntityClass', ':relatedEntityClass'),
$qb->expr()->eq('n.relatedEntityId', ':relatedEntityId')
)
);
$qb
->setParameter('relatedEntityClass', $relatedEntityClass)
->setParameter('relatedEntityId', $relatedEntityId);
@@ -310,4 +311,86 @@ final class NotificationRepository implements ObjectRepository
return $qb;
}
/**
* @return list<int> the ids of the notifications marked as unread
*/
public function markAllNotificationAsReadForUser(User $user): array
{
// Get the database connection from the entity manager
$connection = $this->em->getConnection();
/** @var Result $results */
$results = $connection->transactional(function (Connection $connection) use ($user) {
// Define the SQL query
$sql = <<<'SQL'
DELETE FROM chill_main_notification_addresses_unread
WHERE user_id = :user_id
RETURNING notification_id
SQL;
return $connection->executeQuery($sql, ['user_id' => $user->getId()]);
});
$notificationIdsTouched = [];
foreach ($results->iterateAssociative() as $row) {
$notificationIdsTouched[] = $row['notification_id'];
}
return array_values($notificationIdsTouched);
}
/**
* @param list<int> $notificationIds
*/
public function markAllNotificationAsUnreadForUser(User $user, array $notificationIds): array
{
// Get the database connection from the entity manager
$connection = $this->em->getConnection();
/** @var Result $results */
$results = $connection->transactional(function (Connection $connection) use ($user, $notificationIds) {
// This query double-check that the user is one of the addresses of the notification or the sender,
// if the notification is already marked as unread, this query does not fails.
// this query return the list of notification id which are affected
$sql = <<<'SQL'
INSERT INTO chill_main_notification_addresses_unread (user_id, notification_id)
SELECT ?, chill_main_notification_addresses_user.notification_id
FROM chill_main_notification_addresses_user JOIN chill_main_notification ON chill_main_notification_addresses_user.notification_id = chill_main_notification.id
WHERE (chill_main_notification_addresses_user.user_id = ? OR chill_main_notification.sender_id = ?)
AND chill_main_notification_addresses_user.notification_id IN ({ notification_ids })
ON CONFLICT (user_id, notification_id) DO NOTHING
RETURNING notification_id
SQL;
$params = [$user->getId(), $user->getId(), $user->getId(), ...array_values($notificationIds)];
$sql = strtr($sql, ['{ notification_ids }' => implode(', ', array_fill(0, count($notificationIds), '?'))]);
return $connection->executeQuery($sql, $params);
});
$notificationIdsTouched = [];
foreach ($results->iterateAssociative() as $row) {
$notificationIdsTouched[] = $row['notification_id'];
}
return array_values($notificationIdsTouched);
}
public function findAllUnreadByUser(User $user): array
{
$rsm = new Query\ResultSetMappingBuilder($this->em);
$rsm->addRootEntityFromClassMetadata(Notification::class, 'cmn');
$sql = 'SELECT '.$rsm->generateSelectClause(['cmn' => 'cmn']).' '.
'FROM chill_main_notification cmn '.
'WHERE '.
'EXISTS (SELECT 1 FROM chill_main_notification_addresses_unread cmnau WHERE cmnau.user_id = :userId AND cmnau.notification_id = cmn.id) '.
'ORDER BY cmn.date DESC';
$nq = $this->em->createNativeQuery($sql, $rsm)
->setParameter('userId', $user->getId());
return $nq->getResult();
}
}