feat: CI/CD pipeline setup complete - Ansible playbooks updated, secrets configured, workflow ready
This commit is contained in:
60
src/Domain/SmartLink/DI/SmartLinkServiceInitializer.php
Normal file
60
src/Domain/SmartLink/DI/SmartLinkServiceInitializer.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\SmartLink\DI;
|
||||
|
||||
use App\Domain\SmartLink\Repositories\ClickEventRepository;
|
||||
use App\Domain\SmartLink\Repositories\SmartLinkRepository;
|
||||
use App\Domain\SmartLink\Services\ClickStatisticsService;
|
||||
use App\Domain\SmartLink\Services\ClickTrackingService;
|
||||
use App\Domain\SmartLink\Services\SmartLinkService;
|
||||
use App\Framework\Database\ConnectionInterface;
|
||||
use App\Framework\DI\Container;
|
||||
use App\Framework\DI\Initializer;
|
||||
use App\Infrastructure\SmartLink\Repositories\DatabaseClickEventRepository;
|
||||
|
||||
/**
|
||||
* SmartLink Domain Service Registration
|
||||
*
|
||||
* Registers all SmartLink domain services in the DI container.
|
||||
*/
|
||||
final readonly class SmartLinkServiceInitializer
|
||||
{
|
||||
#[Initializer]
|
||||
public function __invoke(Container $container): void
|
||||
{
|
||||
// Repository Bindings
|
||||
$container->singleton(
|
||||
ClickEventRepository::class,
|
||||
fn(Container $c) => new DatabaseClickEventRepository(
|
||||
$c->get(ConnectionInterface::class)
|
||||
)
|
||||
);
|
||||
|
||||
// SmartLink Management Service
|
||||
$container->singleton(
|
||||
SmartLinkService::class,
|
||||
fn(Container $c) => new SmartLinkService(
|
||||
$c->get(SmartLinkRepository::class)
|
||||
)
|
||||
);
|
||||
|
||||
// Click Tracking Service
|
||||
$container->singleton(
|
||||
ClickTrackingService::class,
|
||||
fn(Container $c) => new ClickTrackingService(
|
||||
$c->get(ClickEventRepository::class),
|
||||
$c->get(SmartLinkRepository::class)
|
||||
)
|
||||
);
|
||||
|
||||
// Click Statistics Service (NEW - for Analytics Dashboard)
|
||||
$container->singleton(
|
||||
ClickStatisticsService::class,
|
||||
fn(Container $c) => new ClickStatisticsService(
|
||||
$c->get(ClickEventRepository::class)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace App\Domain\SmartLink\Exceptions;
|
||||
|
||||
use App\Domain\SmartLink\ValueObjects\ShortCode;
|
||||
use App\Domain\SmartLink\ValueObjects\SmartLinkId;
|
||||
use App\Framework\Exception\ErrorCode;
|
||||
use App\Framework\Exception\Core\DatabaseErrorCode;
|
||||
use App\Framework\Exception\FrameworkException;
|
||||
|
||||
final class SmartLinkNotFoundException extends FrameworkException
|
||||
@@ -14,7 +14,7 @@ final class SmartLinkNotFoundException extends FrameworkException
|
||||
public static function forId(SmartLinkId $id): self
|
||||
{
|
||||
return self::create(
|
||||
ErrorCode::ENTITY_NOT_FOUND,
|
||||
DatabaseErrorCode::ENTITY_NOT_FOUND,
|
||||
sprintf('SmartLink with ID "%s" not found', $id->toString())
|
||||
)->withData(['link_id' => $id->toString()]);
|
||||
}
|
||||
@@ -22,7 +22,7 @@ final class SmartLinkNotFoundException extends FrameworkException
|
||||
public static function forShortCode(ShortCode $shortCode): self
|
||||
{
|
||||
return self::create(
|
||||
ErrorCode::ENTITY_NOT_FOUND,
|
||||
DatabaseErrorCode::ENTITY_NOT_FOUND,
|
||||
sprintf('SmartLink with code "%s" not found', $shortCode->toString())
|
||||
)->withData(['short_code' => $shortCode->toString()]);
|
||||
}
|
||||
|
||||
@@ -17,35 +17,27 @@ final readonly class CreateClickEventsTable implements Migration
|
||||
{
|
||||
$schema = new Schema($connection);
|
||||
|
||||
$schema->create('click_events', function (Blueprint $table) {
|
||||
$schema->create('smartlink_click_events', function (Blueprint $table) {
|
||||
$table->string('id', 26)->primary(); // ULID
|
||||
$table->string('link_id', 26);
|
||||
$table->string('smartlink_id', 26);
|
||||
$table->string('ip_hash', 64); // SHA-256 hash
|
||||
$table->string('country_code', 2);
|
||||
$table->string('device_type', 20); // enum: DeviceType
|
||||
$table->text('user_agent');
|
||||
$table->string('country_code', 2)->nullable();
|
||||
$table->string('device_type', 20)->nullable(); // enum: DeviceType
|
||||
$table->text('user_agent')->nullable();
|
||||
$table->text('referer')->nullable();
|
||||
$table->string('destination_service', 30)->nullable();
|
||||
$table->boolean('converted')->default(false);
|
||||
$table->boolean('is_conversion')->default(false);
|
||||
$table->timestamp('clicked_at');
|
||||
|
||||
$table->foreign('link_id')->references('id')->on('smart_links')->onDelete(ForeignKeyAction::CASCADE);
|
||||
$table->index('link_id');
|
||||
$table->foreign('smartlink_id')->references('id')->on('smart_links')->onDelete(ForeignKeyAction::CASCADE);
|
||||
$table->index('smartlink_id');
|
||||
$table->index('clicked_at');
|
||||
$table->index(['link_id', 'clicked_at']);
|
||||
$table->index(['smartlink_id', 'clicked_at']);
|
||||
$table->index('ip_hash'); // For unique visitor tracking
|
||||
});
|
||||
|
||||
$schema->execute();
|
||||
}
|
||||
|
||||
public function down(ConnectionInterface $connection): void
|
||||
{
|
||||
$schema = new Schema($connection);
|
||||
|
||||
$schema->dropIfExists('click_events');
|
||||
$schema->execute();
|
||||
}
|
||||
|
||||
public function getVersion(): MigrationVersion
|
||||
{
|
||||
|
||||
@@ -21,4 +21,49 @@ interface ClickEventRepository
|
||||
public function countUniqueByLinkId(SmartLinkId $linkId): int;
|
||||
|
||||
public function getConversionsByLinkId(SmartLinkId $linkId): int;
|
||||
|
||||
// Analytics Aggregation Methods
|
||||
|
||||
/**
|
||||
* Get click time-series data grouped by hour
|
||||
*
|
||||
* @return array<array{hour: string, clicks: int, unique_clicks: int}>
|
||||
*/
|
||||
public function getClickTimeSeriesByHour(Timestamp $since): array;
|
||||
|
||||
/**
|
||||
* Get geographic distribution of clicks
|
||||
*
|
||||
* @return array<array{country_code: string, click_count: int}>
|
||||
*/
|
||||
public function getGeographicDistribution(?Timestamp $since = null): array;
|
||||
|
||||
/**
|
||||
* Get device type distribution
|
||||
*
|
||||
* @return array<array{device_type: string, click_count: int}>
|
||||
*/
|
||||
public function getDeviceTypeDistribution(?Timestamp $since = null): array;
|
||||
|
||||
/**
|
||||
* Get top performing links by click count
|
||||
*
|
||||
* @return array<array{link_id: string, click_count: int, unique_click_count: int}>
|
||||
*/
|
||||
public function getTopLinksByClicks(int $limit, ?Timestamp $since = null): array;
|
||||
|
||||
/**
|
||||
* Count total clicks globally
|
||||
*/
|
||||
public function countTotal(?Timestamp $since = null): int;
|
||||
|
||||
/**
|
||||
* Count total unique clicks globally
|
||||
*/
|
||||
public function countUniqueTotal(?Timestamp $since = null): int;
|
||||
|
||||
/**
|
||||
* Count total conversions globally
|
||||
*/
|
||||
public function countConversionsTotal(?Timestamp $since = null): int;
|
||||
}
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\SmartLink\Repositories;
|
||||
|
||||
use App\Domain\SmartLink\Entities\ClickEvent;
|
||||
use App\Domain\SmartLink\Enums\DeviceType;
|
||||
use App\Domain\SmartLink\ValueObjects\ClickId;
|
||||
use App\Domain\SmartLink\ValueObjects\SmartLinkId;
|
||||
use App\Framework\Core\ValueObjects\CountryCode;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
use App\Framework\Database\ConnectionInterface;
|
||||
use App\Framework\Database\ValueObjects\SqlQuery;
|
||||
|
||||
final readonly class DatabaseClickEventRepository implements ClickEventRepository
|
||||
{
|
||||
public function __construct(
|
||||
private ConnectionInterface $connection
|
||||
) {
|
||||
}
|
||||
|
||||
public function save(ClickEvent $event): void
|
||||
{
|
||||
$sql = 'INSERT INTO click_events
|
||||
(id, link_id, ip_hash, country_code, device_type, user_agent, referer, destination_service, converted, clicked_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||
|
||||
$query = SqlQuery::create($sql, [
|
||||
$event->id->toString(),
|
||||
$event->linkId->toString(),
|
||||
$event->ipHash,
|
||||
$event->countryCode->toString(),
|
||||
$event->deviceType->value,
|
||||
$event->userAgentString,
|
||||
$event->referer,
|
||||
$event->destinationService,
|
||||
$event->converted ? 1 : 0,
|
||||
$event->clickedAt->format('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
$this->connection->execute($query);
|
||||
}
|
||||
|
||||
public function findByLinkId(SmartLinkId $linkId, ?Timestamp $since = null): array
|
||||
{
|
||||
if ($since !== null) {
|
||||
$sql = 'SELECT * FROM click_events WHERE link_id = ? AND clicked_at >= ? ORDER BY clicked_at DESC';
|
||||
$query = SqlQuery::create($sql, [$linkId->toString(), $since->format('Y-m-d H:i:s')]);
|
||||
} else {
|
||||
$sql = 'SELECT * FROM click_events WHERE link_id = ? ORDER BY clicked_at DESC';
|
||||
$query = SqlQuery::create($sql, [$linkId->toString()]);
|
||||
}
|
||||
|
||||
$result = $this->connection->query($query);
|
||||
$rows = $result->fetchAll();
|
||||
|
||||
return array_map(fn (array $row) => $this->hydrate($row), $rows);
|
||||
}
|
||||
|
||||
public function getRecentClicksByIpHash(string $ipHash, int $minutes = 60): array
|
||||
{
|
||||
$since = Timestamp::now()->subtractMinutes($minutes);
|
||||
|
||||
$sql = 'SELECT * FROM click_events WHERE ip_hash = ? AND clicked_at >= ?';
|
||||
$query = SqlQuery::create($sql, [$ipHash, $since->format('Y-m-d H:i:s')]);
|
||||
$result = $this->connection->query($query);
|
||||
$rows = $result->fetchAll();
|
||||
|
||||
return array_map(fn (array $row) => $this->hydrate($row), $rows);
|
||||
}
|
||||
|
||||
public function countByLinkId(SmartLinkId $linkId): int
|
||||
{
|
||||
$sql = 'SELECT COUNT(*) as count FROM click_events WHERE link_id = ?';
|
||||
$query = SqlQuery::create($sql, [$linkId->toString()]);
|
||||
$result = $this->connection->query($query);
|
||||
$row = $result->fetch();
|
||||
|
||||
return (int) ($row['count'] ?? 0);
|
||||
}
|
||||
|
||||
public function countUniqueByLinkId(SmartLinkId $linkId): int
|
||||
{
|
||||
$sql = 'SELECT COUNT(DISTINCT ip_hash) as count FROM click_events WHERE link_id = ?';
|
||||
$query = SqlQuery::create($sql, [$linkId->toString()]);
|
||||
$result = $this->connection->query($query);
|
||||
$row = $result->fetch();
|
||||
|
||||
return (int) ($row['count'] ?? 0);
|
||||
}
|
||||
|
||||
public function getConversionsByLinkId(SmartLinkId $linkId): int
|
||||
{
|
||||
$sql = 'SELECT COUNT(*) as count FROM click_events WHERE link_id = ? AND converted = 1';
|
||||
$query = SqlQuery::create($sql, [$linkId->toString()]);
|
||||
$result = $this->connection->query($query);
|
||||
$row = $result->fetch();
|
||||
|
||||
return (int) ($row['count'] ?? 0);
|
||||
}
|
||||
|
||||
private function hydrate(array $row): ClickEvent
|
||||
{
|
||||
return new ClickEvent(
|
||||
id: ClickId::fromString($row['id']),
|
||||
linkId: SmartLinkId::fromString($row['link_id']),
|
||||
ipHash: $row['ip_hash'],
|
||||
countryCode: CountryCode::fromString($row['country_code']),
|
||||
deviceType: DeviceType::from($row['device_type']),
|
||||
userAgentString: $row['user_agent'],
|
||||
referer: $row['referer'],
|
||||
destinationService: $row['destination_service'],
|
||||
converted: (bool) $row['converted'],
|
||||
clickedAt: Timestamp::fromDateTime(new \DateTimeImmutable($row['clicked_at']))
|
||||
);
|
||||
}
|
||||
}
|
||||
232
src/Domain/SmartLink/Services/ClickStatisticsService.php
Normal file
232
src/Domain/SmartLink/Services/ClickStatisticsService.php
Normal file
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\SmartLink\Services;
|
||||
|
||||
use App\Domain\SmartLink\Repositories\ClickEventRepository;
|
||||
use App\Domain\SmartLink\ValueObjects\SmartLinkId;
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
|
||||
/**
|
||||
* Click Statistics Aggregation Service
|
||||
*
|
||||
* Provides comprehensive analytics aggregations for SmartLink clicks including:
|
||||
* - Time-series click data
|
||||
* - Geographic distribution
|
||||
* - Device/Browser statistics
|
||||
* - Top-performing links
|
||||
* - Conversion metrics
|
||||
*/
|
||||
final readonly class ClickStatisticsService
|
||||
{
|
||||
public function __construct(
|
||||
private ClickEventRepository $clickEventRepository
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get aggregated click statistics for a time period
|
||||
*
|
||||
* @return array{total_clicks: int, unique_clicks: int, conversions: int, conversion_rate: float}
|
||||
*/
|
||||
public function getOverviewStats(?Timestamp $since = null): array
|
||||
{
|
||||
$totalClicks = $this->clickEventRepository->countTotal($since);
|
||||
$uniqueClicks = $this->clickEventRepository->countUniqueTotal($since);
|
||||
$conversions = $this->clickEventRepository->countConversionsTotal($since);
|
||||
|
||||
$conversionRate = $totalClicks > 0
|
||||
? ($conversions / $totalClicks) * 100
|
||||
: 0.0;
|
||||
|
||||
return [
|
||||
'total_clicks' => $totalClicks,
|
||||
'unique_clicks' => $uniqueClicks,
|
||||
'conversions' => $conversions,
|
||||
'conversion_rate' => round($conversionRate, 2),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get click time-series data for chart visualization
|
||||
*
|
||||
* @param int $hours Number of hours to include (default: 24)
|
||||
* @return array<array{timestamp: string, clicks: int, unique_clicks: int}>
|
||||
*/
|
||||
public function getClickTimeSeries(int $hours = 24): array
|
||||
{
|
||||
$since = Timestamp::now()->subtractHours($hours);
|
||||
$rawData = $this->clickEventRepository->getClickTimeSeriesByHour($since);
|
||||
|
||||
// Transform for frontend consumption
|
||||
return array_map(function (array $row) {
|
||||
return [
|
||||
'timestamp' => $row['hour'],
|
||||
'clicks' => (int) $row['clicks'],
|
||||
'unique_clicks' => (int) $row['unique_clicks'],
|
||||
];
|
||||
}, $rawData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get geographic distribution of clicks
|
||||
*
|
||||
* @return array<array{country_code: string, country_name: string, click_count: int, percentage: float}>
|
||||
*/
|
||||
public function getGeographicDistribution(?Timestamp $since = null): array
|
||||
{
|
||||
$rawData = $this->clickEventRepository->getGeographicDistribution($since);
|
||||
$totalClicks = array_sum(array_column($rawData, 'click_count'));
|
||||
|
||||
return array_map(function (array $row) use ($totalClicks) {
|
||||
$percentage = $totalClicks > 0
|
||||
? ((int) $row['click_count'] / $totalClicks) * 100
|
||||
: 0.0;
|
||||
|
||||
return [
|
||||
'country_code' => $row['country_code'],
|
||||
'country_name' => $this->getCountryName($row['country_code']),
|
||||
'click_count' => (int) $row['click_count'],
|
||||
'percentage' => round($percentage, 2),
|
||||
];
|
||||
}, $rawData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get device type distribution
|
||||
*
|
||||
* @return array{mobile: int, desktop: int, tablet: int, mobile_percentage: float, desktop_percentage: float, tablet_percentage: float}
|
||||
*/
|
||||
public function getDeviceDistribution(?Timestamp $since = null): array
|
||||
{
|
||||
$rawData = $this->clickEventRepository->getDeviceTypeDistribution($since);
|
||||
|
||||
$mobile = 0;
|
||||
$desktop = 0;
|
||||
$tablet = 0;
|
||||
|
||||
foreach ($rawData as $row) {
|
||||
$count = (int) $row['click_count'];
|
||||
match ($row['device_type']) {
|
||||
'mobile' => $mobile = $count,
|
||||
'desktop' => $desktop = $count,
|
||||
'tablet' => $tablet = $count,
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
$total = $mobile + $desktop + $tablet;
|
||||
|
||||
return [
|
||||
'mobile' => $mobile,
|
||||
'desktop' => $desktop,
|
||||
'tablet' => $tablet,
|
||||
'mobile_percentage' => $total > 0 ? round(($mobile / $total) * 100, 2) : 0.0,
|
||||
'desktop_percentage' => $total > 0 ? round(($desktop / $total) * 100, 2) : 0.0,
|
||||
'tablet_percentage' => $total > 0 ? round(($tablet / $total) * 100, 2) : 0.0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get top performing links by click count
|
||||
*
|
||||
* @param int $limit Number of links to return (default: 10)
|
||||
* @return array<array{link_id: string, clicks: int, unique_clicks: int}>
|
||||
*/
|
||||
public function getTopPerformingLinks(int $limit = 10, ?Timestamp $since = null): array
|
||||
{
|
||||
$rawData = $this->clickEventRepository->getTopLinksByClicks($limit, $since);
|
||||
|
||||
// Note: For now, we return the aggregated data without joining to smart_links table
|
||||
// Frontend will need to fetch link details separately or we can implement a JOIN query
|
||||
return array_map(function (array $row) {
|
||||
return [
|
||||
'link_id' => $row['link_id'],
|
||||
'clicks' => (int) $row['click_count'],
|
||||
'unique_clicks' => (int) $row['unique_click_count'],
|
||||
];
|
||||
}, $rawData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get browser distribution statistics
|
||||
*
|
||||
* @return array<array{browser: string, click_count: int, percentage: float}>
|
||||
*/
|
||||
public function getBrowserDistribution(?Timestamp $since = null): array
|
||||
{
|
||||
// TODO: Implement browser parsing from user_agent field
|
||||
// This requires user-agent parsing library or regex patterns
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get real-time click statistics (last 60 minutes)
|
||||
*
|
||||
* @return array{last_minute: int, last_5_minutes: int, last_15_minutes: int, last_60_minutes: int}
|
||||
*/
|
||||
public function getRealTimeStats(): array
|
||||
{
|
||||
$now = Timestamp::now();
|
||||
|
||||
return [
|
||||
'last_minute' => $this->clickEventRepository->countTotal($now->subtractMinutes(1)),
|
||||
'last_5_minutes' => $this->clickEventRepository->countTotal($now->subtractMinutes(5)),
|
||||
'last_15_minutes' => $this->clickEventRepository->countTotal($now->subtractMinutes(15)),
|
||||
'last_60_minutes' => $this->clickEventRepository->countTotal($now->subtractMinutes(60)),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Get country name from country code
|
||||
*/
|
||||
private function getCountryName(string $countryCode): string
|
||||
{
|
||||
// Simple country code mapping - could be replaced with a proper library
|
||||
$countries = [
|
||||
'DE' => 'Germany',
|
||||
'US' => 'United States',
|
||||
'GB' => 'United Kingdom',
|
||||
'FR' => 'France',
|
||||
'ES' => 'Spain',
|
||||
'IT' => 'Italy',
|
||||
'NL' => 'Netherlands',
|
||||
'AT' => 'Austria',
|
||||
'CH' => 'Switzerland',
|
||||
'BE' => 'Belgium',
|
||||
];
|
||||
|
||||
return $countries[$countryCode] ?? $countryCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get click statistics for a specific link
|
||||
*
|
||||
* @return array{total_clicks: int, unique_clicks: int, conversions: int, conversion_rate: float, avg_clicks_per_day: float}
|
||||
*/
|
||||
public function getLinkStatistics(SmartLinkId $linkId, ?Timestamp $since = null): array
|
||||
{
|
||||
$totalClicks = $this->clickEventRepository->countByLinkId($linkId);
|
||||
$uniqueClicks = $this->clickEventRepository->countUniqueByLinkId($linkId);
|
||||
$conversions = $this->clickEventRepository->getConversionsByLinkId($linkId);
|
||||
|
||||
$conversionRate = $totalClicks > 0
|
||||
? ($conversions / $totalClicks) * 100
|
||||
: 0.0;
|
||||
|
||||
// Calculate average clicks per day
|
||||
$daysSince = $since ? $since->diffInDays(Timestamp::now()) : 30; // Default 30 days
|
||||
$avgClicksPerDay = $daysSince > 0
|
||||
? $totalClicks / $daysSince
|
||||
: 0.0;
|
||||
|
||||
return [
|
||||
'total_clicks' => $totalClicks,
|
||||
'unique_clicks' => $uniqueClicks,
|
||||
'conversions' => $conversions,
|
||||
'conversion_rate' => round($conversionRate, 2),
|
||||
'avg_clicks_per_day' => round($avgClicksPerDay, 2),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -140,6 +140,10 @@ final readonly class SmartLinkService
|
||||
|
||||
public function deleteLink(SmartLinkId $id): void
|
||||
{
|
||||
// Delete all destinations first (to maintain referential integrity)
|
||||
$this->destinationRepository->deleteByLinkId($id);
|
||||
|
||||
// Delete the link itself
|
||||
$this->linkRepository->delete($id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\SmartLink;
|
||||
|
||||
use App\Domain\SmartLink\Repositories\ClickEventRepository;
|
||||
use App\Domain\SmartLink\Repositories\DatabaseClickEventRepository;
|
||||
use App\Domain\SmartLink\Repositories\DatabaseLinkDestinationRepository;
|
||||
use App\Domain\SmartLink\Repositories\DatabaseSmartLinkRepository;
|
||||
use App\Domain\SmartLink\Repositories\LinkDestinationRepository;
|
||||
use App\Domain\SmartLink\Repositories\SmartLinkRepository;
|
||||
use App\Framework\Database\ConnectionInterface;
|
||||
use App\Framework\DI\Initializer;
|
||||
|
||||
final readonly class SmartLinkServiceInitializer
|
||||
{
|
||||
#[Initializer]
|
||||
public function initializeSmartLinkRepository(
|
||||
ConnectionInterface $connection
|
||||
): SmartLinkRepository {
|
||||
return new DatabaseSmartLinkRepository($connection);
|
||||
}
|
||||
|
||||
#[Initializer]
|
||||
public function initializeLinkDestinationRepository(
|
||||
ConnectionInterface $connection
|
||||
): LinkDestinationRepository {
|
||||
return new DatabaseLinkDestinationRepository($connection);
|
||||
}
|
||||
|
||||
#[Initializer]
|
||||
public function initializeClickEventRepository(
|
||||
ConnectionInterface $connection
|
||||
): ClickEventRepository {
|
||||
return new DatabaseClickEventRepository($connection);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user