boolean('state', true); $this->toggleSimple( request: $request, table: 'artwork_favourites', keyColumns: ['user_id', 'artwork_id'], keyValues: ['user_id' => (int) $request->user()->id, 'artwork_id' => $artworkId], insertPayload: ['created_at' => now(), 'updated_at' => now()], requiredTable: 'artwork_favourites' ); $this->syncArtworkStats($artworkId); // Update creator's favorites_received_count $creatorId = (int) DB::table('artworks')->where('id', $artworkId)->value('user_id'); if ($creatorId) { $svc = app(UserStatsService::class); if ($state) { $svc->incrementFavoritesReceived($creatorId); $svc->setLastActiveAt((int) $request->user()->id); // Record activity event (new favourite only) try { \App\Models\ActivityEvent::record( actorId: (int) $request->user()->id, type: \App\Models\ActivityEvent::TYPE_FAVORITE, targetType: \App\Models\ActivityEvent::TARGET_ARTWORK, targetId: $artworkId, ); } catch (\Throwable) {} } else { $svc->decrementFavoritesReceived($creatorId); } } return response()->json($this->statusPayload((int) $request->user()->id, $artworkId)); } public function like(Request $request, int $artworkId): JsonResponse { $this->toggleSimple( request: $request, table: 'artwork_likes', keyColumns: ['user_id', 'artwork_id'], keyValues: ['user_id' => (int) $request->user()->id, 'artwork_id' => $artworkId], insertPayload: ['created_at' => now(), 'updated_at' => now()], requiredTable: 'artwork_likes' ); $this->syncArtworkStats($artworkId); return response()->json($this->statusPayload((int) $request->user()->id, $artworkId)); } public function report(Request $request, int $artworkId): JsonResponse { if (! Schema::hasTable('artwork_reports')) { return response()->json(['message' => 'Reporting unavailable'], 422); } $data = $request->validate([ 'reason' => ['nullable', 'string', 'max:1000'], ]); DB::table('artwork_reports')->updateOrInsert( [ 'artwork_id' => $artworkId, 'reporter_user_id' => (int) $request->user()->id, ], [ 'reason' => trim((string) ($data['reason'] ?? '')) ?: null, 'reported_at' => now(), 'created_at' => now(), 'updated_at' => now(), ] ); return response()->json(['ok' => true, 'reported' => true]); } public function follow(Request $request, int $userId): JsonResponse { $actorId = (int) $request->user()->id; if ($actorId === $userId) { return response()->json(['message' => 'Cannot follow yourself'], 422); } $svc = app(FollowService::class); $state = $request->boolean('state', true); if ($state) { $svc->follow($actorId, $userId); } else { $svc->unfollow($actorId, $userId); } return response()->json([ 'ok' => true, 'is_following' => $state, 'followers_count' => $svc->followersCount($userId), ]); } /** * POST /api/artworks/{id}/share — record a share event (Phase 2 tracking). */ public function share(Request $request, int $artworkId): JsonResponse { $data = $request->validate([ 'platform' => ['required', 'string', 'in:facebook,twitter,pinterest,email,copy,embed'], ]); if (Schema::hasTable('artwork_shares')) { DB::table('artwork_shares')->insert([ 'artwork_id' => $artworkId, 'user_id' => $request->user()?->id, 'platform' => $data['platform'], 'created_at' => now(), ]); } return response()->json(['ok' => true]); } private function toggleSimple( Request $request, string $table, array $keyColumns, array $keyValues, array $insertPayload, string $requiredTable ): void { if (! Schema::hasTable($requiredTable)) { abort(422, 'Interaction unavailable'); } $state = $request->boolean('state', true); $query = DB::table($table); foreach ($keyColumns as $column) { $query->where($column, $keyValues[$column]); } if ($state) { if (! $query->exists()) { DB::table($table)->insert(array_merge($keyValues, $insertPayload)); } } else { $query->delete(); } } private function syncArtworkStats(int $artworkId): void { if (! Schema::hasTable('artwork_stats')) { return; } $favorites = Schema::hasTable('artwork_favourites') ? (int) DB::table('artwork_favourites')->where('artwork_id', $artworkId)->count() : 0; $likes = Schema::hasTable('artwork_likes') ? (int) DB::table('artwork_likes')->where('artwork_id', $artworkId)->count() : 0; DB::table('artwork_stats')->updateOrInsert( ['artwork_id' => $artworkId], [ 'favorites' => $favorites, 'rating_count' => $likes, ] ); } private function statusPayload(int $viewerId, int $artworkId): array { $isFavorited = Schema::hasTable('artwork_favourites') ? DB::table('artwork_favourites')->where('user_id', $viewerId)->where('artwork_id', $artworkId)->exists() : false; $isLiked = Schema::hasTable('artwork_likes') ? DB::table('artwork_likes')->where('user_id', $viewerId)->where('artwork_id', $artworkId)->exists() : false; $favorites = Schema::hasTable('artwork_favourites') ? (int) DB::table('artwork_favourites')->where('artwork_id', $artworkId)->count() : 0; $likes = Schema::hasTable('artwork_likes') ? (int) DB::table('artwork_likes')->where('artwork_id', $artworkId)->count() : 0; return [ 'ok' => true, 'is_favorited' => $isFavorited, 'is_liked' => $isLiked, 'stats' => [ 'favorites' => $favorites, 'likes' => $likes, ], ]; } }