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

514
		return $this->findByFileId(/** @scrutinizer ignore-type */ $track->getFileId(), $track->getUserId());
Loading history...
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

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