f7cloud_client/apps/photos/lib/Album/AlbumMapper.php
root 8b6a0139db f7cloud_client
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 22:59:26 +00:00

602 lines
24 KiB
PHP

<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2022 F7cloud GmbH and F7cloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Photos\Album;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OCA\Photos\Exception\AlreadyInAlbumException;
use OCA\Photos\Filters\FiltersManager;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\IMimeTypeLoader;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IL10N;
use OCP\IUserManager;
use OCP\Security\ISecureRandom;
class AlbumMapper {
// Same mapping as IShare.
public const TYPE_USER = 0;
public const TYPE_GROUP = 1;
public const TYPE_LINK = 3;
public function __construct(
private readonly IDBConnection $connection,
private readonly IMimeTypeLoader $mimeTypeLoader,
private readonly ITimeFactory $timeFactory,
private readonly IUserManager $userManager,
private readonly IGroupManager $groupManager,
private readonly IL10N $l,
private readonly ISecureRandom $random,
private readonly FiltersManager $filtersManager,
) {
}
public function create(string $userId, string $name, string $location = '', ?string $filters = null): AlbumInfo {
$created = $this->timeFactory->getTime();
$query = $this->connection->getQueryBuilder();
$query->insert('photos_albums')
->values([
'user' => $query->createNamedParameter($userId),
'name' => $query->createNamedParameter($name),
'location' => $query->createNamedParameter($location),
'created' => $query->createNamedParameter($created, IQueryBuilder::PARAM_INT),
'last_added_photo' => $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT),
'filters' => $query->createNamedParameter($filters, IQueryBuilder::PARAM_STR),
]);
$query->executeStatement();
$id = $query->getLastInsertId();
return new AlbumInfo($id, $userId, $name, $location, $created, -1, $filters);
}
public function get(int $id): ?AlbumInfo {
$query = $this->connection->getQueryBuilder();
$query->select('name', 'user', 'location', 'created', 'last_added_photo', 'filters')
->from('photos_albums')
->where($query->expr()->eq('album_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
$row = $query->executeQuery()->fetch();
if ($row) {
return new AlbumInfo($id, $row['user'], $row['name'], $row['location'], (int)$row['created'], (int)$row['last_added_photo'], $row['filters']);
} else {
return null;
}
}
/**
* @return AlbumInfo[]
*/
public function getForUser(string $userId): array {
$query = $this->connection->getQueryBuilder();
$query->select('album_id', 'name', 'location', 'created', 'last_added_photo', 'filters')
->from('photos_albums')
->where($query->expr()->eq('user', $query->createNamedParameter($userId)));
$rows = $query->executeQuery()->fetchAll();
return array_map(fn (array $row): AlbumInfo => new AlbumInfo((int)$row['album_id'], $userId, $row['name'], $row['location'], (int)$row['created'], (int)$row['last_added_photo'], $row['filters']), $rows);
}
/**
* @return AlbumInfo
*/
public function getByName(string $albumName, string $userName): ?AlbumInfo {
$query = $this->connection->getQueryBuilder();
$query->select('album_id', 'location', 'created', 'last_added_photo', 'filters')
->from('photos_albums')
->where($query->expr()->eq('name', $query->createNamedParameter($albumName)))
->andWhere($query->expr()->eq('user', $query->createNamedParameter($userName)));
$row = $query->executeQuery()->fetch();
if ($row) {
return new AlbumInfo((int)$row['album_id'], $userName, $albumName, $row['location'], (int)$row['created'], (int)$row['last_added_photo'], $row['filters']);
} else {
return null;
}
}
/**
* @return AlbumInfo[]
*/
public function getForFile(int $fileId): array {
$query = $this->connection->getQueryBuilder();
$query->select('a.album_id', 'name', 'user', 'location', 'created', 'last_added_photo', 'filters')
->from('photos_albums', 'a')
->leftJoin('a', 'photos_albums_files', 'p', $query->expr()->eq('a.album_id', 'p.album_id'))
->where($query->expr()->eq('file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
$rows = $query->executeQuery()->fetchAll();
return array_map(fn (array $row): AlbumInfo => new AlbumInfo((int)$row['album_id'], $row['user'], $row['name'], $row['location'], (int)$row['created'], (int)$row['last_added_photo'], $row['filters']), $rows);
}
/**
* @return AlbumInfo[]
*/
public function getForUserAndFile(string $userId, int $fileId): array {
$query = $this->connection->getQueryBuilder();
$query->select('a.album_id', 'name', 'user', 'location', 'created', 'last_added_photo', 'filters')
->from('photos_albums', 'a')
->leftJoin('a', 'photos_albums_files', 'p', $query->expr()->eq('a.album_id', 'p.album_id'))
->where($query->expr()->eq('user', $query->createNamedParameter($userId)))
->andWhere($query->expr()->eq('file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
$rows = $query->executeQuery()->fetchAll();
return array_map(fn (array $row): AlbumInfo => new AlbumInfo((int)$row['album_id'], $row['user'], $row['name'], $row['location'], (int)$row['created'], (int)$row['last_added_photo'], $row['filters']), $rows);
}
public function rename(int $id, string $newName): void {
$query = $this->connection->getQueryBuilder();
$query->update('photos_albums')
->set('name', $query->createNamedParameter($newName))
->where($query->expr()->eq('album_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
$query->executeStatement();
}
public function setLocation(int $id, string $newLocation): void {
$query = $this->connection->getQueryBuilder();
$query->update('photos_albums')
->set('location', $query->createNamedParameter($newLocation))
->where($query->expr()->eq('album_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
$query->executeStatement();
}
public function delete(int $id): void {
$this->connection->beginTransaction();
$query = $this->connection->getQueryBuilder();
$query->delete('photos_albums')
->where($query->expr()->eq('album_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
$query->executeStatement();
$query = $this->connection->getQueryBuilder();
$query->delete('photos_albums_files')
->where($query->expr()->eq('album_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
$query->executeStatement();
$query = $this->connection->getQueryBuilder();
$query->delete('photos_albums_collabs')
->where($query->expr()->eq('album_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
$query->executeStatement();
$this->connection->commit();
}
/**
* @return AlbumFile[]
*/
public function getForAlbumIdAndUserWithFiles(int $albumId, string $userId, array $filters): array {
$query = $this->connection->getQueryBuilder();
$query->select('fileid', 'mimetype', 'a.album_id', 'size', 'mtime', 'etag', 'added', 'owner')
->selectAlias('f.name', 'file_name')
->selectAlias('a.name', 'album_name')
->from('photos_albums', 'a')
->leftJoin('a', 'photos_albums_files', 'p', $query->expr()->eq('a.album_id', 'p.album_id'))
->leftJoin('p', 'filecache', 'f', $query->expr()->eq('p.file_id', 'f.fileid'))
->where($query->expr()->eq('a.album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->eq('user', $query->createNamedParameter($userId)));
$rows = $query->executeQuery()->fetchAll();
$files = [];
foreach ($rows as $row) {
if ($row['fileid']) {
$mimeType = $this->mimeTypeLoader->getMimetypeById((int)$row['mimetype']);
$files[] = new AlbumFile((int)$row['fileid'], $row['file_name'], $mimeType, (int)$row['size'], (int)$row['mtime'], $row['etag'], (int)$row['added'], $row['owner'], 'user');
}
}
$fileIds = array_map(fn ($file) => $file->getFileId(), $files);
$smartAlbumFiles = array_filter(
$this->filtersManager->getFilesBasedOnFilters($userId, $filters),
fn ($file) => array_search($file->getFileId(), $fileIds) === false,
);
return [...$files, ...$smartAlbumFiles];
}
public function getForAlbumIdAndFileId(int $albumId, int $fileId): ?AlbumFile {
$query = $this->connection->getQueryBuilder();
$query->select('fileid', 'mimetype', 'a.album_id', 'user', 'size', 'mtime', 'etag', 'location', 'created', 'last_added_photo', 'added', 'owner')
->selectAlias('f.name', 'file_name')
->selectAlias('a.name', 'album_name')
->from('photos_albums', 'a')
->leftJoin('a', 'photos_albums_files', 'p', $query->expr()->eq('a.album_id', 'p.album_id'))
->leftJoin('p', 'filecache', 'f', $query->expr()->eq('p.file_id', 'f.fileid'))
->where($query->expr()->eq('a.album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->eq('file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
$row = $query->executeQuery()->fetchAll()[0];
if ($row === null) {
return null;
}
$mimeType = $this->mimeTypeLoader->getMimetypeById((int)$row['mimetype']);
return new AlbumFile((int)$row['fileid'], $row['file_name'], $mimeType, (int)$row['size'], (int)$row['mtime'], $row['etag'], (int)$row['added'], $row['owner'], 'user');
}
public function addFile(int $albumId, int $fileId, string $owner): void {
$added = $this->timeFactory->getTime();
try {
$query = $this->connection->getQueryBuilder();
$query->insert('photos_albums_files')
->values([
'album_id' => $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT),
'file_id' => $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT),
'added' => $query->createNamedParameter($added, IQueryBuilder::PARAM_INT),
'owner' => $query->createNamedParameter($owner),
]);
$query->executeStatement();
} catch (UniqueConstraintViolationException $e) {
throw new AlreadyInAlbumException('File already in album', 0, $e);
}
$query = $this->connection->getQueryBuilder();
$query->update('photos_albums')
->set('last_added_photo', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))
->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)));
$query->executeStatement();
}
public function removeFile(int $albumId, int $fileId): void {
$query = $this->connection->getQueryBuilder();
$query->delete('photos_albums_files')
->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->eq('file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
$query->executeStatement();
$query = $this->connection->getQueryBuilder();
$query->update('photos_albums')
->set('last_added_photo', $query->createNamedParameter($this->getLastAdded($albumId), IQueryBuilder::PARAM_INT))
->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)));
$query->executeStatement();
}
/**
* Remove all files added by a user from an album.
*/
public function removeFilesForUser(int $albumId, string $userId) {
// Remove all photos by this user from the album:
$query = $this->connection->getQueryBuilder();
$query->delete('photos_albums_files')
->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->eq('owner', $query->createNamedParameter($userId)))
->executeStatement();
// Update the last added photo:
$query = $this->connection->getQueryBuilder();
$query->update('photos_albums')
->set('last_added_photo', $query->createNamedParameter($this->getLastAdded($albumId), IQueryBuilder::PARAM_INT))
->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)))
->executeStatement();
}
/**
* Remove a given file from any albums in which it was added by a given user.
*/
public function removeFileWithOwner(int $fileId, string $ownerId): void {
// Get concerned albums before deleting them.
$query = $this->connection->getQueryBuilder();
$albumsRows = $query->select('album_id')
->from('photos_albums_files')
->where($query->expr()->eq('owner', $query->createNamedParameter($ownerId)))
->andWhere($query->expr()->eq('file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)))
->executeQuery()
->fetchAll();
// Remove any occurrence of fileId when owner is ownerId.
$query = $this->connection->getQueryBuilder();
$query->delete('photos_albums_files')
->where($query->expr()->eq('owner', $query->createNamedParameter($ownerId)))
->andWhere($query->expr()->eq('file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)))
->executeStatement();
// Update last_added_photo for concerned albums.
foreach ($albumsRows as $row) {
$query = $this->connection->getQueryBuilder();
$query->update('photos_albums')
->set('last_added_photo', $query->createNamedParameter($this->getLastAdded($row['album_id']), IQueryBuilder::PARAM_INT))
->where($query->expr()->eq('album_id', $query->createNamedParameter($row['album_id'], IQueryBuilder::PARAM_INT)));
$query->executeStatement();
}
}
private function getLastAdded(int $albumId): int {
$query = $this->connection->getQueryBuilder();
$query->select('file_id')
->from('photos_albums_files')
->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)))
->orderBy('added', 'DESC')
->setMaxResults(1);
$id = $query->executeQuery()->fetchOne();
if ($id === false) {
return -1;
} else {
return (int)$id;
}
}
/**
* @return array<array{'id': string, 'label': string, 'type': int}>
*/
public function getCollaborators(int $albumId): array {
$query = $this->connection->getQueryBuilder();
$query->select('collaborator_id', 'collaborator_type')
->from('photos_albums_collabs')
->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)));
$rows = $query->executeQuery()->fetchAll();
$collaborators = array_map(function (array $row) {
/** @var string|null */
$displayName = null;
$displayName = match ($row['collaborator_type']) {
self::TYPE_USER => $this->userManager->get($row['collaborator_id'])?->getDisplayName(),
self::TYPE_GROUP => $this->groupManager->get($row['collaborator_id'])?->getDisplayName(),
self::TYPE_LINK => $this->l->t('Public link'),
default => throw new \Exception('Invalid collaborator type: ' . $row['collaborator_type']),
};
if (is_null($displayName)) {
return null;
}
return [
'id' => $row['collaborator_id'],
'label' => $displayName,
'type' => (int)$row['collaborator_type'],
];
}, $rows);
return array_values(array_filter($collaborators, fn ($c): bool => $c !== null));
}
public function isCollaborator(int $albumId, string $userId): bool {
$query = $this->connection->getQueryBuilder();
$query->select('collaborator_id', 'collaborator_type')
->from('photos_albums_collabs')
->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)));
$rows = $query->executeQuery()->fetchAll();
foreach ($rows as $row) {
switch ($row['collaborator_type']) {
case self::TYPE_USER:
if ($row['collaborator_id'] === $userId) {
return true;
}
break;
case self::TYPE_GROUP:
if ($this->groupManager->isInGroup($userId, $row['collaborator_id'])) {
return true;
}
break;
default:
break;
}
}
return false;
}
/**
* @param array{'id': string, 'type': int} $collaborators
*/
public function setCollaborators(int $albumId, array $collaborators): void {
$existingCollaborators = $this->getCollaborators($albumId);
// Different behavior if type is link to prevent creating multiple link.
$computeKey = (fn ($c): string => ($c['type'] === AlbumMapper::TYPE_LINK ? '' : $c['id']) . $c['type']);
$collaboratorsToAdd = array_udiff($collaborators, $existingCollaborators, fn ($a, $b): int => strcmp($computeKey($a), $computeKey($b)));
$collaboratorsToRemove = array_udiff($existingCollaborators, $collaborators, fn ($a, $b): int => strcmp($computeKey($a), $computeKey($b)));
$this->connection->beginTransaction();
foreach ($collaboratorsToAdd as $collaborator) {
switch ($collaborator['type']) {
case self::TYPE_USER:
if (is_null($this->userManager->get($collaborator['id']))) {
throw new \Exception('Unknown collaborator: ' . $collaborator['id']);
}
break;
case self::TYPE_GROUP:
if (is_null($this->groupManager->get($collaborator['id']))) {
throw new \Exception('Unknown collaborator: ' . $collaborator['id']);
}
break;
case self::TYPE_LINK:
$collaborator['id'] = $this->random->generate(32, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
break;
default:
throw new \Exception('Invalid collaborator type: ' . $collaborator['type']);
}
$query = $this->connection->getQueryBuilder();
$query->insert('photos_albums_collabs')
->setValue('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT))
->setValue('collaborator_id', $query->createNamedParameter($collaborator['id']))
->setValue('collaborator_type', $query->createNamedParameter($collaborator['type'], IQueryBuilder::PARAM_INT))
->executeStatement();
}
foreach ($collaboratorsToRemove as $collaborator) {
switch ($collaborator['type']) {
case self::TYPE_USER:
$this->deleteUserFromAlbumCollaboratorsList($collaborator['id'], $albumId);
break;
default:
$query = $this->connection->getQueryBuilder();
$query->delete('photos_albums_collabs')
->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->eq('collaborator_id', $query->createNamedParameter($collaborator['id'])))
->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter($collaborator['type'], IQueryBuilder::PARAM_INT)))
->executeStatement();
}
}
$this->connection->commit();
}
/**
* @return AlbumInfo[]
*/
public function getSharedAlbumsForCollaborator(string $collaboratorId, int $collaboratorType): array {
$query = $this->connection->getQueryBuilder();
$rows = $query
->select('a.album_id', 'name', 'user', 'location', 'created', 'last_added_photo', 'filters')
->from('photos_albums_collabs', 'c')
->leftJoin('c', 'photos_albums', 'a', $query->expr()->eq('a.album_id', 'c.album_id'))
->where($query->expr()->eq('collaborator_id', $query->createNamedParameter($collaboratorId)))
->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter($collaboratorType, IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->isNotNull('a.album_id'))
->executeQuery()
->fetchAll();
return array_map(fn (array $row): AlbumInfo => new AlbumInfo(
(int)$row['album_id'],
$row['user'],
$row['name'] . ' (' . $row['user'] . ')',
$row['location'],
(int)$row['created'],
(int)$row['last_added_photo'],
$row['filters'],
), $rows);
}
/**
* @return AlbumWithFiles[]
*/
public function getSharedAlbumsForCollaboratorWithFiles(string $collaboratorId, int $collaboratorType): array {
$query = $this->connection->getQueryBuilder();
$rows = $query
->select('fileid', 'mimetype', 'a.album_id', 'size', 'mtime', 'etag', 'location', 'created', 'last_added_photo', 'added', 'owner', 'filters')
->selectAlias('f.name', 'file_name')
->selectAlias('a.name', 'album_name')
->selectAlias('a.user', 'album_user')
->from('photos_albums_collabs', 'c')
->leftJoin('c', 'photos_albums', 'a', $query->expr()->eq('a.album_id', 'c.album_id'))
->leftJoin('a', 'photos_albums_files', 'p', $query->expr()->eq('a.album_id', 'p.album_id'))
->leftJoin('p', 'filecache', 'f', $query->expr()->eq('p.file_id', 'f.fileid'))
->where($query->expr()->eq('collaborator_id', $query->createNamedParameter($collaboratorId)))
->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter($collaboratorType, IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->isNotNull('a.album_id'))
->executeQuery()
->fetchAll();
$filesByAlbum = [];
$albumsById = [];
foreach ($rows as $row) {
$albumId = (int)$row['album_id'];
if ($row['fileid']) {
$mimeType = $this->mimeTypeLoader->getMimetypeById((int)$row['mimetype']);
$filesByAlbum[$albumId][] = new AlbumFile(
(int)$row['fileid'],
$row['file_name'],
$mimeType,
(int)$row['size'],
(int)$row['mtime'],
$row['etag'],
(int)$row['added'],
$row['owner'],
'user',
);
}
if (!isset($albumsById[$albumId])) {
$albumName = $row['album_name'];
// Suffix album name with the album owner to prevent duplicates.
// Not done for public link as it would leak the owner's uid.
if ($collaboratorType !== self::TYPE_LINK) {
$albumName = $row['album_name'] . ' (' . $row['album_user'] . ')';
}
$albumsById[$albumId] = new AlbumInfo(
$albumId,
$row['album_user'],
$albumName,
$row['location'],
(int)$row['created'],
(int)$row['last_added_photo'],
$row['filters'],
$collaboratorType,
);
}
}
$result = [];
foreach ($albumsById as $id => $album) {
$filesByAlbum[$id] ??= [];
$fileIds = array_map(fn ($file) => $file->getFileId(), $filesByAlbum[$id]);
$smartAlbumFiles = array_filter(
$this->filtersManager->getFilesBasedOnFilters($album->getUserId(), $album->getDecodedFilters()),
fn ($file) => array_search($file->getFileId(), $fileIds) === false,
);
$result[] = new AlbumWithFiles($album, $this, [...$filesByAlbum[$id], ...$smartAlbumFiles]);
}
return $result;
}
public function deleteUserFromAlbumCollaboratorsList(string $userId, int $albumId): void {
$query = $this->connection->getQueryBuilder();
$query->delete('photos_albums_collabs')
->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->eq('collaborator_id', $query->createNamedParameter($userId)))
->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter(self::TYPE_USER, IQueryBuilder::PARAM_INT)))
->executeStatement();
// Remove all photos by this user from the album:
$this->removeFilesForUser($albumId, $userId);
}
public function deleteGroupFromAlbumCollaboratorsList(string $groupId, int $albumId): void {
$query = $this->connection->getQueryBuilder();
$query->delete('photos_albums_collabs')
->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->eq('collaborator_id', $query->createNamedParameter($groupId)))
->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter(self::TYPE_GROUP, IQueryBuilder::PARAM_INT)))
->executeStatement();
}
/**
* @return AlbumInfo[]
*/
public function getAlbumsForCollaboratorIdAndFileId(string $collaboratorId, int $collaboratorType, int $fileId): array {
$query = $this->connection->getQueryBuilder();
$rows = $query
->select('a.album_id', 'name', 'user', 'location', 'created', 'last_added_photo', 'filters')
->from('photos_albums_collabs', 'c')
->leftJoin('c', 'photos_albums', 'a', $query->expr()->eq('a.album_id', 'c.album_id'))
->leftJoin('a', 'photos_albums_files', 'p', $query->expr()->eq('a.album_id', 'p.album_id'))
->where($query->expr()->eq('collaborator_id', $query->createNamedParameter($collaboratorId)))
->andWhere($query->expr()->eq('collaborator_type', $query->createNamedParameter($collaboratorType, IQueryBuilder::PARAM_INT)))
->andWhere($query->expr()->eq('file_id', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)))
->groupBy('a.album_id')
->executeQuery()
->fetchAll();
return array_map(fn (array $row): AlbumInfo => new AlbumInfo(
(int)$row['album_id'],
$row['user'],
$row['name'] . ' (' . $row['user'] . ')',
$row['location'],
(int)$row['created'],
(int)$row['last_added_photo'],
$row['filters'],
), $rows);
}
public function setAlbumFilters(int $albumId, string $filters): void {
$query = $this->connection->getQueryBuilder();
$query->update('photos_albums')
->set('filters', $query->createNamedParameter($filters))
->where($query->expr()->eq('album_id', $query->createNamedParameter($albumId, IQueryBuilder::PARAM_STR)));
$query->executeStatement();
}
}