Passed
Pull Request — master (#1078)
by Pauli
05:55 queued 02:58
created

AlbumMapper::advFormatSqlCondition()   D

Complexity

Conditions 28
Paths 28

Size

Total Lines 31
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 28
eloc 28
nc 28
nop 2
dl 0
loc 31
rs 4.1666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 - 2023
13
 */
14
15
namespace OCA\Music\Db;
16
17
use OCA\Music\Utility\Util;
18
19
use OCP\IConfig;
20
use OCP\IDBConnection;
21
22
/**
23
 * Type hint a base class methdo to help Scrutinizer
24
 * @method Album updateOrInsert(Album $album)
25
 * @phpstan-extends BaseMapper<Album>
26
 */
27
class AlbumMapper extends BaseMapper {
28
	public function __construct(IDBConnection $db, IConfig $config) {
29
		parent::__construct($db, $config, 'music_albums', Album::class, 'name', 'album_artist_id');
30
	}
31
32
	/**
33
	 * Override the base implementation to include data from multiple tables
34
	 *
35
	 * {@inheritdoc}
36
	 * @see BaseMapper::selectEntities()
37
	 */
38
	protected function selectEntities(string $condition, string $extension=null) : string {
39
		return "SELECT `*PREFIX*music_albums`.*, `artist`.`name` AS `album_artist_name`
40
				FROM `*PREFIX*music_albums`
41
				INNER JOIN `*PREFIX*music_artists` `artist`
42
				ON `*PREFIX*music_albums`.`album_artist_id` = `artist`.`id`
43
				WHERE $condition $extension";
44
	}
45
46
	/**
47
	 * Overridden from \OCA\Music\Db\BaseMapper to add support for sorting by artist.
48
	 *
49
	 * {@inheritdoc}
50
	 * @see BaseMapper::formatSortingClause()
51
	 */
52
	protected function formatSortingClause(int $sortBy, bool $invertSort = false) : ?string {
53
		if ($sortBy === SortBy::Parent) {
54
			// Note: the alternative form "LOWER(`album_artist_name`) wouldn't work on PostgreSQL, see https://github.com/owncloud/music/issues/1046
55
			$dir = $invertSort ? 'DESC' : 'ASC';
56
			return "ORDER BY LOWER(`artist`.`name`) $dir, LOWER(`*PREFIX*music_albums`.`name`) $dir";
57
		} else {
58
			return parent::formatSortingClause($sortBy, $invertSort);
59
		}
60
	}
61
62
	/**
63
	 * returns artist IDs mapped to album IDs
64
	 * does not include album_artist_id
65
	 *
66
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
67
	 * @param string $userId the user ID
68
	 * @return array int => int[], keys are albums IDs and values are arrays of artist IDs
69
	 */
70
	public function getPerformingArtistsByAlbumId(?array $albumIds, string $userId) : array {
71
		$sql = 'SELECT DISTINCT `track`.`album_id`, `track`.`artist_id`
72
				FROM `*PREFIX*music_tracks` `track`
73
				WHERE `track`.`user_id` = ? ';
74
		$params = [$userId];
75
76
		if ($albumIds !== null) {
0 ignored issues
show
introduced by
The condition $albumIds !== null is always true.
Loading history...
77
			$sql .= 'AND `track`.`album_id` IN ' . $this->questionMarks(\count($albumIds));
78
			$params = \array_merge($params, $albumIds);
79
		}
80
81
		$result = $this->execute($sql, $params);
82
		$artistIds = [];
83
		while ($row = $result->fetch()) {
84
			$artistIds[$row['album_id']][] = (int)$row['artist_id'];
85
		}
86
		return $artistIds;
87
	}
88
89
	/**
90
	 * returns release years mapped to album IDs
91
	 *
92
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
93
	 * @param string $userId the user ID
94
	 * @return array int => int[], keys are albums IDs and values are arrays of years
95
	 */
96
	public function getYearsByAlbumId(?array $albumIds, string $userId) : array {
97
		$sql = 'SELECT DISTINCT `track`.`album_id`, `track`.`year`
98
				FROM `*PREFIX*music_tracks` `track`
99
				WHERE `track`.`user_id` = ?
100
				AND `track`.`year` IS NOT NULL ';
101
		$params = [$userId];
102
103
		if ($albumIds !== null) {
0 ignored issues
show
introduced by
The condition $albumIds !== null is always true.
Loading history...
104
			$sql .= 'AND `track`.`album_id` IN ' . $this->questionMarks(\count($albumIds));
105
			$params = \array_merge($params, $albumIds);
106
		}
107
108
		$result = $this->execute($sql, $params);
109
		$years = [];
110
		while ($row = $result->fetch()) {
111
			$years[$row['album_id']][] = (int)$row['year'];
112
		}
113
		return $years;
114
	}
115
116
	/**
117
	 * returns genres mapped to album IDs
118
	 *
119
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
120
	 * @param string $userId the user ID
121
	 * @return array int => Genre[], keys are albums IDs and values are arrays of *partial* Genre objects (only id and name properties set)
122
	 */
123
	public function getGenresByAlbumId(?array $albumIds, string $userId) : array {
124
		$sql = 'SELECT DISTINCT `album_id`, `genre_id`, `*PREFIX*music_genres`.`name` AS `genre_name`
125
				FROM `*PREFIX*music_tracks`
126
				LEFT JOIN `*PREFIX*music_genres`
127
				ON `genre_id` = `*PREFIX*music_genres`.`id`
128
				WHERE `*PREFIX*music_tracks`.`user_id` = ?
129
				AND `genre_id` IS NOT NULL ';
130
		$params = [$userId];
131
132
		if ($albumIds !== null) {
0 ignored issues
show
introduced by
The condition $albumIds !== null is always true.
Loading history...
133
			$sql .= 'AND `album_id` IN ' . $this->questionMarks(\count($albumIds));
134
			$params = \array_merge($params, $albumIds);
135
		}
136
137
		$result = $this->execute($sql, $params);
138
		$genres = [];
139
		while ($row = $result->fetch()) {
140
			$genre = new Genre();
141
			$genre->setUserId($userId);
142
			$genre->setId((int)$row['genre_id']);
143
			$genre->setName($row['genre_name']);
144
			$genres[$row['album_id']][] = $genre;
145
		}
146
		return $genres;
147
	}
148
149
	/**
150
	 * returns number of disks per album ID
151
	 *
152
	 * @param integer[]|null $albumIds IDs of the albums; get all albums of the user if null given
153
	 * @param string $userId the user ID
154
	 * @return array int => int, keys are albums IDs and values are disk counts
155
	 */
156
	public function getDiscCountByAlbumId(?array $albumIds, string $userId) : array {
157
		$sql = 'SELECT `album_id`, MAX(`disk`) AS `disc_count`
158
				FROM `*PREFIX*music_tracks`
159
				WHERE `user_id` = ?
160
				GROUP BY `album_id` ';
161
		$params = [$userId];
162
163
		if ($albumIds !== null) {
0 ignored issues
show
introduced by
The condition $albumIds !== null is always true.
Loading history...
164
			$sql .= 'HAVING `album_id` IN ' . $this->questionMarks(\count($albumIds));
165
			$params = \array_merge($params, $albumIds);
166
		}
167
168
		$result = $this->execute($sql, $params);
169
		$diskCountByAlbum = [];
170
		while ($row = $result->fetch()) {
171
			$diskCountByAlbum[$row['album_id']] = (int)$row['disc_count'];
172
		}
173
		return $diskCountByAlbum;
174
	}
175
176
	/**
177
	 * returns summed track play counts of each album of the user, omittig albums which have never been played
178
	 *
179
	 * @return array [int => int], keys are album IDs and values are play count sums; ordered largest counts first
180
	 */
181
	public function getAlbumTracksPlayCount(string $userId, ?int $limit=null, ?int $offset=null) : array {
182
		$sql = 'SELECT `album_id`, SUM(`play_count`) AS `sum_count`
183
				FROM `*PREFIX*music_tracks`
184
				WHERE `user_id` = ? AND `play_count` > 0
185
				GROUP BY `album_id`
186
				ORDER BY `sum_count` DESC, `album_id`'; // the second criterion is just to make the order predictable on even counts
187
188
		$result = $this->execute($sql, [$userId], $limit, $offset);
189
		$playCountByAlbum = [];
190
		while ($row = $result->fetch()) {
191
			$playCountByAlbum[$row['album_id']] = (int)$row['sum_count'];
192
		}
193
		return $playCountByAlbum;
194
	}
195
196
	/**
197
	 * returns the latest play time of each album of the user, omittig albums which have never been played
198
	 *
199
	 * @return array [int => string], keys are album IDs and values are date-times; ordered latest times first
200
	 */
201
	public function getLatestAlbumPlayTimes(string $userId, ?int $limit=null, ?int $offset=null) : array {
202
		$sql = 'SELECT `album_id`, MAX(`last_played`) AS `latest_time`
203
				FROM `*PREFIX*music_tracks`
204
				WHERE `user_id` = ? AND `last_played` IS NOT NULL
205
				GROUP BY `album_id`
206
				ORDER BY `latest_time` DESC';
207
208
		$result = $this->execute($sql, [$userId], $limit, $offset);
209
		$latestTimeByAlbum = [];
210
		while ($row = $result->fetch()) {
211
			$latestTimeByAlbum[$row['album_id']] = $row['latest_time'];
212
		}
213
		return $latestTimeByAlbum;
214
	}
215
216
	/**
217
	 * returns the latest play time of each album of the user, including albums which have never been played
218
	 *
219
	 * @return array [int => ?string], keys are album IDs and values are date-times (or null for never played);
220
	 *									ordered furthest times first
221
	 */
222
	public function getFurthestAlbumPlayTimes(string $userId, ?int $limit=null, ?int $offset=null) : array {
223
		$sql = 'SELECT `album_id`, MAX(`last_played`) AS `latest_time`
224
				FROM `*PREFIX*music_tracks`
225
				WHERE `user_id` = ?
226
				GROUP BY `album_id`
227
				ORDER BY `latest_time` ASC';
228
229
		$result = $this->execute($sql, [$userId], $limit, $offset);
230
		$latestTimeByAlbum = [];
231
		while ($row = $result->fetch()) {
232
			$latestTimeByAlbum[$row['album_id']] = $row['latest_time'];
233
		}
234
		return $latestTimeByAlbum;
235
	}
236
237
	/**
238
	 * @return Album[]
239
	 */
240
	public function findAllByNameRecursive(string $name, string $userId, ?int $limit=null, ?int $offset=null) : array {
241
		$condition = '( LOWER(`artist`.`name`) LIKE LOWER(?) OR
242
						LOWER(`*PREFIX*music_albums`.`name`) LIKE LOWER(?) )';
243
		$sql = $this->selectUserEntities($condition, 'ORDER BY LOWER(`*PREFIX*music_albums`.`name`)');
244
		$name = BaseMapper::prepareSubstringSearchPattern($name);
245
		$params = [$userId, $name, $name];
246
		return $this->findEntities($sql, $params, $limit, $offset);
247
	}
248
249
	/**
250
	 * returns albums of a specified artist
251
	 * The artist may be an album_artist or the artist of a track
252
	 *
253
	 * @param integer $artistId
254
	 * @return Album[]
255
	 */
256
	public function findAllByArtist(int $artistId, string $userId, ?int $limit=null, ?int $offset=null) : array {
257
		$sql = $this->selectEntities(
258
				'`*PREFIX*music_albums`.`id` IN (
259
					SELECT DISTINCT `album`.`id`
260
					FROM `*PREFIX*music_albums` `album`
261
					WHERE `album`.`album_artist_id` = ?
262
						UNION
263
					SELECT DISTINCT `track`.`album_id`
264
					FROM `*PREFIX*music_tracks` `track`
265
					WHERE `track`.`artist_id` = ?
266
				) AND `*PREFIX*music_albums`.`user_id` = ?',
267
				'ORDER BY LOWER(`*PREFIX*music_albums`.`name`)');
268
		$params = [$artistId, $artistId, $userId];
269
		return $this->findEntities($sql, $params, $limit, $offset);
270
	}
271
272
	/**
273
	 * returns albums of a specified artists
274
	 * The artist must be album_artist on the album, artists of individual tracks are not considered
275
	 *
276
	 * @param int[] $artistIds
277
	 * @return Album[]
278
	 */
279
	public function findAllByAlbumArtist(array $artistIds, string $userId, ?int $limit=null, ?int $offset=null) : array {
280
		$sql = $this->selectUserEntities('`album_artist_id` IN ' . $this->questionMarks(\count($artistIds)));
281
		$params = \array_merge([$userId], $artistIds);
282
		return $this->findEntities($sql, $params, $limit, $offset);
283
	}
284
285
	/**
286
	 * @return Album[]
287
	 */
288
	public function findAllByGenre(int $genreId, string $userId, int $limit=null, int $offset=null) : array {
289
		$sql = $this->selectUserEntities('EXISTS '.
290
				'(SELECT 1 FROM `*PREFIX*music_tracks` `track`
291
				  WHERE `*PREFIX*music_albums`.`id` = `track`.`album_id`
292
				  AND `track`.`genre_id` = ?)');
293
294
		$params = [$userId, $genreId];
295
		return $this->findEntities($sql, $params, $limit, $offset);
296
	}
297
298
	/**
299
	 * @return boolean True if one or more albums were influenced
300
	 */
301
	public function updateFolderCover(int $coverFileId, int $folderId) : bool {
302
		$sql = 'SELECT DISTINCT `tracks`.`album_id`
303
				FROM `*PREFIX*music_tracks` `tracks`
304
				JOIN `*PREFIX*filecache` `files` ON `tracks`.`file_id` = `files`.`fileid`
305
				WHERE `files`.`parent` = ?';
306
		$params = [$folderId];
307
		$result = $this->execute($sql, $params);
308
309
		$updated = false;
310
		if ($result->rowCount()) {
311
			$sql = 'UPDATE `*PREFIX*music_albums`
312
					SET `cover_file_id` = ?
313
					WHERE `cover_file_id` IS NULL AND `id` IN (?)';
314
			$params = [$coverFileId, \join(",", $result->fetchAll(\PDO::FETCH_COLUMN))];
315
			$result = $this->execute($sql, $params);
316
			$updated = $result->rowCount() > 0;
317
		}
318
319
		return $updated;
320
	}
321
322
	/**
323
	 * Set file ID to be used as cover for an album
324
	 */
325
	public function setCover(?int $coverFileId, int $albumId) : void {
326
		$sql = 'UPDATE `*PREFIX*music_albums`
327
				SET `cover_file_id` = ?
328
				WHERE `id` = ?';
329
		$params = [$coverFileId, $albumId];
330
		$this->execute($sql, $params);
331
	}
332
333
	/**
334
	 * @param integer[] $coverFileIds
335
	 * @param string[]|null $userIds the users whose music library is targeted; all users are targeted if omitted
336
	 * @return Album[] albums which got modified (with incomplete data, only id and user are valid),
337
	 *         empty array if none
338
	 */
339
	public function removeCovers(array $coverFileIds, array $userIds=null) : array {
340
		// find albums using the given file as cover
341
		$sql = 'SELECT `id`, `user_id` FROM `*PREFIX*music_albums` WHERE `cover_file_id` IN ' .
342
			$this->questionMarks(\count($coverFileIds));
343
		$params = $coverFileIds;
344
		if ($userIds !== null) {
345
			$sql .= ' AND `user_id` IN ' . $this->questionMarks(\count($userIds));
346
			$params = \array_merge($params, $userIds);
347
		}
348
		$albums = $this->findEntities($sql, $params);
349
350
		// if any albums found, remove the cover from those
351
		$count = \count($albums);
352
		if ($count) {
353
			$sql = 'UPDATE `*PREFIX*music_albums`
354
				SET `cover_file_id` = NULL
355
				WHERE `id` IN ' . $this->questionMarks($count);
356
			$params = Util::extractIds($albums);
357
			$this->execute($sql, $params);
358
		}
359
360
		return $albums;
361
	}
362
363
	/**
364
	 * @param string|null $userId target user; omit to target all users
365
	 * @return array of dictionaries with keys [albumId, userId, parentFolderId]
366
	 */
367
	public function getAlbumsWithoutCover(string $userId = null) : array {
368
		$sql = 'SELECT DISTINCT `albums`.`id`, `albums`.`user_id`, `files`.`parent`
369
				FROM `*PREFIX*music_albums` `albums`
370
				JOIN `*PREFIX*music_tracks` `tracks` ON `albums`.`id` = `tracks`.`album_id`
371
				JOIN `*PREFIX*filecache` `files` ON `tracks`.`file_id` = `files`.`fileid`
372
				WHERE `albums`.`cover_file_id` IS NULL';
373
		$params = [];
374
		if ($userId !== null) {
375
			$sql .= ' AND `albums`.`user_id` = ?';
376
			$params[] = $userId;
377
		}
378
		$result = $this->execute($sql, $params);
379
		$return = [];
380
		while ($row = $result->fetch()) {
381
			$return[] = [
382
				'albumId' => (int)$row['id'],
383
				'userId' => $row['user_id'],
384
				'parentFolderId' => (int)$row['parent']
385
			];
386
		}
387
		return $return;
388
	}
389
390
	/**
391
	 * @return boolean True if a cover image was found and added for the album
392
	 */
393
	public function findAlbumCover(int $albumId, int $parentFolderId) : bool {
394
		$return = false;
395
		$imagesSql = 'SELECT `fileid`, `name`
396
					FROM `*PREFIX*filecache`
397
					JOIN `*PREFIX*mimetypes` ON `*PREFIX*mimetypes`.`id` = `*PREFIX*filecache`.`mimetype`
398
					WHERE `parent` = ? AND `*PREFIX*mimetypes`.`mimetype` LIKE \'image%\'';
399
		$params = [$parentFolderId];
400
		$result = $this->execute($imagesSql, $params);
401
		$images = $result->fetchAll();
402
		if (\count($images) > 0) {
403
			$getImageRank = function($imageName) {
404
				$coverNames = ['cover', 'albumart', 'album', 'front', 'folder'];
405
				foreach ($coverNames as $i => $coverName) {
406
					if (Util::startsWith($imageName, $coverName, /*$ignoreCase=*/true)) {
407
						return $i;
408
					}
409
				}
410
				return \count($coverNames);
411
			};
412
413
			\usort($images, function ($imageA, $imageB) use ($getImageRank) {
414
				return $getImageRank($imageA['name']) <=> $getImageRank($imageB['name']);
415
			});
416
			$imageId = (int)$images[0]['fileid'];
417
			$this->setCover($imageId, $albumId);
418
			$return = true;
419
		}
420
		return $return;
421
	}
422
423
	/**
424
	 * Given an array of track IDs, find corresponding uniqu album IDs, including only
425
	 * those album which have a cover art set.
426
	 * @param int[] $trackIds
427
	 * @return Album[]
428
	 */
429
	public function findAlbumsWithCoversForTracks(array $trackIds, string $userId, int $limit) : array {
430
		$sql = 'SELECT DISTINCT `albums`.*
431
				FROM `*PREFIX*music_albums` `albums`
432
				JOIN `*PREFIX*music_tracks` `tracks` ON `albums`.`id` = `tracks`.`album_id`
433
				WHERE `albums`.`cover_file_id` IS NOT NULL
434
				AND `albums`.`user_id` = ?
435
				AND `tracks`.`id` IN ' . $this->questionMarks(\count($trackIds));
436
		$params = \array_merge([$userId], $trackIds);
437
438
		return $this->findEntities($sql, $params, $limit);
439
	}
440
441
	/**
442
	 * Returns the count of albums where the given Artist is featured in
443
	 * @param integer $artistId
444
	 * @return integer
445
	 */
446
	public function countByArtist(int $artistId) : int {
447
		$sql = 'SELECT COUNT(*) AS count FROM (
448
					SELECT DISTINCT `track`.`album_id`
449
					FROM `*PREFIX*music_tracks` `track`
450
					WHERE `track`.`artist_id` = ?
451
						UNION
452
					SELECT `album`.`id`
453
					FROM `*PREFIX*music_albums` `album`
454
					WHERE `album`.`album_artist_id` = ?
455
				) tmp';
456
		$params = [$artistId, $artistId];
457
		$result = $this->execute($sql, $params);
458
		$row = $result->fetch();
459
		return (int)$row['count'];
460
	}
461
462
	/**
463
	 * Returns the count of albums where the given artist is the album artist
464
	 * @param integer $artistId
465
	 * @return integer
466
	 */
467
	public function countByAlbumArtist(int $artistId) : int {
468
		$sql = 'SELECT COUNT(*) AS count
469
				FROM `*PREFIX*music_albums` `album`
470
				WHERE `album`.`album_artist_id` = ?';
471
		$params = [$artistId];
472
		$result = $this->execute($sql, $params);
473
		$row = $result->fetch();
474
		return (int)$row['count'];
475
	}
476
477
	/**
478
	 * Overridden from the base implementation to provide support for table-specific rules
479
	 *
480
	 * {@inheritdoc}
481
	 * @see BaseMapper::advFormatSqlCondition()
482
	 */
483
	protected function advFormatSqlCondition(string $rule, string $sqlOp) : string {
484
		// The extra subquery "mysqlhack" seen around some nested queries is needed in order for these to not be insanely slow on MySQL.
485
		// In case of 'recent_played', the MySQL 5.5.62 errored with "1235 This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'" without the extra subquery.
486
		switch ($rule) {
487
			case 'artist':			return "LOWER(`artist`.`name`) $sqlOp LOWER(?)";
488
			case 'song_artist':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*music_artists` `ar` ON `t`.`artist_id` = `ar`.`id` WHERE LOWER(`ar`.`name`) $sqlOp LOWER(?))";
489
			case 'song':			return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` WHERE LOWER(`t`.`title`) $sqlOp LOWER(?))";
490
			case 'year':			// fall through, we only have one kind of year
491
			case 'original_year':	return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` FROM `*PREFIX*music_tracks` GROUP BY `album_id` HAVING MIN(`year`) $sqlOp ?) mysqlhack)";
492
			case 'songrating':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` WHERE `t`.`rating` $sqlOp ?)";
493
			case 'artistrating':	return "`artist`.rating $sqlOp ?";
494
			case 'played_times':	return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` from `*PREFIX*music_tracks` GROUP BY `album_id` HAVING SUM(`play_count`) $sqlOp ?) mysqlhack)";
495
			case 'last_play':		return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` from `*PREFIX*music_tracks` GROUP BY `album_id` HAVING MAX(`last_played`) $sqlOp ?) mysqlhack)";
496
			case 'played':			// fall through, we give no access to other people's data; not part of the API spec but Ample uses this
497
			case 'myplayed':		return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` from `*PREFIX*music_tracks` GROUP BY `album_id` HAVING MAX(`last_played`) $sqlOp) mysqlhack)"; // operator "IS NULL" or "IS NOT NULL"
498
			case 'myplayedartist':	return "`album_artist_id` IN (SELECT * FROM (SELECT `artist_id` from `*PREFIX*music_tracks` GROUP BY `artist_id` HAVING MAX(`last_played`) $sqlOp) mysqlhack)"; // operator "IS NULL" or "IS NOT NULL"
499
			case 'song_count':		return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` FROM `*PREFIX*music_tracks` GROUP BY `album_id` HAVING COUNT(`id`) $sqlOp ?) mysqlhack)";
500
			case 'time':			return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` FROM `*PREFIX*music_tracks` GROUP BY `album_id` HAVING SUM(`length`) $sqlOp ?) mysqlhack)";
501
			case 'genre':			// fall through, alias
502
			case 'album_genre':		return "`*PREFIX*music_albums`.`id` IN (SELECT * FROM (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*music_genres` `g` ON `t`.`genre_id` = `g`.`id` GROUP BY `album_id` HAVING LOWER(GROUP_CONCAT(`g`.`name`)) $sqlOp LOWER(?)) mysqlhack)"; // GROUP_CONCAT not available on PostgreSQL
503
			case 'song_genre':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*music_genres` `g` ON `t`.`genre_id` = `g`.`id` WHERE LOWER(`g`.`name`) $sqlOp LOWER(?))";
504
			case 'no_genre':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*music_genres` `g` ON `t`.`genre_id` = `g`.`id` WHERE `g`.`name` " . (($sqlOp == 'IS NOT NULL') ? '=' : '!=') . ' "")';
505
			case 'playlist':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` WHERE $sqlOp EXISTS (SELECT 1 from `*PREFIX*music_playlists` `p` WHERE `p`.`id` = ? AND `p`.`track_ids` LIKE CONCAT('%|',`t`.`id`, '|%')))";
506
			case 'playlist_name':	return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` WHERE EXISTS (SELECT 1 from `*PREFIX*music_playlists` `p` WHERE `p`.`name` $sqlOp ? AND `p`.`track_ids` LIKE CONCAT('%|',`t`.`id`, '|%')))";
507
			case 'file':			return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*filecache` `f` ON `t`.`file_id` = `f`.`fileid` WHERE LOWER(`f`.`name`) $sqlOp LOWER(?))";
508
			case 'recent_played':	return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM (SELECT `album_id`, MAX(`last_played`) FROM `*PREFIX*music_tracks` WHERE `user_id` = ? GROUP BY `album_id` ORDER BY MAX(`last_played`) DESC LIMIT $sqlOp) mysqlhack)";
509
			case 'mbid_album':		return parent::advFormatSqlCondition('mbid', $sqlOp); // alias
510
			case 'mbid_song':		return "`*PREFIX*music_albums`.`id` IN (SELECT `album_id` FROM `*PREFIX*music_tracks` `t` WHERE `t`.`mbid` $sqlOp ?)";
511
			case 'mbid_artist':		return "`artist`.`mbid` $sqlOp ?";
512
			case 'has_image':		return "`*PREFIX*music_albums`.`cover_file_id` $sqlOp"; // operator "IS NULL" or "IS NOT NULL"
513
			default:				return parent::advFormatSqlCondition($rule, $sqlOp);
514
		}
515
	}
516
517
	/**
518
	 * {@inheritdoc}
519
	 * @see \OCA\Music\Db\BaseMapper::findUniqueEntity()
520
	 * @param Album $album
521
	 * @return Album
522
	 */
523
	protected function findUniqueEntity(Entity $album) : Entity {
524
		$sql = $this->selectUserEntities('`*PREFIX*music_albums`.`hash` = ?');
525
		return $this->findEntity($sql, [$album->getUserId(), $album->getHash()]);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->findEntity...(), $album->getHash())) returns the type OCP\AppFramework\Db\Entity which includes types incompatible with the type-hinted return OCA\Music\Db\Entity.
Loading history...
Bug introduced by
The method getHash() does not exist on OCA\Music\Db\Entity. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

525
		return $this->findEntity($sql, [$album->getUserId(), $album->/** @scrutinizer ignore-call */ getHash()]);
Loading history...
526
	}
527
}
528