refactor(notifications): remove factory indirection, interface, and listener duplication
Remove NotificationRepositoryFactory, NotificationServicesFactory, and NotificationRepositoryInterface — all single-consumer abstractions with no polymorphism benefit. Simplify container registrar to wire services directly. Extract shared listener logic (sanitizeTenantIds, resolveDisplayName, resolveUuid) into NotificationListenerTrait, eliminating duplication across 4 listeners. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Module\Notifications\Listeners;
|
||||
|
||||
trait NotificationListenerTrait
|
||||
{
|
||||
/**
|
||||
* Resolve display name from payload with optional fallback to user record.
|
||||
*
|
||||
* Covers three variants:
|
||||
* - payload['display_name'] is present → use it
|
||||
* - fallback to user record (display_name, then first+last)
|
||||
* - last resort: '#' + user ID
|
||||
*/
|
||||
protected function resolveDisplayName(array $payload, ?array $user, int $userId): string
|
||||
{
|
||||
$displayName = trim((string) ($payload['display_name'] ?? ''));
|
||||
if ($displayName === '' && is_array($user)) {
|
||||
$displayName = trim((string) ($user['display_name'] ?? ''));
|
||||
if ($displayName === '') {
|
||||
$displayName = trim((string) (($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '')));
|
||||
}
|
||||
}
|
||||
if ($displayName === '') {
|
||||
$displayName = '#' . $userId;
|
||||
}
|
||||
|
||||
return $displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve UUID from payload with optional fallback to user record.
|
||||
*/
|
||||
protected function resolveUuid(array $payload, ?array $user): string
|
||||
{
|
||||
$uuid = trim((string) ($payload['uuid'] ?? ''));
|
||||
if ($uuid === '' && is_array($user)) {
|
||||
$uuid = trim((string) ($user['uuid'] ?? ''));
|
||||
}
|
||||
|
||||
return $uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<int>
|
||||
*/
|
||||
protected function sanitizeTenantIds(mixed $tenantIds): array
|
||||
{
|
||||
if (!is_array($tenantIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ids = array_values(array_unique(array_map('intval', $tenantIds)));
|
||||
return array_values(array_filter($ids, static fn (int $tenantId): bool => $tenantId > 0));
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ use MintyPHP\Service\Access\PermissionService;
|
||||
|
||||
final class UserActivatedNotificationListener implements EventListener
|
||||
{
|
||||
use NotificationListenerTrait;
|
||||
|
||||
public function __construct(
|
||||
private readonly NotificationService $notificationService,
|
||||
private readonly UserReadRepositoryInterface $userReadRepository,
|
||||
@@ -26,21 +28,8 @@ final class UserActivatedNotificationListener implements EventListener
|
||||
}
|
||||
|
||||
$user = $this->userReadRepository->find($userId);
|
||||
$displayName = trim((string) ($payload['display_name'] ?? ''));
|
||||
if ($displayName === '' && is_array($user)) {
|
||||
$displayName = trim((string) ($user['display_name'] ?? ''));
|
||||
if ($displayName === '') {
|
||||
$displayName = trim((string) (($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '')));
|
||||
}
|
||||
}
|
||||
if ($displayName === '') {
|
||||
$displayName = '#' . $userId;
|
||||
}
|
||||
|
||||
$uuid = trim((string) ($payload['uuid'] ?? ''));
|
||||
if ($uuid === '' && is_array($user)) {
|
||||
$uuid = trim((string) ($user['uuid'] ?? ''));
|
||||
}
|
||||
$displayName = $this->resolveDisplayName($payload, $user, $userId);
|
||||
$uuid = $this->resolveUuid($payload, $user);
|
||||
|
||||
$tenantIds = $this->sanitizeTenantIds($payload['tenant_ids'] ?? []);
|
||||
if ($tenantIds === []) {
|
||||
@@ -79,17 +68,4 @@ final class UserActivatedNotificationListener implements EventListener
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<int>
|
||||
*/
|
||||
private function sanitizeTenantIds(mixed $tenantIds): array
|
||||
{
|
||||
if (!is_array($tenantIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ids = array_values(array_unique(array_map('intval', $tenantIds)));
|
||||
return array_values(array_filter($ids, static fn (int $tenantId): bool => $tenantId > 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ use MintyPHP\Service\Access\PermissionService;
|
||||
|
||||
final class UserAssignmentChangedNotificationListener implements EventListener
|
||||
{
|
||||
use NotificationListenerTrait;
|
||||
|
||||
public function __construct(
|
||||
private readonly NotificationService $notificationService,
|
||||
private readonly UserReadRepositoryInterface $userReadRepository
|
||||
@@ -25,21 +27,8 @@ final class UserAssignmentChangedNotificationListener implements EventListener
|
||||
|
||||
$actorUserId = isset($payload['actor_user_id']) ? (int) $payload['actor_user_id'] : null;
|
||||
$user = $this->userReadRepository->find($userId);
|
||||
$displayName = trim((string) ($payload['display_name'] ?? ''));
|
||||
if ($displayName === '' && is_array($user)) {
|
||||
$displayName = trim((string) ($user['display_name'] ?? ''));
|
||||
if ($displayName === '') {
|
||||
$displayName = trim((string) (($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '')));
|
||||
}
|
||||
}
|
||||
if ($displayName === '') {
|
||||
$displayName = '#' . $userId;
|
||||
}
|
||||
|
||||
$uuid = trim((string) ($payload['uuid'] ?? ''));
|
||||
if ($uuid === '' && is_array($user)) {
|
||||
$uuid = trim((string) ($user['uuid'] ?? ''));
|
||||
}
|
||||
$displayName = $this->resolveDisplayName($payload, $user, $userId);
|
||||
$uuid = $this->resolveUuid($payload, $user);
|
||||
|
||||
$tenantIds = $this->sanitizeTenantIds($payload['tenant_ids'] ?? []);
|
||||
$changes = is_array($payload['changes'] ?? null) ? $payload['changes'] : [];
|
||||
@@ -87,17 +76,4 @@ final class UserAssignmentChangedNotificationListener implements EventListener
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<int>
|
||||
*/
|
||||
private function sanitizeTenantIds(mixed $tenantIds): array
|
||||
{
|
||||
if (!is_array($tenantIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ids = array_values(array_unique(array_map('intval', $tenantIds)));
|
||||
return array_values(array_filter($ids, static fn (int $tenantId): bool => $tenantId > 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ use MintyPHP\Service\Access\PermissionService;
|
||||
|
||||
final class UserCreatedNotificationListener implements EventListener
|
||||
{
|
||||
use NotificationListenerTrait;
|
||||
|
||||
public function __construct(
|
||||
private readonly NotificationService $notificationService,
|
||||
private readonly UserReadRepositoryInterface $userReadRepository,
|
||||
@@ -33,17 +35,13 @@ final class UserCreatedNotificationListener implements EventListener
|
||||
return;
|
||||
}
|
||||
|
||||
$displayName = trim((string) ($user['display_name'] ?? ''));
|
||||
if ($displayName === '') {
|
||||
$displayName = trim(($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? ''));
|
||||
}
|
||||
$displayName = $this->resolveDisplayName($payload, $user, $userId);
|
||||
|
||||
$tenantIds = $this->userTenantRepository->listTenantIdsByUserId($userId);
|
||||
if ($tenantIds === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only notify users who have the users.create permission (admins)
|
||||
$adminUserIds = $this->userReadRepository->listPrivilegedUserIdsByPermissionKeys(
|
||||
[PermissionService::USERS_CREATE]
|
||||
);
|
||||
|
||||
@@ -11,6 +11,8 @@ use MintyPHP\Service\Access\PermissionService;
|
||||
|
||||
final class UserDeactivatedNotificationListener implements EventListener
|
||||
{
|
||||
use NotificationListenerTrait;
|
||||
|
||||
public function __construct(
|
||||
private readonly NotificationService $notificationService,
|
||||
private readonly UserReadRepositoryInterface $userReadRepository,
|
||||
@@ -26,21 +28,8 @@ final class UserDeactivatedNotificationListener implements EventListener
|
||||
}
|
||||
|
||||
$user = $this->userReadRepository->find($userId);
|
||||
$displayName = trim((string) ($payload['display_name'] ?? ''));
|
||||
if ($displayName === '' && is_array($user)) {
|
||||
$displayName = trim((string) ($user['display_name'] ?? ''));
|
||||
if ($displayName === '') {
|
||||
$displayName = trim((string) (($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '')));
|
||||
}
|
||||
}
|
||||
if ($displayName === '') {
|
||||
$displayName = '#' . $userId;
|
||||
}
|
||||
|
||||
$uuid = trim((string) ($payload['uuid'] ?? ''));
|
||||
if ($uuid === '' && is_array($user)) {
|
||||
$uuid = trim((string) ($user['uuid'] ?? ''));
|
||||
}
|
||||
$displayName = $this->resolveDisplayName($payload, $user, $userId);
|
||||
$uuid = $this->resolveUuid($payload, $user);
|
||||
|
||||
$tenantIds = $this->sanitizeTenantIds($payload['tenant_ids'] ?? []);
|
||||
if ($tenantIds === []) {
|
||||
@@ -79,17 +68,4 @@ final class UserDeactivatedNotificationListener implements EventListener
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<int>
|
||||
*/
|
||||
private function sanitizeTenantIds(mixed $tenantIds): array
|
||||
{
|
||||
if (!is_array($tenantIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$ids = array_values(array_unique(array_map('intval', $tenantIds)));
|
||||
return array_values(array_filter($ids, static fn (int $tenantId): bool => $tenantId > 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ use MintyPHP\Service\Access\PermissionService;
|
||||
|
||||
final class UserDeletedNotificationListener implements EventListener
|
||||
{
|
||||
use NotificationListenerTrait;
|
||||
|
||||
public function __construct(
|
||||
private readonly NotificationService $notificationService,
|
||||
private readonly UserReadRepositoryInterface $userReadRepository
|
||||
|
||||
@@ -10,9 +10,8 @@ use MintyPHP\Module\Notifications\Listeners\UserAssignmentChangedNotificationLis
|
||||
use MintyPHP\Module\Notifications\Listeners\UserCreatedNotificationListener;
|
||||
use MintyPHP\Module\Notifications\Listeners\UserDeactivatedNotificationListener;
|
||||
use MintyPHP\Module\Notifications\Listeners\UserDeletedNotificationListener;
|
||||
use MintyPHP\Module\Notifications\Service\NotificationRepositoryFactory;
|
||||
use MintyPHP\Module\Notifications\Repository\NotificationRepository;
|
||||
use MintyPHP\Module\Notifications\Service\NotificationService;
|
||||
use MintyPHP\Module\Notifications\Service\NotificationServicesFactory;
|
||||
use MintyPHP\Repository\Tenant\UserTenantRepository;
|
||||
use MintyPHP\Repository\User\UserReadRepository;
|
||||
use MintyPHP\Service\Access\PermissionService;
|
||||
@@ -24,13 +23,11 @@ final class NotificationsContainerRegistrar implements ContainerRegistrar
|
||||
$container->set(NotificationsAuthorizationPolicy::class, static fn (AppContainer $c): NotificationsAuthorizationPolicy => new NotificationsAuthorizationPolicy(
|
||||
$c->get(PermissionService::class)
|
||||
));
|
||||
$container->set(NotificationRepositoryFactory::class, static fn (): NotificationRepositoryFactory => new NotificationRepositoryFactory());
|
||||
|
||||
$container->set(NotificationServicesFactory::class, static fn (AppContainer $c): NotificationServicesFactory => new NotificationServicesFactory(
|
||||
$c->get(NotificationRepositoryFactory::class)
|
||||
));
|
||||
$container->set(NotificationRepository::class, static fn (): NotificationRepository => new NotificationRepository());
|
||||
|
||||
$container->set(NotificationService::class, static fn (AppContainer $c): NotificationService => $c->get(NotificationServicesFactory::class)->createNotificationService(
|
||||
$container->set(NotificationService::class, static fn (AppContainer $c): NotificationService => new NotificationService(
|
||||
$c->get(NotificationRepository::class),
|
||||
$c->get(UserTenantRepository::class)
|
||||
));
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace MintyPHP\Module\Notifications\Repository;
|
||||
use MintyPHP\DB;
|
||||
use MintyPHP\Repository\Support\RepositoryArrayHelper;
|
||||
|
||||
class NotificationRepository implements NotificationRepositoryInterface
|
||||
class NotificationRepository
|
||||
{
|
||||
private const DEDUPE_WINDOW_SECONDS = 1800;
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Module\Notifications\Repository;
|
||||
|
||||
interface NotificationRepositoryInterface
|
||||
{
|
||||
/** @return int|false Insert ID on success, false on failure */
|
||||
public function create(array $data): int|false;
|
||||
|
||||
/**
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
public function listByUser(int $userId, ?int $tenantId, int $limit, int $offset): array;
|
||||
|
||||
public function countUnreadByUser(int $userId, ?int $tenantId): int;
|
||||
|
||||
public function markRead(int $id, int $userId, ?int $tenantId): bool;
|
||||
|
||||
public function markAllReadByUser(int $userId, ?int $tenantId): int;
|
||||
|
||||
public function delete(int $id, int $userId, ?int $tenantId): bool;
|
||||
|
||||
public function deleteAllByUser(int $userId): int;
|
||||
|
||||
public function purgeReadOlderThanDays(int $days): int;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Module\Notifications\Service;
|
||||
|
||||
use MintyPHP\Module\Notifications\Repository\NotificationRepository;
|
||||
use MintyPHP\Module\Notifications\Repository\NotificationRepositoryInterface;
|
||||
|
||||
class NotificationRepositoryFactory
|
||||
{
|
||||
private ?NotificationRepository $notificationRepository = null;
|
||||
|
||||
public function createNotificationRepository(): NotificationRepositoryInterface
|
||||
{
|
||||
return $this->notificationRepository ??= new NotificationRepository();
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace MintyPHP\Module\Notifications\Service;
|
||||
|
||||
use MintyPHP\Module\Notifications\Repository\NotificationRepositoryInterface;
|
||||
use MintyPHP\Module\Notifications\Repository\NotificationRepository;
|
||||
use MintyPHP\Repository\Tenant\UserTenantRepositoryInterface;
|
||||
|
||||
class NotificationService
|
||||
@@ -18,7 +18,7 @@ class NotificationService
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private readonly NotificationRepositoryInterface $notificationRepository,
|
||||
private readonly NotificationRepository $notificationRepository,
|
||||
private readonly UserTenantRepositoryInterface $userTenantRepository
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Module\Notifications\Service;
|
||||
|
||||
use MintyPHP\Repository\Tenant\UserTenantRepositoryInterface;
|
||||
|
||||
class NotificationServicesFactory
|
||||
{
|
||||
private ?NotificationService $notificationService = null;
|
||||
|
||||
public function __construct(
|
||||
private readonly NotificationRepositoryFactory $notificationRepositoryFactory
|
||||
) {
|
||||
}
|
||||
|
||||
public function createNotificationService(UserTenantRepositoryInterface $userTenantRepository): NotificationService
|
||||
{
|
||||
return $this->notificationService ??= new NotificationService(
|
||||
$this->notificationRepositoryFactory->createNotificationRepository(),
|
||||
$userTenantRepository
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user