Passed
Push — master ( 83669c...348a90 )
by Pauli
07:30 queued 04:27
created

TrackMapper::findAllByCriteria()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 16
c 1
b 0
f 0
nc 16
nop 9
dl 0
loc 29
rs 9.4222

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 - 2023
13
 */
14
15
namespace OCA\Music\Db;
16
17
use OCP\IConfig;
18
use OCP\IDBConnection;
19
20
/**
21
 * @phpstan-extends BaseMapper<Track>
22
 */
23
class TrackMapper extends BaseMapper {
24
	public function __construct(IDBConnection $db, IConfig $config) {
25
		parent::__construct($db, $config, 'music_tracks', Track::class, 'title', 'album_id');
26
	}
27
28
	/**
29
	 * Override the base implementation to include data from multiple tables
30
	 *
31
	 * {@inheritdoc}
32
	 * @see BaseMapper::selectEntities()
33
	 */
34
	protected function selectEntities(string $condition, string $extension=null) : string {
35
		return "SELECT `*PREFIX*music_tracks`.*, `file`.`name` AS `filename`, `file`.`size`, `file`.`mtime` AS `file_mod_time`,
36
						`album`.`name` AS `album_name`, `artist`.`name` AS `artist_name`, `genre`.`name` AS `genre_name`
37
				FROM `*PREFIX*music_tracks`
38
				INNER JOIN `*PREFIX*filecache` `file`
39
				ON `*PREFIX*music_tracks`.`file_id` = `file`.`fileid`
40
				INNER JOIN `*PREFIX*music_albums` `album`
41
				ON `*PREFIX*music_tracks`.`album_id` = `album`.`id`
42
				INNER JOIN `*PREFIX*music_artists` `artist`
43
				ON `*PREFIX*music_tracks`.`artist_id` = `artist`.`id`
44
				LEFT JOIN `*PREFIX*music_genres` `genre`
45
				ON `*PREFIX*music_tracks`.`genre_id` = `genre`.`id`
46
				WHERE $condition $extension";
47
	}
48
49
	/**
50
	 * Overridden from the base implementation to add support for sorting by artist, play_count, and last_played.
51
	 *
52
	 * {@inheritdoc}
53
	 * @see BaseMapper::formatSortingClause()
54
	 */
55
	protected function formatSortingClause(int $sortBy, bool $invertSort = false) : ?string {
56
		$dir = $invertSort ? 'DESC' : 'ASC';
57
		switch ($sortBy) {
58
			case SortBy::Parent:
59
				// Note: the alternative form "LOWER(`artist_name`) wouldn't work on PostgreSQL, see https://github.com/owncloud/music/issues/1046 for a similar case
60
				return "ORDER BY LOWER(`artist`.`name`) $dir, LOWER(`title`) $dir";
61
			case SortBy::PlayCount:
62
				return "ORDER BY `play_count` $dir";
63
			case SortBy::LastPlayed:
64
				return "ORDER BY `last_played` $dir";
65
			default:
66
				return parent::formatSortingClause($sortBy, $invertSort);
67
		}
68
	}
69
70
	/**
71
	 * Returns all tracks of the given artist (both album and track artists are considered)
72
	 * @param int[] $artistIds
73
	 * @return Track[]
74
	 */
75
	public function findAllByArtist(array $artistIds, string $userId, ?int $limit=null, ?int $offset=null) : array {
76
		$questionMarks = $this->questionMarks(\count($artistIds));
77
		$sql = $this->selectUserEntities(
78
				"`artist_id` IN $questionMarks OR `album_id` IN (SELECT `id` from `*PREFIX*music_albums` WHERE `album_artist_id` IN $questionMarks)",
79
				'ORDER BY LOWER(`title`)');
80
		$params = \array_merge([$userId], $artistIds, $artistIds);
81
		return $this->findEntities($sql, $params, $limit, $offset);
82
	}
83
84
	/**
85
	 * @param int[] $albumIds
86
	 * @return Track[]
87
	 */
88
	public function findAllByAlbum(array $albumIds, string $userId, ?int $artistId=null, ?int $limit=null, ?int $offset=null) : array {
89
		$condition = '`album_id` IN ' . $this->questionMarks(\count($albumIds));
90
		$params = \array_merge([$userId], $albumIds);
91
92
		if ($artistId !== null) {
93
			$condition .= ' AND `artist_id` = ? ';
94
			$params[] = $artistId;
95
		}
96
97
		$sql = $this->selectUserEntities($condition,
98
				'ORDER BY `*PREFIX*music_tracks`.`disk`, `number`, LOWER(`title`)');
99
		return $this->findEntities($sql, $params, $limit, $offset);
100
	}
101
102
	/**
103
	 * @return Track[]
104
	 */
105
	public function findAllByFolder(int $folderId, string $userId, ?int $limit=null, ?int $offset=null) : array {
106
		$sql = $this->selectUserEntities('`file`.`parent` = ?', 'ORDER BY LOWER(`title`)');
107
		$params = [$userId, $folderId];
108
		return $this->findEntities($sql, $params, $limit, $offset);
109
	}
110
111
	/**
112
	 * @return Track[]
113
	 */
114
	public function findAllByGenre(int $genreId, string $userId, ?int $limit=null, ?int $offset=null) : array {
115
		$sql = $this->selectUserEntities('`genre_id` = ?', 'ORDER BY LOWER(`title`)');
116
		$params = [$userId, $genreId];
117
		return $this->findEntities($sql, $params, $limit, $offset);
118
	}
119
120
	/**
121
	 * @param string $userId
122
	 * @return int[]
123
	 */
124
	public function findAllFileIds(string $userId) : array {
125
		$sql = 'SELECT `file_id` FROM `*PREFIX*music_tracks` WHERE `user_id` = ?';
126
		$result = $this->execute($sql, [$userId]);
127
128
		return \array_map(function ($i) {
129
			return (int)$i['file_id'];
130
		}, $result->fetchAll());
131
	}
132
133
	/**
134
	 * Find a track of user matching a file ID
135
	 * @throws \OCP\AppFramework\Db\DoesNotExistException if not found
136
	 */
137
	public function findByFileId(int $fileId, string $userId) : Track {
138
		$sql = $this->selectUserEntities('`file_id` = ?');
139
		$params = [$userId, $fileId];
140
		return $this->findEntity($sql, $params);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->findEntity($sql, $params) returns the type OCP\AppFramework\Db\Entity which includes types incompatible with the type-hinted return OCA\Music\Db\Track.
Loading history...
141
	}
142
143
	/**
144
	 * Find tracks of user with multiple file IDs
145
	 * @param integer[] $fileIds
146
	 * @param string[] $userIds
147
	 * @return Track[]
148
	 */
149
	public function findByFileIds(array $fileIds, array $userIds) : array {
150
		$sql = $this->selectEntities(
151
				'`*PREFIX*music_tracks`.`user_id` IN ' . $this->questionMarks(\count($userIds)) .
152
				' AND `file_id` IN '. $this->questionMarks(\count($fileIds)));
153
		$params = \array_merge($userIds, $fileIds);
154
		return $this->findEntities($sql, $params);
155
	}
156
157
	/**
158
	 * Finds tracks of all users matching one or multiple file IDs
159
	 * @param integer[] $fileIds
160
	 * @return Track[]
161
	 */
162
	public function findAllByFileIds(array $fileIds) : array {
163
		$sql = $this->selectEntities('`file_id` IN '.
164
				$this->questionMarks(\count($fileIds)));
165
		return $this->findEntities($sql, $fileIds);
166
	}
167
168
	public function countByArtist(int $artistId) : int {
169
		$sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?';
170
		$result = $this->execute($sql, [$artistId]);
171
		$row = $result->fetch();
172
		return (int)$row['count'];
173
	}
174
175
	public function countByAlbum(int $albumId) : int {
176
		$sql = 'SELECT COUNT(*) AS `count` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?';
177
		$result = $this->execute($sql, [$albumId]);
178
		$row = $result->fetch();
179
		return (int)$row['count'];
180
	}
181
182
	/**
183
	 * @return integer Duration in seconds
184
	 */
185
	public function totalDurationOfAlbum(int $albumId) : int {
186
		$sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `album_id` = ?';
187
		$result = $this->execute($sql, [$albumId]);
188
		$row = $result->fetch();
189
		return (int)$row['duration'];
190
	}
191
192
	/**
193
	 * @return integer Duration in seconds
194
	 */
195
	public function totalDurationByArtist(int $artistId) : int {
196
		$sql = 'SELECT SUM(`length`) AS `duration` FROM `*PREFIX*music_tracks` WHERE `artist_id` = ?';
197
		$result = $this->execute($sql, [$artistId]);
198
		$row = $result->fetch();
199
		return (int)$row['duration'];
200
	}
201
202
	/**
203
	 * Get durations of the given tracks.
204
	 * @param integer[] $trackIds
205
	 * @return array {int => int} where keys are track IDs and values are corresponding durations
206
	 */
207
	public function getDurations(array $trackIds) : array {
208
		$result = [];
209
210
		if (!empty($trackIds)) {
211
			$sql = 'SELECT `id`, `length` FROM `*PREFIX*music_tracks` WHERE `id` IN ' .
212
						$this->questionMarks(\count($trackIds));
213
			$rows = $this->execute($sql, $trackIds)->fetchAll();
214
			foreach ($rows as $row) {
215
				$result[$row['id']] = (int)$row['length'];
216
			}
217
		}
218
		return $result;
219
	}
220
221
	/**
222
	 * @return Track[]
223
	 */
224
	public function findAllByNameRecursive(string $name, string $userId, ?int $limit=null, ?int $offset=null) {
225
		$condition = '( LOWER(`artist`.`name`) LIKE LOWER(?) OR
226
						LOWER(`album`.`name`) LIKE LOWER(?) OR
227
						LOWER(`title`) LIKE LOWER(?) )';
228
		$sql = $this->selectUserEntities($condition, 'ORDER BY LOWER(`title`)');
229
		$name = BaseMapper::prepareSubstringSearchPattern($name);
230
		$params = [$userId, $name, $name, $name];
231
		return $this->findEntities($sql, $params, $limit, $offset);
232
	}
233
234
	/**
235
	 * Returns all tracks specified by name and/or artist name
236
	 * @param string|null $name the name of the track
237
	 * @param string|null $artistName the name of the artist
238
	 * @param string $userId the name of the user
239
	 * @return Track[] Tracks matching the criteria
240
	 */
241
	public function findAllByNameAndArtistName(?string $name, ?string $artistName, string $userId) : array {
242
		$sqlConditions = [];
243
		$params = [$userId];
244
245
		if (!empty($name)) {
246
			$sqlConditions[] = '`title` = ?';
247
			$params[] = $name;
248
		}
249
250
		if (!empty($artistName)) {
251
			$sqlConditions[] = '`artist`.`name` = ?';
252
			$params[] = $artistName;
253
		}
254
255
		// at least one condition has to be given, otherwise return an empty set
256
		if (\count($sqlConditions) > 0) {
257
			$sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions));
258
			return $this->findEntities($sql, $params);
259
		} else {
260
			return [];
261
		}
262
	}
263
264
	/**
265
	 * Returns all tracks specified by various criteria, all of which are optional
266
	 * @param int[] $genres Array of genre IDs
267
	 * @param int[] $artists Array of artist IDs
268
	 * @param int|null $fromYear Earliest release year to include
269
	 * @param int|null $toYear Latest release year to include
270
	 * @param int $sortBy Sorting rule as defined in the class SortBy
271
	 * @param string $userId the name of the user
272
	 * @return Track[] Tracks matching the criteria
273
	 */
274
	public function findAllByCriteria(
275
			array $genres, array $artists, ?int $fromYear, ?int $toYear,
276
			int $sortBy, bool $invertSort, string $userId, ?int $limit=null, ?int $offset=null) : array {
277
278
		$sqlConditions = [];
279
		$params = [$userId];
280
281
		if (!empty($genres)) {
282
			$sqlConditions[] = '`genre_id` IN ' . $this->questionMarks(\count($genres));
283
			$params = \array_merge($params, $genres);
284
		}
285
286
		if (!empty($artists)) {
287
			$sqlConditions[] = '`artist_id` IN ' . $this->questionMarks(\count($artists));
288
			$params = \array_merge($params, $artists);
289
		}
290
291
		if (!empty($fromYear)) {
292
			$sqlConditions[] = '`year` >= ?';
293
			$params[] = $fromYear;
294
		}
295
296
		if (!empty($toYear)) {
297
			$sqlConditions[] = '`year` <= ?';
298
			$params[] = $toYear;
299
		}
300
301
		$sql = $this->selectUserEntities(\implode(' AND ', $sqlConditions), $this->formatSortingClause($sortBy, $invertSort));
302
		return $this->findEntities($sql, $params, $limit, $offset);
303
	}
304
305
	/**
306
	 * Find most frequently played tracks
307
	 * @return Track[]
308
	 */
309
	public function findFrequentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
310
		$sql = $this->selectUserEntities('`play_count` > 0', 'ORDER BY `play_count` DESC, LOWER(`title`)');
311
		return $this->findEntities($sql, [$userId], $limit, $offset);
312
	}
313
314
	/**
315
	 * Find most recently played tracks
316
	 * @return Track[]
317
	 */
318
	public function findRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
319
		$sql = $this->selectUserEntities('`last_played` IS NOT NULL', 'ORDER BY `last_played` DESC');
320
		return $this->findEntities($sql, [$userId], $limit, $offset);
321
	}
322
323
	/**
324
	 * Find least recently played tracks
325
	 * @return Track[]
326
	 */
327
	public function findNotRecentPlay(string $userId, ?int $limit=null, ?int $offset=null) : array {
328
		$sql = $this->selectUserEntities(null, 'ORDER BY `last_played` ASC');
329
		return $this->findEntities($sql, [$userId], $limit, $offset);
330
	}
331
332
	/**
333
	 * Finds all track IDs of the user along with the parent folder ID of each track
334
	 * @return array where keys are folder IDs and values are arrays of track IDs
335
	 */
336
	public function findTrackAndFolderIds(string $userId) : array {
337
		$sql = 'SELECT `track`.`id` AS id, `file`.`name` AS `filename`, `file`.`parent` AS parent
338
				FROM `*PREFIX*music_tracks` `track`
339
				JOIN `*PREFIX*filecache` `file`
340
				ON `track`.`file_id` = `file`.`fileid`
341
				WHERE `track`.`user_id` = ?';
342
343
		$rows = $this->execute($sql, [$userId])->fetchAll();
344
345
		// Sort the results according the file names. This can't be made using ORDERBY in the
346
		// SQL query because then we couldn't use the "natural order" comparison algorithm
347
		\usort($rows, function ($a, $b) {
348
			return \strnatcasecmp($a['filename'], $b['filename']);
349
		});
350
351
		// group the files to parent folder "buckets"
352
		$result = [];
353
		foreach ($rows as $row) {
354
			$result[(int)$row['parent']][] = (int)$row['id'];
355
		}
356
357
		return $result;
358
	}
359
360
	/**
361
	 * Find names and parents of the file system nodes with given IDs within the given storage
362
	 * @param int[] $nodeIds
363
	 * @param string $storageId
364
	 * @return array where keys are the node IDs and values are associative arrays
365
	 *         like { 'name' => string, 'parent' => int };
366
	 */
367
	public function findNodeNamesAndParents(array $nodeIds, string $storageId) : array {
368
		$result = [];
369
370
		if (!empty($nodeIds)) {
371
			$sql = 'SELECT `fileid`, `name`, `parent` '.
372
					'FROM `*PREFIX*filecache` `filecache` '.
373
					'JOIN `*PREFIX*storages` `storages` '.
374
					'ON `filecache`.`storage` = `storages`.`numeric_id` '.
375
					'WHERE `storages`.`id` = ? '.
376
					'AND `filecache`.`fileid` IN '. $this->questionMarks(\count($nodeIds));
377
378
			$rows = $this->execute($sql, \array_merge([$storageId], $nodeIds))->fetchAll();
379
380
			foreach ($rows as $row) {
381
				$result[$row['fileid']] = [
382
					'name' => $row['name'],
383
					'parent' => (int)$row['parent']
384
				];
385
			}
386
		}
387
388
		return $result;
389
	}
390
391
	/**
392
	 * Returns all genre IDs associated with the given artist
393
	 * @return int[]
394
	 */
395
	public function getGenresByArtistId(int $artistId, string $userId) : array {
396
		$sql = 'SELECT DISTINCT(`genre_id`) FROM `*PREFIX*music_tracks` WHERE
397
				`genre_id` IS NOT NULL AND `user_id` = ? AND `artist_id` = ?';
398
		$rows = $this->execute($sql, [$userId, $artistId]);
399
		return $rows->fetchAll(\PDO::FETCH_COLUMN);
400
	}
401
402
	/**
403
	 * Returns all tracks IDs of the user, organized by the genre_id.
404
	 * @return array where keys are genre IDs and values are arrays of track IDs
405
	 */
406
	public function mapGenreIdsToTrackIds(string $userId) : array {
407
		$sql = 'SELECT `id`, `genre_id` FROM `*PREFIX*music_tracks`
408
				WHERE `genre_id` IS NOT NULL and `user_id` = ?';
409
		$rows = $this->execute($sql, [$userId])->fetchAll();
410
411
		$result = [];
412
		foreach ($rows as $row) {
413
			$result[(int)$row['genre_id']][] = (int)$row['id'];
414
		}
415
416
		return $result;
417
	}
418
419
	/**
420
	 * Returns file IDs of the tracks which do not have genre scanned. This is not the same
421
	 * thing as unknown genre, which means that the genre has been scanned but was not found
422
	 * from the track metadata.
423
	 * @return int[]
424
	 */
425
	public function findFilesWithoutScannedGenre(string $userId) : array {
426
		$sql = 'SELECT `track`.`file_id` FROM `*PREFIX*music_tracks` `track`
427
				INNER JOIN `*PREFIX*filecache` `file`
428
				ON `track`.`file_id` = `file`.`fileid`
429
				WHERE `genre_id` IS NULL and `user_id` = ?';
430
		$rows = $this->execute($sql, [$userId]);
431
		return $rows->fetchAll(\PDO::FETCH_COLUMN);
432
	}
433
434
	/**
435
	 * Update "last played" timestamp and increment the total play count of the track.
436
	 * The DB row is updated *without* updating the `updated` column.
437
	 * @return bool true if the track was found and updated, false otherwise
438
	 */
439
	public function recordTrackPlayed(int $trackId, string $userId, \DateTime $timeOfPlay) : bool {
440
		$sql = 'UPDATE `*PREFIX*music_tracks`
441
				SET `last_played` = ?, `play_count` = `play_count` + 1
442
				WHERE `user_id` = ? AND `id` = ?';
443
		$params = [$timeOfPlay->format(BaseMapper::SQL_DATE_FORMAT), $userId, $trackId];
444
		$result = $this->execute($sql, $params);
445
		return ($result->rowCount() > 0);
446
	}
447
448
	/**
449
	 * Overridden from the base implementation to provide support for table-specific rules
450
	 *
451
	 * {@inheritdoc}
452
	 * @see BaseMapper::advFormatSqlCondition()
453
	 */
454
	protected function advFormatSqlCondition(string $rule, string $sqlOp) : string {
455
		// The extra subquery "mysqlhack" seen around some nested queries is needed in order for these to not be insanely slow on MySQL.
456
		switch ($rule) {
457
			case 'anywhere':		return self::formatAdvSearchAnywhereCond($sqlOp); 
458
			case 'album':			return "`album_id` IN (SELECT `id` from `*PREFIX*music_albums` `al` WHERE LOWER(`al`.`name`) $sqlOp LOWER(?))";
459
			case 'artist':			return "LOWER(`artist`.`name`) $sqlOp LOWER(?)";
460
			case 'album_artist':	return "`album_id` IN (SELECT `al`.`id` from `*PREFIX*music_albums` `al` JOIN `*PREFIX*music_artists` `ar` ON `al`.`album_artist_id` = `ar`.`id` WHERE LOWER(`ar`.`name`) $sqlOp LOWER(?))";
461
			case 'track':			return "`number` $sqlOp ?";
462
			case 'year':			return "`year` $sqlOp ?";
463
			case 'albumrating':		return "`album`.`rating` $sqlOp ?";
464
			case 'artistrating':	return "`artist`.`rating` $sqlOp ?";
465
			case 'favorite_album':	return "`album_id` IN (SELECT `id` from `*PREFIX*music_albums` `al` WHERE LOWER(`al`.`name`) $sqlOp LOWER(?) AND `al`.`starred` IS NOT NULL)";
466
			case 'favorite_artist':	return "`artist_id` IN (SELECT `id` from `*PREFIX*music_artists` `ar` WHERE LOWER(`ar`.`name`) $sqlOp LOWER(?) AND `ar`.`starred` IS NOT NULL)";
467
			case 'played_times':	return "`play_count` $sqlOp ?";
468
			case 'last_play':		return "`last_played` $sqlOp ?";
469
			case 'played':			// fall through, we give no access to other people's data
470
			case 'myplayed':		return "`last_played` $sqlOp"; // operator "IS NULL" or "IS NOT NULL"
471
			case 'myplayedalbum':	return "`album_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"
472
			case 'myplayedartist':	return "`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"
473
			case 'time':			return "`length` $sqlOp ?";
474
			case 'genre':			// fall through
475
			case 'song_genre':		return "LOWER(`genre`.`name`) $sqlOp LOWER(?)";
476
			case 'album_genre':		return "`album_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
477
			case 'artist_genre':	return "`artist_id` IN (SELECT * FROM (SELECT `artist_id` FROM `*PREFIX*music_tracks` `t` JOIN `*PREFIX*music_genres` `g` ON `t`.`genre_id` = `g`.`id` GROUP BY `artist_id` HAVING LOWER(GROUP_CONCAT(`g`.`name`)) $sqlOp LOWER(?)) mysqlhack)"; // GROUP_CONCAT not available on PostgreSQL
478
			case 'no_genre':		return ($sqlOp == 'IS NOT NULL') ? '`genre`.`name` = ""' : '`genre`.`name` != ""';
479
			case 'playlist':		return "$sqlOp EXISTS (SELECT 1 from `*PREFIX*music_playlists` `p` WHERE `p`.`id` = ? AND `p`.`track_ids` LIKE CONCAT('%|',`*PREFIX*music_tracks`.`id`, '|%'))";
480
			case 'playlist_name':	return "EXISTS (SELECT 1 from `*PREFIX*music_playlists` `p` WHERE `p`.`name` $sqlOp ? AND `p`.`track_ids` LIKE CONCAT('%|',`*PREFIX*music_tracks`.`id`, '|%'))";
481
			case 'recent_played':	return "`*PREFIX*music_tracks`.`id` IN (SELECT * FROM (SELECT `id` FROM `*PREFIX*music_tracks` WHERE `user_id` = ? ORDER BY `last_played` DESC LIMIT $sqlOp) mysqlhack)";
482
			case 'file':			return "LOWER(`file`.`name`) $sqlOp LOWER(?)";
483
			case 'mbid_song':		return parent::advFormatSqlCondition('mbid', $sqlOp); // alias
484
			case 'mbid_album':		return "`album_id` IN (SELECT `id` from `*PREFIX*music_albums` `al` WHERE `al`.`mbid` $sqlOp ?)";
485
			case 'mbid_artist':		return "`artist`.`mbid` $sqlOp ?";
486
			default:				return parent::advFormatSqlCondition($rule, $sqlOp);
487
		}
488
	}
489
490
	private static function formatAdvSearchAnywhereCond(string $sqlOp) : string {
491
		$fields = [
492
			"`*PREFIX*music_tracks`.`title`",
493
			"`file`.`name`",
494
			"`artist`.`name`",
495
			"`album`.`name`",
496
			"`genre`.`name`"
497
		];
498
		$parts = \array_map(function(string $field) use ($sqlOp) {
499
			return "LOWER($field) $sqlOp LOWER(?)";
500
		}, $fields);
501
502
		$negativeOp = \in_array($sqlOp, ['NOT LIKE', '!=', 'NOT SOUNDS LIKE', 'NOT REGEXP']);
503
		$cond = \implode($negativeOp ? ' AND ' : ' OR ', $parts);
504
505
		return "($cond)";
506
	}
507
508
	/**
509
	 * {@inheritdoc}
510
	 * @see \OCA\Music\Db\BaseMapper::findUniqueEntity()
511
	 * @param Track $track
512
	 * @return Track
513
	 */
514
	protected function findUniqueEntity(Entity $track) : Entity {
515
		return $this->findByFileId($track->getFileId(), $track->getUserId());
0 ignored issues
show
Bug introduced by
The method getFileId() 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

515
		return $this->findByFileId($track->/** @scrutinizer ignore-call */ getFileId(), $track->getUserId());
Loading history...
Bug introduced by
It seems like $track->getFileId() can also be of type null; however, parameter $fileId of OCA\Music\Db\TrackMapper::findByFileId() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

515
		return $this->findByFileId(/** @scrutinizer ignore-type */ $track->getFileId(), $track->getUserId());
Loading history...
516
	}
517
}
518