docs: consolidate documentation into organized structure

- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -0,0 +1,149 @@
<?php
declare(strict_types=1);
namespace App\Domain\SmartLink\Repositories;
use App\Domain\SmartLink\Entities\SmartLink;
use App\Domain\SmartLink\ValueObjects\LinkSettings;
use App\Domain\SmartLink\Enums\LinkStatus;
use App\Domain\SmartLink\Enums\LinkType;
use App\Domain\SmartLink\ValueObjects\LinkTitle;
use App\Domain\SmartLink\ValueObjects\ShortCode;
use App\Domain\SmartLink\ValueObjects\SmartLinkId;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\ValueObjects\SqlQuery;
final readonly class DatabaseSmartLinkRepository implements SmartLinkRepository
{
public function __construct(
private ConnectionInterface $connection
) {}
public function save(SmartLink $link): void
{
$sql = 'INSERT INTO smart_links (id, short_code, type, title, cover_image_url, status, user_id, settings, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
type = VALUES(type),
title = VALUES(title),
cover_image_url = VALUES(cover_image_url),
status = VALUES(status),
settings = VALUES(settings),
updated_at = VALUES(updated_at)';
$query = SqlQuery::create($sql, [
$link->id->toString(),
$link->shortCode->toString(),
$link->type->value,
$link->title->toString(),
$link->coverImageUrl,
$link->status->value,
$link->userId,
json_encode($link->settings->toArray()),
$link->createdAt->format('Y-m-d H:i:s'),
$link->updatedAt->format('Y-m-d H:i:s'),
]);
$this->connection->execute($query);
}
public function findById(SmartLinkId $id): ?SmartLink
{
$sql = 'SELECT * FROM smart_links WHERE id = ?';
$query = SqlQuery::create($sql, [$id->toString()]);
$result = $this->connection->query($query);
$row = $result->fetch();
return $row ? $this->hydrate($row) : null;
}
public function findByShortCode(ShortCode $shortCode): ?SmartLink
{
$sql = 'SELECT * FROM smart_links WHERE short_code = ?';
$query = SqlQuery::create($sql, [$shortCode->toString()]);
$result = $this->connection->query($query);
$row = $result->fetch();
return $row ? $this->hydrate($row) : null;
}
public function findByUserId(string $userId, ?LinkStatus $status = null): array
{
if ($status !== null) {
$sql = 'SELECT * FROM smart_links WHERE user_id = ? AND status = ? ORDER BY created_at DESC';
$query = SqlQuery::create($sql, [$userId, $status->value]);
} else {
$sql = 'SELECT * FROM smart_links WHERE user_id = ? ORDER BY created_at DESC';
$query = SqlQuery::create($sql, [$userId]);
}
$result = $this->connection->query($query);
$rows = $result->fetchAll();
return array_map(fn(array $row) => $this->hydrate($row), $rows);
}
public function existsShortCode(ShortCode $shortCode): bool
{
$sql = 'SELECT COUNT(*) as count FROM smart_links WHERE short_code = ?';
$query = SqlQuery::create($sql, [$shortCode->toString()]);
$result = $this->connection->query($query);
$count = $result->fetch();
return ($count['count'] ?? 0) > 0;
}
public function delete(SmartLinkId $id): void
{
$sql = 'DELETE FROM smart_links WHERE id = ?';
$query = SqlQuery::create($sql, [$id->toString()]);
$this->connection->execute($query);
}
public function findActiveLinks(): array
{
$sql = 'SELECT * FROM smart_links WHERE status = ? ORDER BY created_at DESC';
$query = SqlQuery::create($sql, [LinkStatus::ACTIVE->value]);
$result = $this->connection->query($query);
$rows = $result->fetchAll();
return array_map(fn(array $row) => $this->hydrate($row), $rows);
}
public function getTotalClicks(SmartLinkId $id): int
{
$sql = 'SELECT COUNT(*) as count FROM click_events WHERE link_id = ?';
$query = SqlQuery::create($sql, [$id->toString()]);
$result = $this->connection->query($query);
$row = $result->fetch();
return (int) ($row['count'] ?? 0);
}
private function hydrate(array $row): SmartLink
{
$settings = json_decode($row['settings'], true);
return new SmartLink(
id: SmartLinkId::fromString($row['id']),
shortCode: ShortCode::fromString($row['short_code']),
type: LinkType::from($row['type']),
title: LinkTitle::fromString($row['title']),
coverImageUrl: $row['cover_image_url'],
status: LinkStatus::from($row['status']),
userId: $row['user_id'],
settings: new LinkSettings(
trackClicks: (bool) $settings['track_clicks'],
enableGeoRouting: (bool) $settings['enable_geo_routing'],
showPreview: (bool) $settings['show_preview'],
customDomain: $settings['custom_domain'] ?? null,
clickLimit: $settings['click_limit'] ?? null,
password: $settings['password'] ?? null
),
createdAt: Timestamp::fromDateTime(new \DateTimeImmutable($row['created_at'])),
updatedAt: Timestamp::fromDateTime(new \DateTimeImmutable($row['updated_at']))
);
}
}