Expand Link app with analytics, settings, and custom domains.
Deploy Ladill Link / deploy (push) Successful in 37s
Deploy Ladill Link / deploy (push) Successful in 37s
Add Bitly-style branded domain support via Ladill Domains SSL API, account analytics dashboard, settings page with default domain picker, and fix SSO/dashboard issues from QR Plus template leftovers. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Link;
|
||||
|
||||
use App\Models\LinkClick;
|
||||
use App\Models\ShortLink;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class LinkAnalyticsService
|
||||
{
|
||||
/** @return array{total_clicks:int,unique_clicks:int,clicks_7d:int,clicks_30d:int,links_total:int} */
|
||||
public function summaryForAccount(User $account): array
|
||||
{
|
||||
$links = ShortLink::query()->where('user_id', $account->id);
|
||||
$clicks = LinkClick::query()->whereIn('short_link_id', (clone $links)->select('id'));
|
||||
|
||||
return [
|
||||
'total_clicks' => (int) (clone $links)->sum('clicks_total'),
|
||||
'unique_clicks' => (int) (clone $links)->sum('unique_clicks_total'),
|
||||
'clicks_7d' => (int) (clone $clicks)->where('clicked_at', '>=', now()->subDays(7))->count(),
|
||||
'clicks_30d' => (int) (clone $clicks)->where('clicked_at', '>=', now()->subDays(30))->count(),
|
||||
'links_total' => (int) (clone $links)->count(),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return Collection<int, object{date:string,total:int}> */
|
||||
public function dailyClicksForAccount(User $account, int $days = 30): Collection
|
||||
{
|
||||
$rows = LinkClick::query()
|
||||
->selectRaw('DATE(clicked_at) as date, COUNT(*) as total')
|
||||
->whereIn('short_link_id', ShortLink::query()->where('user_id', $account->id)->select('id'))
|
||||
->where('clicked_at', '>=', now()->subDays($days - 1)->startOfDay())
|
||||
->groupBy('date')
|
||||
->orderBy('date')
|
||||
->get()
|
||||
->keyBy('date');
|
||||
|
||||
return collect(range(0, $days - 1))->map(function (int $offset) use ($rows, $days) {
|
||||
$date = now()->subDays($days - 1 - $offset)->toDateString();
|
||||
|
||||
return (object) [
|
||||
'date' => $date,
|
||||
'total' => (int) ($rows[$date]->total ?? 0),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/** @return Collection<int, array{label:string,total:int}> */
|
||||
public function topLinksForAccount(User $account, int $limit = 8): Collection
|
||||
{
|
||||
return ShortLink::query()
|
||||
->where('user_id', $account->id)
|
||||
->orderByDesc('clicks_total')
|
||||
->limit($limit)
|
||||
->get()
|
||||
->map(fn (ShortLink $link) => [
|
||||
'label' => $link->label ?: $link->slug,
|
||||
'slug' => $link->slug,
|
||||
'total' => $link->clicks_total,
|
||||
'url' => route('user.links.show', $link),
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return Collection<int, object> */
|
||||
public function recentClicksForAccount(User $account, int $limit = 15): Collection
|
||||
{
|
||||
return LinkClick::query()
|
||||
->with('shortLink:id,slug,label')
|
||||
->whereIn('short_link_id', ShortLink::query()->where('user_id', $account->id)->select('id'))
|
||||
->latest('clicked_at')
|
||||
->limit($limit)
|
||||
->get();
|
||||
}
|
||||
|
||||
/** @return Collection<int, array{label:string,total:int}> */
|
||||
public function referrersForAccount(User $account, int $limit = 8): Collection
|
||||
{
|
||||
return LinkClick::query()
|
||||
->select('referer', DB::raw('COUNT(*) as total'))
|
||||
->whereIn('short_link_id', ShortLink::query()->where('user_id', $account->id)->select('id'))
|
||||
->whereNotNull('referer')
|
||||
->where('referer', '!=', '')
|
||||
->groupBy('referer')
|
||||
->orderByDesc('total')
|
||||
->limit($limit)
|
||||
->get()
|
||||
->map(fn ($row) => [
|
||||
'label' => $this->refererLabel((string) $row->referer),
|
||||
'total' => (int) $row->total,
|
||||
]);
|
||||
}
|
||||
|
||||
private function refererLabel(string $referer): string
|
||||
{
|
||||
$host = parse_url($referer, PHP_URL_HOST);
|
||||
|
||||
return is_string($host) && $host !== '' ? $host : $referer;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user