storing analytics data
This commit is contained in:
124
app/Http/Controllers/Web/CommunityActivityController.php
Normal file
124
app/Http/Controllers/Web/CommunityActivityController.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ActivityEvent;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Community activity feed.
|
||||
*
|
||||
* GET /community/activity?type=global|following
|
||||
*/
|
||||
final class CommunityActivityController extends Controller
|
||||
{
|
||||
private const PER_PAGE = 30;
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
$type = $request->query('type', 'global'); // global | following
|
||||
$perPage = self::PER_PAGE;
|
||||
|
||||
$query = ActivityEvent::query()
|
||||
->orderByDesc('created_at')
|
||||
->with(['actor:id,name,username']);
|
||||
|
||||
if ($type === 'following' && $user) {
|
||||
// Show only events from followed users
|
||||
$followingIds = DB::table('user_followers')
|
||||
->where('follower_id', $user->id)
|
||||
->pluck('user_id')
|
||||
->all();
|
||||
|
||||
if (empty($followingIds)) {
|
||||
$query->whereRaw('0 = 1'); // empty result set
|
||||
} else {
|
||||
$query->whereIn('actor_id', $followingIds);
|
||||
}
|
||||
}
|
||||
|
||||
$events = $query->paginate($perPage)->withQueryString();
|
||||
$enriched = $this->enrich($events->getCollection());
|
||||
|
||||
return view('web.community.activity', [
|
||||
'events' => $events,
|
||||
'enriched' => $enriched,
|
||||
'active_tab' => $type,
|
||||
'page_title' => 'Community Activity',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach target object data to each event for display.
|
||||
*/
|
||||
private function enrich(\Illuminate\Support\Collection $events): \Illuminate\Support\Collection
|
||||
{
|
||||
// Collect artwork IDs and user IDs to eager-load
|
||||
$artworkIds = $events
|
||||
->where('target_type', ActivityEvent::TARGET_ARTWORK)
|
||||
->pluck('target_id')
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$userIds = $events
|
||||
->where('target_type', ActivityEvent::TARGET_USER)
|
||||
->pluck('target_id')
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$artworks = Artwork::whereIn('id', $artworkIds)
|
||||
->with('user:id,name,username')
|
||||
->get(['id', 'title', 'slug', 'user_id', 'hash', 'thumb_ext'])
|
||||
->keyBy('id');
|
||||
|
||||
$users = User::whereIn('id', $userIds)
|
||||
->with('profile:user_id,avatar_hash')
|
||||
->get(['id', 'name', 'username'])
|
||||
->keyBy('id');
|
||||
|
||||
return $events->map(function (ActivityEvent $event) use ($artworks, $users): array {
|
||||
$target = null;
|
||||
|
||||
if ($event->target_type === ActivityEvent::TARGET_ARTWORK) {
|
||||
$artwork = $artworks->get($event->target_id);
|
||||
$target = $artwork ? [
|
||||
'id' => $artwork->id,
|
||||
'title' => $artwork->title,
|
||||
'url' => '/art/' . $artwork->id . '/' . $artwork->slug,
|
||||
'thumb' => $artwork->thumbUrl('sm'),
|
||||
] : null;
|
||||
} elseif ($event->target_type === ActivityEvent::TARGET_USER) {
|
||||
$u = $users->get($event->target_id);
|
||||
$target = $u ? [
|
||||
'id' => $u->id,
|
||||
'name' => $u->name,
|
||||
'username' => $u->username,
|
||||
'url' => '/@' . $u->username,
|
||||
] : null;
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $event->id,
|
||||
'type' => $event->type,
|
||||
'target_type' => $event->target_type,
|
||||
'actor' => [
|
||||
'id' => $event->actor?->id,
|
||||
'name' => $event->actor?->name,
|
||||
'username' => $event->actor?->username,
|
||||
'url' => '/@' . $event->actor?->username,
|
||||
],
|
||||
'target' => $target,
|
||||
'created_at' => $event->created_at?->toIso8601String(),
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ use App\Services\ArtworkSearchService;
|
||||
use App\Services\ArtworkService;
|
||||
use App\Services\ThumbnailPresenter;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
@@ -190,26 +191,56 @@ final class DiscoverController extends Controller
|
||||
->pluck('user_id');
|
||||
|
||||
if ($followingIds->isEmpty()) {
|
||||
$artworks = Artwork::query()->paginate(0);
|
||||
// Trending fallback: show popular artworks so the page isn't blank
|
||||
try {
|
||||
$fallbackResults = $this->searchService->discoverTrending(12);
|
||||
$fallbackArtworks = $fallbackResults->getCollection()
|
||||
->transform(fn ($a) => $this->presentArtwork($a));
|
||||
} catch (\Throwable) {
|
||||
$fallbackArtworks = collect();
|
||||
}
|
||||
|
||||
// Suggested creators: most-followed users the viewer doesn't follow yet
|
||||
$suggestedCreators = DB::table('users')
|
||||
->join('user_statistics', 'users.id', '=', 'user_statistics.user_id')
|
||||
->where('users.id', '!=', $user->id)
|
||||
->whereNotNull('users.email_verified_at')
|
||||
->where('users.is_active', true)
|
||||
->orderByDesc('user_statistics.followers_count')
|
||||
->limit(8)
|
||||
->select(
|
||||
'users.id',
|
||||
'users.name',
|
||||
'users.username',
|
||||
'user_statistics.followers_count',
|
||||
)
|
||||
->get();
|
||||
|
||||
return view('web.discover.index', [
|
||||
'artworks' => $artworks,
|
||||
'page_title' => 'Following Feed',
|
||||
'section' => 'following',
|
||||
'description' => 'Follow some creators to see their work here.',
|
||||
'icon' => 'fa-user-group',
|
||||
'empty' => true,
|
||||
'artworks' => collect(),
|
||||
'page_title' => 'Following Feed',
|
||||
'section' => 'following',
|
||||
'description' => 'Follow some creators to see their work here.',
|
||||
'icon' => 'fa-user-group',
|
||||
'empty' => true,
|
||||
'fallback_trending' => $fallbackArtworks,
|
||||
'fallback_creators' => $suggestedCreators,
|
||||
]);
|
||||
}
|
||||
|
||||
$artworks = Artwork::query()
|
||||
->public()
|
||||
->published()
|
||||
->with(['user:id,name,username', 'categories:id,name,slug,content_type_id,parent_id,sort_order'])
|
||||
->whereIn('user_id', $followingIds)
|
||||
->orderByDesc('published_at')
|
||||
->paginate($perPage)
|
||||
->withQueryString();
|
||||
$page = (int) request()->get('page', 1);
|
||||
$cacheKey = "discover.following.{$user->id}.p{$page}";
|
||||
|
||||
$artworks = Cache::remember($cacheKey, 60, function () use ($user, $followingIds, $perPage): \Illuminate\Pagination\LengthAwarePaginator {
|
||||
return Artwork::query()
|
||||
->public()
|
||||
->published()
|
||||
->with(['user:id,name,username', 'categories:id,name,slug,content_type_id,parent_id,sort_order'])
|
||||
->whereIn('user_id', $followingIds)
|
||||
->orderByDesc('published_at')
|
||||
->paginate($perPage)
|
||||
->withQueryString();
|
||||
});
|
||||
|
||||
$artworks->getCollection()->transform(fn ($a) => $this->presentArtwork($a));
|
||||
|
||||
|
||||
@@ -14,7 +14,10 @@ final class HomeController extends Controller
|
||||
|
||||
public function index(Request $request): \Illuminate\View\View
|
||||
{
|
||||
$sections = $this->homepage->all();
|
||||
$user = $request->user();
|
||||
$sections = $user
|
||||
? $this->homepage->allForUser($user)
|
||||
: $this->homepage->all();
|
||||
|
||||
$hero = $sections['hero'];
|
||||
|
||||
@@ -27,8 +30,9 @@ final class HomeController extends Controller
|
||||
];
|
||||
|
||||
return view('web.home', [
|
||||
'meta' => $meta,
|
||||
'props' => $sections,
|
||||
'meta' => $meta,
|
||||
'props' => $sections,
|
||||
'is_logged_in' => (bool) $user,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user