Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/Relations/HasManyPhotosByTag.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ public function addEagerConstraints(array $albums): void
)
->where(fn (Builder $q) => $this->getPhotoIdsWithTags($q, $tag_ids, $album->is_and));
}

// The LEFT JOIN with photo_album (added by applySearchabilityFilter/applySensitivityFilter
// for access control checks) produces one row per album membership. Since a photo can
// belong to multiple regular albums, this causes duplicate rows in the result. Adding
// DISTINCT ensures each photo appears only once.
$this->getRelationQuery()->distinct();
}

/**
Expand Down
27 changes: 27 additions & 0 deletions tests/Feature_v2/Album/AlbumPhotosEndpointTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

namespace Tests\Feature_v2\Album;

use App\Models\Album;
use Tests\Feature_v2\Base\BaseApiWithDataTest;

/**
Expand Down Expand Up @@ -125,4 +126,30 @@ public function testGetAlbumPhotosMissingAlbumId(): void
'message' => 'The album id field is required.',
]);
}

public function testTagAlbumPhotosNoDuplicatesWhenPhotoInMultipleAlbums(): void
{
// Regression test for: tag album photo listing returns duplicate entries.
// When a photo belongs to multiple regular albums, the LEFT JOIN with
// photo_album in applySearchabilityFilter produces one row per album
// membership, causing duplicate photos in the tag album response.

$this->actingAs($this->userMayUpload1);

// photo1 is already in album1 and has the 'test' tag.
// tagAlbum1 is linked to the 'test' tag.
// Add photo1 to a second album to trigger the duplicate via the photo_album JOIN.
$extraAlbum = Album::factory()->as_root()->owned_by($this->userMayUpload1)->create();
$this->photo1->albums()->attach($extraAlbum->id);

$response = $this->getJsonWithData('Album::photos', ['album_id' => $this->tagAlbum1->id]);
$this->assertOk($response);

$photos = $response->json('photos');
$photoIds = array_column($photos, 'id');

// Each photo must appear exactly once.
$this->assertSame(count(array_unique($photoIds)), count($photoIds), 'Tag album must not return duplicate photos');
$this->assertContains($this->photo1->id, $photoIds);
}
}
Loading