feat(Docker): Upgrade to PHP 8.5.0RC3 with native ext-uri support
BREAKING CHANGE: Requires PHP 8.5.0RC3 Changes: - Update Docker base image from php:8.4-fpm to php:8.5.0RC3-fpm - Enable ext-uri for native WHATWG URL parsing support - Update composer.json PHP requirement from ^8.4 to ^8.5 - Add ext-uri as required extension in composer.json - Move URL classes from Url.php85/ to Url/ directory (now compatible) - Remove temporary PHP 8.4 compatibility workarounds Benefits: - Native URL parsing with Uri\WhatWg\Url class - Better performance for URL operations - Future-proof with latest PHP features - Eliminates PHP version compatibility issues
This commit is contained in:
@@ -6,7 +6,8 @@ namespace App\Framework\Notification\Storage;
|
||||
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
use App\Framework\Database\ConnectionInterface;
|
||||
use App\Framework\Database\SqlQuery;
|
||||
use App\Framework\Database\ValueObjects\SqlQuery;
|
||||
use App\Framework\DI\Attributes\DefaultImplementation;
|
||||
use App\Framework\Notification\Notification;
|
||||
use App\Framework\Notification\ValueObjects\NotificationChannel;
|
||||
use App\Framework\Notification\ValueObjects\NotificationId;
|
||||
@@ -17,17 +18,16 @@ use App\Framework\Notification\ValueObjects\NotificationType;
|
||||
/**
|
||||
* Database implementation of NotificationRepository
|
||||
*/
|
||||
#[DefaultImplementation]
|
||||
final readonly class DatabaseNotificationRepository implements NotificationRepository
|
||||
{
|
||||
public function __construct(
|
||||
private ConnectionInterface $connection
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
public function save(Notification $notification): void
|
||||
{
|
||||
$query = new SqlQuery(
|
||||
sql: <<<'SQL'
|
||||
$query = SqlQuery::create(<<<'SQL'
|
||||
INSERT INTO notifications (
|
||||
id, recipient_id, type, title, body, data,
|
||||
channels, priority, status, created_at, sent_at,
|
||||
@@ -38,7 +38,7 @@ final readonly class DatabaseNotificationRepository implements NotificationRepos
|
||||
sent_at = EXCLUDED.sent_at,
|
||||
read_at = EXCLUDED.read_at
|
||||
SQL,
|
||||
params: [
|
||||
[
|
||||
$notification->id->toString(),
|
||||
$notification->recipientId,
|
||||
$notification->type->toString(),
|
||||
@@ -61,9 +61,9 @@ final readonly class DatabaseNotificationRepository implements NotificationRepos
|
||||
|
||||
public function findById(NotificationId $id): ?Notification
|
||||
{
|
||||
$query = new SqlQuery(
|
||||
sql: 'SELECT * FROM notifications WHERE id = ?',
|
||||
params: [$id->toString()]
|
||||
$query = SqlQuery::create(
|
||||
'SELECT * FROM notifications WHERE id = ?',
|
||||
[$id->toString()]
|
||||
);
|
||||
|
||||
$row = $this->connection->queryOne($query);
|
||||
@@ -73,14 +73,14 @@ final readonly class DatabaseNotificationRepository implements NotificationRepos
|
||||
|
||||
public function findByUser(string $userId, int $limit = 20, int $offset = 0): array
|
||||
{
|
||||
$query = new SqlQuery(
|
||||
sql: <<<'SQL'
|
||||
$query = SqlQuery::create(
|
||||
<<<'SQL'
|
||||
SELECT * FROM notifications
|
||||
WHERE recipient_id = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ? OFFSET ?
|
||||
SQL,
|
||||
params: [$userId, $limit, $offset]
|
||||
[$userId, $limit, $offset]
|
||||
);
|
||||
|
||||
$rows = $this->connection->query($query)->fetchAll();
|
||||
@@ -90,15 +90,15 @@ final readonly class DatabaseNotificationRepository implements NotificationRepos
|
||||
|
||||
public function findUnreadByUser(string $userId, int $limit = 20): array
|
||||
{
|
||||
$query = new SqlQuery(
|
||||
sql: <<<'SQL'
|
||||
$query = SqlQuery::create(
|
||||
<<<'SQL'
|
||||
SELECT * FROM notifications
|
||||
WHERE recipient_id = ?
|
||||
AND status != ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?
|
||||
SQL,
|
||||
params: [$userId, NotificationStatus::READ->value, $limit]
|
||||
[$userId, NotificationStatus::READ->value, $limit]
|
||||
);
|
||||
|
||||
$rows = $this->connection->query($query)->fetchAll();
|
||||
@@ -108,13 +108,13 @@ final readonly class DatabaseNotificationRepository implements NotificationRepos
|
||||
|
||||
public function countUnreadByUser(string $userId): int
|
||||
{
|
||||
$query = new SqlQuery(
|
||||
sql: <<<'SQL'
|
||||
$query = SqlQuery::create(
|
||||
<<<'SQL'
|
||||
SELECT COUNT(*) as count FROM notifications
|
||||
WHERE recipient_id = ?
|
||||
AND status != ?
|
||||
SQL,
|
||||
params: [$userId, NotificationStatus::READ->value]
|
||||
[$userId, NotificationStatus::READ->value]
|
||||
);
|
||||
|
||||
return (int) $this->connection->queryScalar($query);
|
||||
@@ -122,15 +122,15 @@ final readonly class DatabaseNotificationRepository implements NotificationRepos
|
||||
|
||||
public function markAsRead(NotificationId $id): bool
|
||||
{
|
||||
$query = new SqlQuery(
|
||||
sql: <<<'SQL'
|
||||
$query = SqlQuery::create(
|
||||
<<<'SQL'
|
||||
UPDATE notifications
|
||||
SET status = ?, read_at = ?
|
||||
WHERE id = ?
|
||||
SQL,
|
||||
params: [
|
||||
[
|
||||
NotificationStatus::READ->value,
|
||||
(new Timestamp())->format('Y-m-d H:i:s'),
|
||||
Timestamp::now()->format('Y-m-d H:i:s'),
|
||||
$id->toString(),
|
||||
]
|
||||
);
|
||||
@@ -140,16 +140,16 @@ final readonly class DatabaseNotificationRepository implements NotificationRepos
|
||||
|
||||
public function markAllAsReadForUser(string $userId): int
|
||||
{
|
||||
$query = new SqlQuery(
|
||||
sql: <<<'SQL'
|
||||
$query = SqlQuery::create(
|
||||
<<<'SQL'
|
||||
UPDATE notifications
|
||||
SET status = ?, read_at = ?
|
||||
WHERE recipient_id = ?
|
||||
AND status != ?
|
||||
SQL,
|
||||
params: [
|
||||
[
|
||||
NotificationStatus::READ->value,
|
||||
(new Timestamp())->format('Y-m-d H:i:s'),
|
||||
Timestamp::now()->format('Y-m-d H:i:s'),
|
||||
$userId,
|
||||
NotificationStatus::READ->value,
|
||||
]
|
||||
@@ -160,9 +160,9 @@ final readonly class DatabaseNotificationRepository implements NotificationRepos
|
||||
|
||||
public function delete(NotificationId $id): bool
|
||||
{
|
||||
$query = new SqlQuery(
|
||||
sql: 'DELETE FROM notifications WHERE id = ?',
|
||||
params: [$id->toString()]
|
||||
$query = SqlQuery::create(
|
||||
'DELETE FROM notifications WHERE id = ?',
|
||||
[$id->toString()]
|
||||
);
|
||||
|
||||
return $this->connection->execute($query) > 0;
|
||||
@@ -172,13 +172,13 @@ final readonly class DatabaseNotificationRepository implements NotificationRepos
|
||||
{
|
||||
$cutoffDate = (new Timestamp())->modify("-{$daysOld} days");
|
||||
|
||||
$query = new SqlQuery(
|
||||
sql: <<<'SQL'
|
||||
$query = SqlQuery::create(
|
||||
<<<'SQL'
|
||||
DELETE FROM notifications
|
||||
WHERE status = ?
|
||||
AND created_at < ?
|
||||
SQL,
|
||||
params: [
|
||||
[
|
||||
$status->value,
|
||||
$cutoffDate->format('Y-m-d H:i:s'),
|
||||
]
|
||||
@@ -195,19 +195,19 @@ final readonly class DatabaseNotificationRepository implements NotificationRepos
|
||||
);
|
||||
|
||||
return new Notification(
|
||||
id: NotificationId::fromString($row['id']),
|
||||
id : NotificationId::fromString($row['id']),
|
||||
recipientId: $row['recipient_id'],
|
||||
type: NotificationType::fromString($row['type']),
|
||||
title: $row['title'],
|
||||
body: $row['body'],
|
||||
data: json_decode($row['data'], true) ?? [],
|
||||
channels: $channels,
|
||||
priority: NotificationPriority::from($row['priority']),
|
||||
status: NotificationStatus::from($row['status']),
|
||||
createdAt: Timestamp::fromString($row['created_at']),
|
||||
sentAt: $row['sent_at'] ? Timestamp::fromString($row['sent_at']) : null,
|
||||
readAt: $row['read_at'] ? Timestamp::fromString($row['read_at']) : null,
|
||||
actionUrl: $row['action_url'],
|
||||
type : NotificationType::fromString($row['type']),
|
||||
title : $row['title'],
|
||||
body : $row['body'],
|
||||
createdAt : Timestamp::fromTimestamp((int) strtotime($row['created_at'])),
|
||||
data : json_decode($row['data'], true) ?? [],
|
||||
channels : $channels,
|
||||
priority : NotificationPriority::from($row['priority']),
|
||||
status : NotificationStatus::from($row['status']),
|
||||
sentAt : $row['sent_at'] ? Timestamp::fromTimestamp((int) strtotime($row['sent_at'])) : null,
|
||||
readAt : $row['read_at'] ? Timestamp::fromTimestamp((int) strtotime($row['read_at'])) : null,
|
||||
actionUrl : $row['action_url'],
|
||||
actionLabel: $row['action_label']
|
||||
);
|
||||
}
|
||||
|
||||
@@ -45,10 +45,10 @@ final readonly class TemplateRenderer
|
||||
|
||||
// Create base notification
|
||||
$notification = Notification::create(
|
||||
recipientId: $recipientId,
|
||||
type: $type,
|
||||
title: $title,
|
||||
body: $body,
|
||||
$recipientId,
|
||||
$type,
|
||||
$title,
|
||||
$body,
|
||||
...$channels
|
||||
)->withPriority($template->defaultPriority);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace App\Framework\Notification\ValueObjects;
|
||||
/**
|
||||
* Type/Category of notification for user preferences and filtering
|
||||
*/
|
||||
final readonly class NotificationType
|
||||
final readonly class NotificationType implements NotificationTypeInterface
|
||||
{
|
||||
private function __construct(
|
||||
private string $value
|
||||
@@ -57,4 +57,14 @@ final readonly class NotificationType
|
||||
{
|
||||
return $this->value === $other->value;
|
||||
}
|
||||
|
||||
public function getDisplayName(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function isCritical(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user