|
17 | 17 | use App\Enum\FileStatus; |
18 | 18 | use App\Enum\SizeVariantType; |
19 | 19 | use App\Exceptions\ConfigurationException; |
| 20 | +use App\Exceptions\ConflictingPropertyException; |
20 | 21 | use App\Http\Requests\Photo\CopyPhotosRequest; |
21 | 22 | use App\Http\Requests\Photo\DeletePhotosRequest; |
22 | 23 | use App\Http\Requests\Photo\EditPhotoRequest; |
23 | 24 | use App\Http\Requests\Photo\FromUrlRequest; |
24 | 25 | use App\Http\Requests\Photo\MovePhotosRequest; |
25 | 26 | use App\Http\Requests\Photo\RenamePhotoRequest; |
26 | 27 | use App\Http\Requests\Photo\RotatePhotoRequest; |
| 28 | +use App\Http\Requests\Photo\SetPhotoRatingRequest; |
27 | 29 | use App\Http\Requests\Photo\SetPhotosStarredRequest; |
28 | 30 | use App\Http\Requests\Photo\SetPhotosTagsRequest; |
29 | 31 | use App\Http\Requests\Photo\UploadPhotoRequest; |
|
36 | 38 | use App\Jobs\ExtractZip; |
37 | 39 | use App\Jobs\ProcessImageJob; |
38 | 40 | use App\Jobs\WatermarkerJob; |
| 41 | +use App\Models\PhotoRating; |
39 | 42 | use App\Models\SizeVariant; |
| 43 | +use App\Models\Statistics; |
40 | 44 | use App\Models\Tag; |
41 | 45 | use App\Repositories\ConfigManager; |
42 | 46 | use Illuminate\Routing\Controller; |
@@ -166,6 +170,89 @@ public function star(SetPhotosStarredRequest $request): void |
166 | 170 | } |
167 | 171 | } |
168 | 172 |
|
| 173 | + /** |
| 174 | + * Set the rating for a photo. |
| 175 | + * |
| 176 | + * @param SetPhotoRatingRequest $request |
| 177 | + * |
| 178 | + * @return PhotoResource |
| 179 | + * |
| 180 | + * @throws ConflictingPropertyException |
| 181 | + */ |
| 182 | + public function rate(SetPhotoRatingRequest $request): PhotoResource |
| 183 | + { |
| 184 | + try { |
| 185 | + DB::beginTransaction(); |
| 186 | + |
| 187 | + $photo = $request->photo(); |
| 188 | + $user = Auth::user(); |
| 189 | + $rating = $request->rating(); |
| 190 | + |
| 191 | + // Ensure statistics record exists atomically (Q001-07) |
| 192 | + $statistics = Statistics::firstOrCreate( |
| 193 | + ['photo_id' => $photo->id], |
| 194 | + [ |
| 195 | + 'album_id' => null, |
| 196 | + 'visit_count' => 0, |
| 197 | + 'download_count' => 0, |
| 198 | + 'favourite_count' => 0, |
| 199 | + 'shared_count' => 0, |
| 200 | + 'rating_sum' => 0, |
| 201 | + 'rating_count' => 0, |
| 202 | + ] |
| 203 | + ); |
| 204 | + |
| 205 | + if ($rating > 0) { |
| 206 | + // Find existing rating by this user for this photo |
| 207 | + $existingRating = PhotoRating::where('photo_id', $photo->id) |
| 208 | + ->where('user_id', $user->id) |
| 209 | + ->first(); |
| 210 | + |
| 211 | + if ($existingRating !== null) { |
| 212 | + // Update: adjust statistics delta |
| 213 | + $delta = $rating - $existingRating->rating; |
| 214 | + $statistics->rating_sum += $delta; |
| 215 | + $existingRating->rating = $rating; |
| 216 | + $existingRating->save(); |
| 217 | + } else { |
| 218 | + // Insert: create new rating and increment statistics |
| 219 | + PhotoRating::create([ |
| 220 | + 'photo_id' => $photo->id, |
| 221 | + 'user_id' => $user->id, |
| 222 | + 'rating' => $rating, |
| 223 | + ]); |
| 224 | + $statistics->rating_sum += $rating; |
| 225 | + $statistics->rating_count += 1; |
| 226 | + } |
| 227 | + |
| 228 | + $statistics->save(); |
| 229 | + } else { |
| 230 | + // Rating == 0: remove rating (idempotent, Q001-06) |
| 231 | + $existingRating = PhotoRating::where('photo_id', $photo->id) |
| 232 | + ->where('user_id', $user->id) |
| 233 | + ->first(); |
| 234 | + |
| 235 | + if ($existingRating !== null) { |
| 236 | + $statistics->rating_sum -= $existingRating->rating; |
| 237 | + $statistics->rating_count -= 1; |
| 238 | + $statistics->save(); |
| 239 | + $existingRating->delete(); |
| 240 | + } |
| 241 | + // If no existing rating, do nothing (idempotent) |
| 242 | + } |
| 243 | + |
| 244 | + DB::commit(); |
| 245 | + |
| 246 | + // Reload photo with fresh statistics |
| 247 | + $photo->refresh(); |
| 248 | + |
| 249 | + return new PhotoResource($photo, null); |
| 250 | + } catch (\Throwable $e) { |
| 251 | + DB::rollBack(); |
| 252 | + throw new ConflictingPropertyException('Failed to update photo rating due to a conflict. Please try again.', $e); |
| 253 | + } |
| 254 | + } |
| 255 | + |
169 | 256 | /** |
170 | 257 | * Moves the photos to an album. |
171 | 258 | */ |
|
0 commit comments