Passed
Push — master ( 4e74cd...0361f1 )
by Pauli
02:51
created

AlbumMapper::findAll()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 8
dl 0
loc 10
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php declare(strict_types=1);
2
3
/**
4
 * ownCloud - Music app
5
 *
6
 * This file is licensed under the Affero General Public License version 3 or
7
 * later. See the COPYING file.
8
 *
9
 * @author Morris Jobke <[email protected]>
10
 * @author Pauli Järvinen <[email protected]>
11
 * @copyright Morris Jobke 2013, 2014
12
 * @copyright Pauli Järvinen 2016 - 2021
13
 */
14
15
namespace OCA\Music\Db;
16
17
use \OCA\Music\Utility\Util;
18
19
use \OCP\AppFramework\Db\Entity;
20
use \OCP\IDBConnection;
21
22
class AlbumMapper extends BaseMapper {
23
	public function __construct(IDBConnection $db) {
24
		parent::__construct($db, 'music_albums', '\OCA\Music\Db\Album', 'name');
25
	}
26
27
	/**
28
	 * Override the base implementation to include data from multiple tables
29
	 *
30
	 * {@inheritdoc}
31
	 * @see BaseMapper::selectEntities()
32
	 */
33
	protected function selectEntities(string $condition, string $extension=null) : string {
34
		return "SELECT `*PREFIX*music_albums`.*, `artist`.`name` AS `album_artist_name`
35
				FROM `*PREFIX*music_albums`
36
				INNER JOIN `*PREFIX*music_artists` `artist`
37
				ON `*PREFIX*music_albums`.`album_artist_id` = `artist`.`id`
38
				WHERE $condition $extension";
39
	}
40
41
	/**
42
	 * Overridden from \OCA\Music\Db\BaseMapper to add support for sorting by artist.
43
	 *
44
	 * {@inheritdoc}
45
	 * @see BaseMapper::formatSortingClause()
46
	 */
47
	protected function formatSortingClause(int $sortBy) : ?string {
48
		if ($sortBy === SortBy::Parent) {
49
			return 'ORDER BY LOWER(`album_artist_name`), LOWER(`*PREFIX*music_albums`.`name`)';
50
		} else {
51
			return parent::formatSortingClause($sortBy);
52
		}
53
	}
54
55
	/**
56
	 * returns artist IDs mapped to album IDs
57
	 * does not include album_artist_id
58
	 *
59
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
60
	 * @param string $userId the user ID
61
	 * @return array int => int[], keys are albums IDs and values are arrays of artist IDs
62
	 */
63
	public function getPerformingArtistsByAlbumId(?array $albumIds, string $userId) : array {
64
		$sql = 'SELECT DISTINCT `track`.`album_id`, `track`.`artist_id`
65
				FROM `*PREFIX*music_tracks` `track`
66
				WHERE `track`.`user_id` = ? ';
67
		$params = [$userId];
68
69
		if ($albumIds !== null) {
0 ignored issues
show
introduced by
The condition $albumIds !== null is always true.
Loading history...
70
			$sql .= 'AND `track`.`album_id` IN ' . $this->questionMarks(\count($albumIds));
71
			$params = \array_merge($params, $albumIds);
72
		}
73
74
		$result = $this->execute($sql, $params);
75
		$artistIds = [];
76
		while ($row = $result->fetch()) {
77
			$artistIds[$row['album_id']][] = (int)$row['artist_id'];
78
		}
79
		return $artistIds;
80
	}
81
82
	/**
83
	 * returns release years mapped to album IDs
84
	 *
85
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
86
	 * @param string $userId the user ID
87
	 * @return array int => int[], keys are albums IDs and values are arrays of years
88
	 */
89
	public function getYearsByAlbumId(?array $albumIds, string $userId) : array {
90
		$sql = 'SELECT DISTINCT `track`.`album_id`, `track`.`year`
91
				FROM `*PREFIX*music_tracks` `track`
92
				WHERE `track`.`user_id` = ?
93
				AND `track`.`year` IS NOT NULL ';
94
		$params = [$userId];
95
96
		if ($albumIds !== null) {
0 ignored issues
show
introduced by
The condition $albumIds !== null is always true.
Loading history...
97
			$sql .= 'AND `track`.`album_id` IN ' . $this->questionMarks(\count($albumIds));
98
			$params = \array_merge($params, $albumIds);
99
		}
100
101
		$result = $this->execute($sql, $params);
102
		$years = [];
103
		while ($row = $result->fetch()) {
104
			$years[$row['album_id']][] = (int)$row['year'];
105
		}
106
		return $years;
107
	}
108
109
	/**
110
	 * returns genres mapped to album IDs
111
	 *
112
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
113
	 * @param string $userId the user ID
114
	 * @return array int => Genre[], keys are albums IDs and values are arrays of *partial* Genre objects (only id and name properties set)
115
	 */
116
	public function getGenresByAlbumId(?array $albumIds, string $userId) : array {
117
		$sql = 'SELECT DISTINCT `album_id`, `genre_id`, `*PREFIX*music_genres`.`name` AS `genre_name`
118
				FROM `*PREFIX*music_tracks`
119
				LEFT JOIN `*PREFIX*music_genres`
120
				ON `genre_id` = `*PREFIX*music_genres`.`id`
121
				WHERE `*PREFIX*music_tracks`.`user_id` = ?
122
				AND `genre_id` IS NOT NULL ';
123
		$params = [$userId];
124
125
		if ($albumIds !== null) {
0 ignored issues
show
introduced by
The condition $albumIds !== null is always true.
Loading history...
126
			$sql .= 'AND `album_id` IN ' . $this->questionMarks(\count($albumIds));
127
			$params = \array_merge($params, $albumIds);
128
		}
129
130
		$result = $this->execute($sql, $params);
131
		$genres = [];
132
		while ($row = $result->fetch()) {
133
			$genre = new Genre();
134
			$genre->setUserId($userId);
135
			$genre->setId((int)$row['genre_id']);
136
			$genre->setName($row['genre_name']);
137
			$genres[$row['album_id']][] = $genre;
138
		}
139
		return $genres;
140
	}
141
142
	/**
143
	 * returns number of disks per album ID
144
	 *
145
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
146
	 * @param string $userId the user ID
147
	 * @return array int => int, keys are albums IDs and values are disk counts
148
	 */
149
	public function getDiscCountByAlbumId(?array $albumIds, string $userId) : array {
150
		$sql = 'SELECT `album_id`, MAX(`disk`) AS `disc_count`
151
				FROM `*PREFIX*music_tracks`
152
				WHERE `user_id` = ?
153
				GROUP BY `album_id` ';
154
		$params = [$userId];
155
156
		if ($albumIds !== null) {
0 ignored issues
show
introduced by
The condition $albumIds !== null is always true.
Loading history...
157
			$sql .= 'HAVING `album_id` IN ' . $this->questionMarks(\count($albumIds));
158
			$params = \array_merge($params, $albumIds);
159
		}
160
161
		$result = $this->execute($sql, $params);
162
		$diskCountByAlbum = [];
163
		while ($row = $result->fetch()) {
164
			$diskCountByAlbum[$row['album_id']] = (int)$row['disc_count'];
165
		}
166
		return $diskCountByAlbum;
167
	}
168
169
	/**
170
	 * returns albums of a specified artist
171
	 * The artist may be an album_artist or the artist of a track
172
	 *
173
	 * @param integer $artistId ID of the artist
174
	 * @param string $userId the user ID
175
	 * @return Album[]
176
	 */
177
	public function findAllByArtist(int $artistId, string $userId) : array {
178
		$sql = $this->selectEntities(
179
				'`*PREFIX*music_albums`.`id` IN (
180
					SELECT DISTINCT `album`.`id`
181
					FROM `*PREFIX*music_albums` `album`
182
					WHERE `album`.`album_artist_id` = ? AND `album`.`user_id` = ?
183
						UNION
184
					SELECT DISTINCT `track`.`album_id`
185
					FROM `*PREFIX*music_tracks` `track`
186
					WHERE `track`.`artist_id` = ? AND `track`.`user_id` = ?
187
				)',
188
				'ORDER BY LOWER(`*PREFIX*music_albums`.`name`)');
189
		$params = [$artistId, $userId, $artistId, $userId];
190
		return $this->findEntities($sql, $params);
191
	}
192
193
	/**
194
	 * returns albums of a specified artist
195
	 * The artist must album_artist on the album, artists of individual tracks are not considered
196
	 *
197
	 * @param integer $artistId ID of the artist
198
	 * @param string $userId the user ID
199
	 * @return Album[]
200
	 */
201
	public function findAllByAlbumArtist(int $artistId, string $userId) : array {
202
		$sql = $this->selectUserEntities('`album_artist_id` = ?');
203
		$params = [$userId, $artistId];
204
		return $this->findEntities($sql, $params);
205
	}
206
207
	/**
208
	 * @return Album[]
209
	 */
210
	public function findAllByGenre(int $genreId, string $userId, int $limit=null, int $offset=null) : array {
211
		$sql = $this->selectUserEntities('EXISTS '.
212
				'(SELECT 1 FROM `*PREFIX*music_tracks` `track`
213
				  WHERE `*PREFIX*music_albums`.`id` = `track`.`album_id`
214
				  AND `track`.`genre_id` = ?)');
215
216
		$params = [$userId, $genreId];
217
		return $this->findEntities($sql, $params, $limit, $offset);
218
	}
219
220
	/**
221
	 * @return boolean True if one or more albums were influenced
222
	 */
223
	public function updateFolderCover(int $coverFileId, int $folderId) : bool {
224
		$sql = 'SELECT DISTINCT `tracks`.`album_id`
225
				FROM `*PREFIX*music_tracks` `tracks`
226
				JOIN `*PREFIX*filecache` `files` ON `tracks`.`file_id` = `files`.`fileid`
227
				WHERE `files`.`parent` = ?';
228
		$params = [$folderId];
229
		$result = $this->execute($sql, $params);
230
231
		$updated = false;
232
		if ($result->rowCount()) {
233
			$sql = 'UPDATE `*PREFIX*music_albums`
234
					SET `cover_file_id` = ?
235
					WHERE `cover_file_id` IS NULL AND `id` IN (?)';
236
			$params = [$coverFileId, \join(",", $result->fetchAll(\PDO::FETCH_COLUMN))];
237
			$result = $this->execute($sql, $params);
238
			$updated = $result->rowCount() > 0;
239
		}
240
241
		return $updated;
242
	}
243
244
	/**
245
	 * Set file ID to be used as cover for an album
246
	 */
247
	public function setCover(?int $coverFileId, int $albumId) : void {
248
		$sql = 'UPDATE `*PREFIX*music_albums`
249
				SET `cover_file_id` = ?
250
				WHERE `id` = ?';
251
		$params = [$coverFileId, $albumId];
252
		$this->execute($sql, $params);
253
	}
254
255
	/**
256
	 * @param integer[] $coverFileIds
257
	 * @param string[]|null $userIds the users whose music library is targeted; all users are targeted if omitted
258
	 * @return Album[] albums which got modified (with incomplete data, only id and user are valid),
259
	 *         empty array if none
260
	 */
261
	public function removeCovers(array $coverFileIds, array $userIds=null) : array {
262
		// find albums using the given file as cover
263
		$sql = 'SELECT `id`, `user_id` FROM `*PREFIX*music_albums` WHERE `cover_file_id` IN ' .
264
			$this->questionMarks(\count($coverFileIds));
265
		$params = $coverFileIds;
266
		if ($userIds !== null) {
267
			$sql .= ' AND `user_id` IN ' . $this->questionMarks(\count($userIds));
268
			$params = \array_merge($params, $userIds);
269
		}
270
		$albums = $this->findEntities($sql, $params);
271
272
		// if any albums found, remove the cover from those
273
		$count = \count($albums);
274
		if ($count) {
275
			$sql = 'UPDATE `*PREFIX*music_albums`
276
				SET `cover_file_id` = NULL
277
				WHERE `id` IN ' . $this->questionMarks($count);
278
			$params = Util::extractIds($albums);
279
			$this->execute($sql, $params);
280
		}
281
282
		return $albums;
283
	}
284
285
	/**
286
	 * @param string|null $userId target user; omit to target all users
287
	 * @return array of dictionaries with keys [albumId, userId, parentFolderId]
288
	 */
289
	public function getAlbumsWithoutCover(string $userId = null) : array {
290
		$sql = 'SELECT DISTINCT `albums`.`id`, `albums`.`user_id`, `files`.`parent`
291
				FROM `*PREFIX*music_albums` `albums`
292
				JOIN `*PREFIX*music_tracks` `tracks` ON `albums`.`id` = `tracks`.`album_id`
293
				JOIN `*PREFIX*filecache` `files` ON `tracks`.`file_id` = `files`.`fileid`
294
				WHERE `albums`.`cover_file_id` IS NULL';
295
		$params = [];
296
		if ($userId !== null) {
297
			$sql .= ' AND `albums`.`user_id` = ?';
298
			$params[] = $userId;
299
		}
300
		$result = $this->execute($sql, $params);
301
		$return = [];
302
		while ($row = $result->fetch()) {
303
			$return[] = [
304
				'albumId' => (int)$row['id'],
305
				'userId' => $row['user_id'],
306
				'parentFolderId' => (int)$row['parent']
307
			];
308
		}
309
		return $return;
310
	}
311
312
	/**
313
	 * @return boolean True if a cover image was found and added for the album
314
	 */
315
	public function findAlbumCover(int $albumId, int $parentFolderId) : bool {
316
		$return = false;
317
		$coverNames = ['cover', 'albumart', 'front', 'folder'];
318
		$imagesSql = 'SELECT `fileid`, `name`
319
					FROM `*PREFIX*filecache`
320
					JOIN `*PREFIX*mimetypes` ON `*PREFIX*mimetypes`.`id` = `*PREFIX*filecache`.`mimetype`
321
					WHERE `parent` = ? AND `*PREFIX*mimetypes`.`mimetype` LIKE \'image%\'';
322
		$params = [$parentFolderId];
323
		$result = $this->execute($imagesSql, $params);
324
		$images = $result->fetchAll();
325
		if (\count($images)) {
326
			\usort($images, function ($imageA, $imageB) use ($coverNames) {
327
				$nameA = \strtolower($imageA['name']);
328
				$nameB = \strtolower($imageB['name']);
329
				$indexA = PHP_INT_MAX;
330
				$indexB = PHP_INT_MAX;
331
				foreach ($coverNames as $i => $coverName) {
332
					if ($indexA === PHP_INT_MAX && \strpos($nameA, $coverName) === 0) {
333
						$indexA = $i;
334
					}
335
					if ($indexB === PHP_INT_MAX && \strpos($nameB, $coverName) === 0) {
336
						$indexB = $i;
337
					}
338
					if ($indexA !== PHP_INT_MAX  && $indexB !== PHP_INT_MAX) {
339
						break;
340
					}
341
				}
342
				return $indexA > $indexB;
343
			});
344
			$imageId = (int)$images[0]['fileid'];
345
			$this->setCover($imageId, $albumId);
346
			$return = true;
347
		}
348
		return $return;
349
	}
350
351
	/**
352
	 * Returns the count of albums where the given Artist is featured in
353
	 * @param integer $artistId
354
	 * @return integer
355
	 */
356
	public function countByArtist(int $artistId) : int {
357
		$sql = 'SELECT COUNT(*) AS count FROM (
358
					SELECT DISTINCT `track`.`album_id`
359
					FROM `*PREFIX*music_tracks` `track`
360
					WHERE `track`.`artist_id` = ?
361
						UNION
362
					SELECT `album`.`id`
363
					FROM `*PREFIX*music_albums` `album`
364
					WHERE `album`.`album_artist_id` = ?
365
				) tmp';
366
		$params = [$artistId, $artistId];
367
		$result = $this->execute($sql, $params);
368
		$row = $result->fetch();
369
		return (int)$row['count'];
370
	}
371
372
	/**
373
	 * Returns the count of albums where the given artist is the album artist
374
	 * @param integer $artistId
375
	 * @return integer
376
	 */
377
	public function countByAlbumArtist(int $artistId) : int {
378
		$sql = 'SELECT COUNT(*) AS count
379
				FROM `*PREFIX*music_albums` `album`
380
				WHERE `album`.`album_artist_id` = ?';
381
		$params = [$artistId];
382
		$result = $this->execute($sql, $params);
383
		$row = $result->fetch();
384
		return (int)$row['count'];
385
	}
386
387
	/**
388
	 * @see \OCA\Music\Db\BaseMapper::findUniqueEntity()
389
	 * @param Album $album
390
	 * @return Album
391
	 */
392
	protected function findUniqueEntity(Entity $album) : Entity {
393
		$sql = $this->selectUserEntities('`*PREFIX*music_albums`.`hash` = ?');
394
		return $this->findEntity($sql, [$album->getUserId(), $album->getHash()]);
395
	}
396
}
397