Skip to content

Commit 6032535

Browse files
committed
Fix formatting and phpstan
1 parent ef0ee27 commit 6032535

File tree

4 files changed

+126
-83
lines changed

4 files changed

+126
-83
lines changed

app/Actions/Photo/Rating.php

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
/**
4+
* SPDX-License-Identifier: MIT
5+
* Copyright (c) 2017-2018 Tobias Reich
6+
* Copyright (c) 2018-2025 LycheeOrg.
7+
*/
8+
9+
namespace App\Actions\Photo;
10+
11+
use App\Exceptions\ConflictingPropertyException;
12+
use App\Models\Photo;
13+
use App\Models\PhotoRating;
14+
use App\Models\Statistics;
15+
use App\Models\User;
16+
use Illuminate\Support\Facades\DB;
17+
18+
class Rating
19+
{
20+
/**
21+
* Set or update the rating for the photo.
22+
*
23+
* Handles:
24+
* - Creating new ratings (rating > 0, no existing rating)
25+
* - Updating existing ratings (rating > 0, existing rating)
26+
* - Removing ratings (rating == 0)
27+
* - Atomic statistics updates
28+
*
29+
* @param Photo $photo The photo to rate
30+
* @param User $user The user rating the photo
31+
* @param int $rating The rating value (0-5, where 0 removes the rating)
32+
*
33+
* @return Photo the photo with refreshed statistics
34+
*
35+
* @throws ConflictingPropertyException if a database conflict occurs during the transaction
36+
*/
37+
public function do(Photo $photo, User $user, int $rating): Photo
38+
{
39+
try {
40+
DB::transaction(function () use ($photo, $user, $rating): void {
41+
// Ensure statistics record exists atomically (Q001-07)
42+
$statistics = Statistics::firstOrCreate(
43+
['photo_id' => $photo->id],
44+
[
45+
'album_id' => null,
46+
'visit_count' => 0,
47+
'download_count' => 0,
48+
'favourite_count' => 0,
49+
'shared_count' => 0,
50+
'rating_sum' => 0,
51+
'rating_count' => 0,
52+
]
53+
);
54+
55+
if ($rating > 0) {
56+
// Find existing rating by this user for this photo
57+
$existing_rating = PhotoRating::where('photo_id', $photo->id)
58+
->where('user_id', $user->id)
59+
->first();
60+
61+
if ($existing_rating !== null) {
62+
// Update: adjust statistics delta
63+
$delta = $rating - $existing_rating->rating;
64+
$statistics->rating_sum += $delta;
65+
$existing_rating->rating = $rating;
66+
$existing_rating->save();
67+
} else {
68+
// Insert: create new rating and increment statistics
69+
PhotoRating::create([
70+
'photo_id' => $photo->id,
71+
'user_id' => $user->id,
72+
'rating' => $rating,
73+
]);
74+
$statistics->rating_sum += $rating;
75+
$statistics->rating_count++;
76+
}
77+
78+
$statistics->save();
79+
} else {
80+
// Rating == 0: remove rating (idempotent, Q001-06)
81+
$existing_rating = PhotoRating::where('photo_id', $photo->id)
82+
->where('user_id', $user->id)
83+
->first();
84+
85+
if ($existing_rating !== null) {
86+
$statistics->rating_sum -= $existing_rating->rating;
87+
$statistics->rating_count--;
88+
$statistics->save();
89+
$existing_rating->delete();
90+
}
91+
// If no existing rating, do nothing (idempotent)
92+
}
93+
});
94+
95+
// Reload photo with fresh statistics
96+
$photo->refresh();
97+
98+
return $photo;
99+
} catch (\Throwable $e) {
100+
throw new ConflictingPropertyException('Failed to update photo rating due to a conflict. Please try again.', $e);
101+
}
102+
}
103+
}

app/Http/Controllers/Gallery/PhotoController.php

Lines changed: 11 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use App\Actions\Import\FromUrl;
1212
use App\Actions\Photo\Delete;
1313
use App\Actions\Photo\MoveOrDuplicate;
14+
use App\Actions\Photo\Rating;
1415
use App\Actions\Photo\Rotate;
1516
use App\Constants\FileSystem;
1617
use App\Contracts\Models\AbstractAlbum;
@@ -38,9 +39,7 @@
3839
use App\Jobs\ExtractZip;
3940
use App\Jobs\ProcessImageJob;
4041
use App\Jobs\WatermarkerJob;
41-
use App\Models\PhotoRating;
4242
use App\Models\SizeVariant;
43-
use App\Models\Statistics;
4443
use App\Models\Tag;
4544
use App\Repositories\ConfigManager;
4645
use Illuminate\Routing\Controller;
@@ -174,83 +173,24 @@ public function star(SetPhotosStarredRequest $request): void
174173
* Set the rating for a photo.
175174
*
176175
* @param SetPhotoRatingRequest $request
176+
* @param Rating $rating
177177
*
178178
* @return PhotoResource
179179
*
180180
* @throws ConflictingPropertyException
181181
*/
182-
public function rate(SetPhotoRatingRequest $request): PhotoResource
182+
public function rate(SetPhotoRatingRequest $request, Rating $rating): PhotoResource
183183
{
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();
184+
/** @var \App\Models\User $user */
185+
$user = Auth::user();
245186

246-
// Reload photo with fresh statistics
247-
$photo->refresh();
187+
$photo = $rating->do(
188+
$request->photo(),
189+
$user,
190+
$request->rating()
191+
);
248192

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-
}
193+
return new PhotoResource($photo, null);
254194
}
255195

256196
/**

app/Http/Resources/Models/PhotoResource.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,10 @@ public function __construct(Photo $photo, ?AbstractAlbum $album)
114114

115115
// Load current user's rating if authenticated
116116
if (Auth::check()) {
117-
$userRating = $photo->ratings()
117+
$user_rating = $photo->ratings()
118118
->where('user_id', Auth::id())
119119
->first();
120-
$this->current_user_rating = $userRating?->rating;
120+
$this->current_user_rating = $user_rating?->rating;
121121
}
122122
}
123123

app/Models/PhotoRating.php

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
/**
1717
* App\Models\PhotoRating.
1818
*
19-
* @property int $id
20-
* @property string $photo_id
21-
* @property int $user_id
22-
* @property int $rating
19+
* @property int $id
20+
* @property string $photo_id
21+
* @property int $user_id
22+
* @property int $rating
2323
* @property \Illuminate\Support\Carbon $created_at
2424
* @property \Illuminate\Support\Carbon $updated_at
25-
* @property Photo $photo
26-
* @property User $user
25+
* @property Photo $photo
26+
* @property User $user
2727
*
2828
* @method static \Illuminate\Database\Eloquent\Builder|PhotoRating newModelQuery()
2929
* @method static \Illuminate\Database\Eloquent\Builder|PhotoRating newQuery()
@@ -37,10 +37,10 @@
3737
class PhotoRating extends Model
3838
{
3939
use ThrowsConsistentExceptions;
40-
/** @phpstan-use HasFactory<\Database\Factories\PhotoRatingFactory> */
4140
use HasFactory;
4241

43-
protected $table = 'photo_ratings';
42+
// protected $table = 'photo_ratings';
43+
public $timestamps = false;
4444

4545
protected $fillable = [
4646
'photo_id',
@@ -56,7 +56,7 @@ class PhotoRating extends Model
5656
/**
5757
* Get the photo that this rating belongs to.
5858
*
59-
* @return BelongsTo<Photo, PhotoRating>
59+
* @return BelongsTo<Photo,$this>
6060
*/
6161
public function photo(): BelongsTo
6262
{
@@ -66,7 +66,7 @@ public function photo(): BelongsTo
6666
/**
6767
* Get the user who created this rating.
6868
*
69-
* @return BelongsTo<User, PhotoRating>
69+
* @return BelongsTo<User,$this>
7070
*/
7171
public function user(): BelongsTo
7272
{

0 commit comments

Comments
 (0)